Merge branch 'kvm-amd-pmu-fixes' into HEAD
[linux-2.6-microblaze.git] / drivers / char / random.c
index ee3ad2b..3a293f9 100644 (file)
@@ -333,7 +333,7 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],
        chacha20_block(chacha_state, first_block);
 
        memcpy(key, first_block, CHACHA_KEY_SIZE);
-       memcpy(random_data, first_block + CHACHA_KEY_SIZE, random_data_len);
+       memmove(random_data, first_block + CHACHA_KEY_SIZE, random_data_len);
        memzero_explicit(first_block, sizeof(first_block));
 }
 
@@ -523,49 +523,59 @@ EXPORT_SYMBOL(get_random_bytes);
 
 static ssize_t get_random_bytes_user(void __user *buf, size_t nbytes)
 {
-       bool large_request = nbytes > 256;
-       ssize_t ret = 0;
-       size_t len;
+       size_t len, left, ret = 0;
        u32 chacha_state[CHACHA_STATE_WORDS];
        u8 output[CHACHA_BLOCK_SIZE];
 
        if (!nbytes)
                return 0;
 
-       len = min_t(size_t, 32, nbytes);
-       crng_make_state(chacha_state, output, len);
-
-       if (copy_to_user(buf, output, len))
-               return -EFAULT;
-       nbytes -= len;
-       buf += len;
-       ret += len;
-
-       while (nbytes) {
-               if (large_request && need_resched()) {
-                       if (signal_pending(current))
-                               break;
-                       schedule();
-               }
+       /*
+        * Immediately overwrite the ChaCha key at index 4 with random
+        * bytes, in case userspace causes copy_to_user() below to sleep
+        * forever, so that we still retain forward secrecy in that case.
+        */
+       crng_make_state(chacha_state, (u8 *)&chacha_state[4], CHACHA_KEY_SIZE);
+       /*
+        * However, if we're doing a read of len <= 32, we don't need to
+        * use chacha_state after, so we can simply return those bytes to
+        * the user directly.
+        */
+       if (nbytes <= CHACHA_KEY_SIZE) {
+               ret = nbytes - copy_to_user(buf, &chacha_state[4], nbytes);
+               goto out_zero_chacha;
+       }
 
+       for (;;) {
                chacha20_block(chacha_state, output);
                if (unlikely(chacha_state[12] == 0))
                        ++chacha_state[13];
 
                len = min_t(size_t, nbytes, CHACHA_BLOCK_SIZE);
-               if (copy_to_user(buf, output, len)) {
-                       ret = -EFAULT;
+               left = copy_to_user(buf, output, len);
+               if (left) {
+                       ret += len - left;
                        break;
                }
 
-               nbytes -= len;
                buf += len;
                ret += len;
+               nbytes -= len;
+               if (!nbytes)
+                       break;
+
+               BUILD_BUG_ON(PAGE_SIZE % CHACHA_BLOCK_SIZE != 0);
+               if (ret % PAGE_SIZE == 0) {
+                       if (signal_pending(current))
+                               break;
+                       cond_resched();
+               }
        }
 
-       memzero_explicit(chacha_state, sizeof(chacha_state));
        memzero_explicit(output, sizeof(output));
-       return ret;
+out_zero_chacha:
+       memzero_explicit(chacha_state, sizeof(chacha_state));
+       return ret ? ret : -EFAULT;
 }
 
 /*
@@ -1008,7 +1018,7 @@ int __init rand_initialize(void)
  */
 void add_device_randomness(const void *buf, size_t size)
 {
-       cycles_t cycles = random_get_entropy();
+       unsigned long cycles = random_get_entropy();
        unsigned long flags, now = jiffies;
 
        if (crng_init == 0 && size)
@@ -1039,8 +1049,7 @@ struct timer_rand_state {
  */
 static void add_timer_randomness(struct timer_rand_state *state, unsigned int num)
 {
-       cycles_t cycles = random_get_entropy();
-       unsigned long flags, now = jiffies;
+       unsigned long cycles = random_get_entropy(), now = jiffies, flags;
        long delta, delta2, delta3;
 
        spin_lock_irqsave(&input_pool.lock, flags);
@@ -1329,8 +1338,7 @@ static void mix_interrupt_randomness(struct work_struct *work)
 void add_interrupt_randomness(int irq)
 {
        enum { MIX_INFLIGHT = 1U << 31 };
-       cycles_t cycles = random_get_entropy();
-       unsigned long now = jiffies;
+       unsigned long cycles = random_get_entropy(), now = jiffies;
        struct fast_pool *fast_pool = this_cpu_ptr(&irq_randomness);
        struct pt_regs *regs = get_irq_regs();
        unsigned int new_count;
@@ -1343,16 +1351,12 @@ void add_interrupt_randomness(int irq)
        if (cycles == 0)
                cycles = get_reg(fast_pool, regs);
 
-       if (sizeof(cycles) == 8)
+       if (sizeof(unsigned long) == 8) {
                irq_data.u64[0] = cycles ^ rol64(now, 32) ^ irq;
-       else {
+               irq_data.u64[1] = regs ? instruction_pointer(regs) : _RET_IP_;
+       } else {
                irq_data.u32[0] = cycles ^ irq;
                irq_data.u32[1] = now;
-       }
-
-       if (sizeof(unsigned long) == 8)
-               irq_data.u64[1] = regs ? instruction_pointer(regs) : _RET_IP_;
-       else {
                irq_data.u32[2] = regs ? instruction_pointer(regs) : _RET_IP_;
                irq_data.u32[3] = get_reg(fast_pool, regs);
        }
@@ -1399,7 +1403,7 @@ static void entropy_timer(struct timer_list *t)
 static void try_to_generate_entropy(void)
 {
        struct {
-               cycles_t cycles;
+               unsigned long cycles;
                struct timer_list timer;
        } stack;
 
@@ -1534,6 +1538,13 @@ static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes,
 {
        static int maxwarn = 10;
 
+       /*
+        * Opportunistically attempt to initialize the RNG on platforms that
+        * have fast cycle counters, but don't (for now) require it to succeed.
+        */
+       if (!crng_ready())
+               try_to_generate_entropy();
+
        if (!crng_ready() && maxwarn > 0) {
                maxwarn--;
                if (__ratelimit(&urandom_warning))