فهرست منبع

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.
Peter Elliott 5 سال پیش
والد
کامیت
e1aef94a40
4فایلهای تغییر یافته به همراه71 افزوده شده و 12 حذف شده
  1. 49 7
      Kernel/Random.cpp
  2. 19 2
      Kernel/Random.h
  3. 1 1
      Kernel/VM/PageDirectory.cpp
  4. 2 2
      Kernel/init.cpp

+ 49 - 7
Kernel/Random.cpp

@@ -44,24 +44,48 @@ KernelRng& KernelRng::the()
 
 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) {
             u32 value = 0;
-            asm volatile(
-                "1:\n"
-                "rdseed %0\n"
-                "jnc 1b\n"
-                : "=r"(value));
+            if (g_cpu_supports_rdseed) {
+                asm volatile(
+                    "1:\n"
+                    "rdseed %0\n"
+                    "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);
         }
     }
 }
 
+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 };
 
 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?
     if (are_interrupts_enabled()) {
         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)
 {
-    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++];
+    }
 }
 
 }

+ 19 - 2
Kernel/Random.h

@@ -61,7 +61,7 @@ public:
             this->reseed();
         }
 
-        ASSERT(m_reseed_number > 0);
+        ASSERT(is_seeded());
 
         // FIXME: More than 2^20 bytes cannot be generated without refreshing the key.
         ASSERT(n < (1 << 20));
@@ -86,6 +86,16 @@ public:
         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:
     void reseed()
     {
@@ -118,8 +128,14 @@ class KernelRng : public Lockable<FortunaPRNG<Crypto::Cipher::AESCipher, Crypto:
 public:
     static KernelRng& the();
 
+    void wait_for_entropy();
+
+    void wake_if_ready();
+
 private:
     KernelRng();
+
+    WaitQueue m_seed_queue;
 };
 
 class EntropySource {
@@ -143,6 +159,7 @@ public:
         Event<T> event = { read_tsc(), m_source, event_data };
         KernelRng::the().resource().add_random_event(event, m_pool);
         m_pool++;
+        KernelRng::the().wake_if_ready();
     }
 
 private:
@@ -153,7 +170,7 @@ private:
 };
 
 // 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_good_random_bytes(u8*, size_t);

+ 1 - 1
Kernel/VM/PageDirectory.cpp

@@ -79,7 +79,7 @@ PageDirectory::PageDirectory(Process& process, const RangeAllocator* parent_rang
     if (parent_range_allocator) {
         m_range_allocator.initialize_from_parent(*parent_range_allocator);
     } 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;
         m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base);
     }

+ 2 - 2
Kernel/init.cpp

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