1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-22 19:35:06 +00:00

Kernel: Make Random work on CPUs without rdrand

- If rdseed is not available, fallback to rdrand.
- If rdrand is not available, block for entropy, or use insecure prng
  depending on if user wants fast or good random.
This commit is contained in:
Peter Elliott 2020-06-27 11:10:01 -06:00 committed by Andreas Kling
parent 1eb338ab71
commit e1aef94a40
4 changed files with 71 additions and 12 deletions

View file

@ -44,24 +44,48 @@ KernelRng& KernelRng::the()
KernelRng::KernelRng() KernelRng::KernelRng()
{ {
if (g_cpu_supports_rdseed) { if (g_cpu_supports_rdseed || g_cpu_supports_rdrand) {
for (size_t i = 0; i < resource().pool_count * resource().reseed_threshold; ++i) { for (size_t i = 0; i < resource().pool_count * resource().reseed_threshold; ++i) {
u32 value = 0; u32 value = 0;
asm volatile( if (g_cpu_supports_rdseed) {
"1:\n" asm volatile(
"rdseed %0\n" "1:\n"
"jnc 1b\n" "rdseed %0\n"
: "=r"(value)); "jnc 1b\n"
: "=r"(value));
} else {
asm volatile(
"1:\n"
"rdrand %0\n"
"jnc 1b\n"
: "=r"(value));
}
this->resource().add_random_event(value, i % 32); this->resource().add_random_event(value, i % 32);
} }
} }
} }
void KernelRng::wait_for_entropy()
{
if (!resource().is_ready()) {
Thread::current->wait_on(m_seed_queue);
}
}
void KernelRng::wake_if_ready()
{
if (resource().is_ready()) {
m_seed_queue.wake_all();
}
}
size_t EntropySource::next_source { 0 }; size_t EntropySource::next_source { 0 };
void get_good_random_bytes(u8* buffer, size_t buffer_size) void get_good_random_bytes(u8* buffer, size_t buffer_size)
{ {
KernelRng::the().wait_for_entropy();
// FIXME: What if interrupts are disabled because we're in an interrupt? // FIXME: What if interrupts are disabled because we're in an interrupt?
if (are_interrupts_enabled()) { if (are_interrupts_enabled()) {
LOCKER(KernelRng::the().lock()); LOCKER(KernelRng::the().lock());
@ -73,7 +97,25 @@ void get_good_random_bytes(u8* buffer, size_t buffer_size)
void get_fast_random_bytes(u8* buffer, size_t buffer_size) void get_fast_random_bytes(u8* buffer, size_t buffer_size)
{ {
return get_good_random_bytes(buffer, buffer_size); if (KernelRng::the().resource().is_ready()) {
return get_good_random_bytes(buffer, buffer_size);
}
static u32 next = 1;
union {
u8 bytes[4];
u32 value;
} u;
size_t offset = 4;
for (size_t i = 0; i < buffer_size; ++i) {
if (offset >= 4) {
next = next * 1103515245 + 12345;
u.value = next;
offset = 0;
}
buffer[i] = u.bytes[offset++];
}
} }
} }

View file

@ -61,7 +61,7 @@ public:
this->reseed(); this->reseed();
} }
ASSERT(m_reseed_number > 0); ASSERT(is_seeded());
// FIXME: More than 2^20 bytes cannot be generated without refreshing the key. // FIXME: More than 2^20 bytes cannot be generated without refreshing the key.
ASSERT(n < (1 << 20)); ASSERT(n < (1 << 20));
@ -86,6 +86,16 @@ public:
m_pools[pool].update(reinterpret_cast<const u8*>(&event_data), sizeof(T)); m_pools[pool].update(reinterpret_cast<const u8*>(&event_data), sizeof(T));
} }
bool is_seeded() const
{
return m_reseed_number > 0;
}
bool is_ready() const
{
return is_seeded() || m_p0_len >= reseed_threshold;
}
private: private:
void reseed() void reseed()
{ {
@ -118,8 +128,14 @@ class KernelRng : public Lockable<FortunaPRNG<Crypto::Cipher::AESCipher, Crypto:
public: public:
static KernelRng& the(); static KernelRng& the();
void wait_for_entropy();
void wake_if_ready();
private: private:
KernelRng(); KernelRng();
WaitQueue m_seed_queue;
}; };
class EntropySource { class EntropySource {
@ -143,6 +159,7 @@ public:
Event<T> event = { read_tsc(), m_source, event_data }; Event<T> event = { read_tsc(), m_source, event_data };
KernelRng::the().resource().add_random_event(event, m_pool); KernelRng::the().resource().add_random_event(event, m_pool);
m_pool++; m_pool++;
KernelRng::the().wake_if_ready();
} }
private: private:
@ -153,7 +170,7 @@ private:
}; };
// NOTE: These API's are primarily about expressing intent/needs in the calling code. // NOTE: These API's are primarily about expressing intent/needs in the calling code.
// We don't make any guarantees about actual fastness or goodness yet. // The only difference is that get_fast_random is guaranteed not to block.
void get_fast_random_bytes(u8*, size_t); void get_fast_random_bytes(u8*, size_t);
void get_good_random_bytes(u8*, size_t); void get_good_random_bytes(u8*, size_t);

View file

@ -79,7 +79,7 @@ PageDirectory::PageDirectory(Process& process, const RangeAllocator* parent_rang
if (parent_range_allocator) { if (parent_range_allocator) {
m_range_allocator.initialize_from_parent(*parent_range_allocator); m_range_allocator.initialize_from_parent(*parent_range_allocator);
} else { } else {
size_t random_offset = (get_good_random<u32>() % 32 * MB) & PAGE_MASK; size_t random_offset = (get_fast_random<u32>() % 32 * MB) & PAGE_MASK;
u32 base = userspace_range_base + random_offset; u32 base = userspace_range_base + random_offset;
m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base); m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base);
} }

View file

@ -131,7 +131,7 @@ extern "C" [[noreturn]] void init()
klog() << "Starting SerenityOS..."; klog() << "Starting SerenityOS...";
__stack_chk_guard = get_good_random<u32>(); __stack_chk_guard = get_fast_random<u32>();
TimeManagement::initialize(); TimeManagement::initialize();
@ -169,7 +169,7 @@ extern "C" [[noreturn]] void init()
extern "C" [[noreturn]] void init_ap(u32 cpu) extern "C" [[noreturn]] void init_ap(u32 cpu)
{ {
APIC::the().enable(cpu); APIC::the().enable(cpu);
#if 0 #if 0
Scheduler::idle_loop(); Scheduler::idle_loop();
#else #else