The crypto is so slow that there's no point unrolling this function.

A simpler and clearer implementation will do just fine.

Two other changes:
1. Move the debug print outside the spinlock;
2. Move all modification of rand_data_valid out of _get_more_prng_bytes;
   that variable belongs to the byte-at-a-time layer outside the
   block-oriented primitive.

This is the code that gave us CVE-2013-4345; it's full of corner cases
which the standard test vectors don't exercise.  The stutter test was
created to exercise it.  (And yes, it did catch problems during
development.)

Signed-off-by: George Spelvin <li...@horizon.com>
---
 crypto/ansi_cprng.c | 74 ++++++++++++++++++-----------------------------------
 1 file changed, 25 insertions(+), 49 deletions(-)

diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index c1c81266..a8cf98a5 100644
--- a/crypto/ansi_cprng.c
+++ b/crypto/ansi_cprng.c
@@ -145,7 +145,6 @@ static int _get_more_prng_bytes(struct prng_context *ctx, 
bool cont_test)
        hexdump("DT'", ctx->DT);
 
        dbgprint("Returning new block for context %p\n", ctx);
-       ctx->rand_data_valid = 0;
 
        return 0;
 }
@@ -156,68 +155,45 @@ static int get_prng_bytes(u8 *buf, unsigned int nbytes,
 {
        u8 *ptr = buf;
        unsigned int byte_count = nbytes;
-       int err;
+       unsigned int read_pos;
+       int err = -EINVAL;
 
+       dbgprint(KERN_CRIT "getting %u random bytes for context %p\n",
+               nbytes, ctx);
 
        spin_lock_bh(&ctx->prng_lock);
 
-       err = -EINVAL;
        if (ctx->flags & PRNG_NEED_RESET)
                goto done;
 
-       err = byte_count;
-
-       dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",
-               byte_count, ctx);
-
-
-remainder:
-       if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
-               if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
-                       memset(buf, 0, nbytes);
-                       err = -EINVAL;
-                       goto done;
-               }
-       }
-
-       /*
-        * Copy any data less than an entire block
-        */
-       if (byte_count < DEFAULT_BLK_SZ) {
-empty_rbuf:
-               while (ctx->rand_data_valid < DEFAULT_BLK_SZ) {
-                       *ptr = ctx->rand_data[ctx->rand_data_valid];
-                       ptr++;
-                       byte_count--;
-                       ctx->rand_data_valid++;
-                       if (byte_count == 0)
-                               goto done;
-               }
-       }
-
-       /*
-        * Now copy whole blocks
-        */
-       for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
-               if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+       read_pos = ctx->rand_data_valid;
+       if (byte_count > DEFAULT_BLK_SZ - read_pos) {
+               /* Leading partial block */
+               unsigned int avail = DEFAULT_BLK_SZ - read_pos;
+
+               memcpy(ptr, ctx->rand_data + read_pos, avail);
+               ptr += avail;
+               byte_count -= avail;
+               read_pos = 0;
+
+               /* Intermediate full blocks */
+               for (;;) {
                        if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
                                memset(buf, 0, nbytes);
-                               err = -EINVAL;
                                goto done;
                        }
+                       if (byte_count < DEFAULT_BLK_SZ)
+                               break;
+                       memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
+                       ptr += DEFAULT_BLK_SZ;
+                       byte_count -= DEFAULT_BLK_SZ;
                }
-               if (ctx->rand_data_valid > 0)
-                       goto empty_rbuf;
-               memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
-               ctx->rand_data_valid += DEFAULT_BLK_SZ;
-               ptr += DEFAULT_BLK_SZ;
        }
 
-       /*
-        * Now go back and get any remaining partial block
-        */
-       if (byte_count)
-               goto remainder;
+       /* The final partial block; read_pos + byte_count <= DEFAULT_BLK_SZ */
+       memcpy(ptr, ctx->rand_data + read_pos, byte_count);
+       ctx->rand_data_valid = read_pos + byte_count;
+       err = nbytes;
 
 done:
        spin_unlock_bh(&ctx->prng_lock);
-- 
2.1.3

--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to