From: Aristeu Rozanski <arozansk@redhat.com> Subject: [kernel team][RHEL5 PATCH 1/2] random: fix error in entropy extraction Date: Wed, 30 May 2007 09:31:11 -0400 Bugzilla: 241718 Message-Id: <20070530133111.GG5318@redhat.com> Changelog: [misc] random: fix error in entropy extraction This patch is the backport of the commit 602b6aeefe8932dd8bb15014e8fe6bb25d736361, which fixes an error while extracting entropy, only hashing 25% of the buffer because of a wrong cast. The discussion began on lkml[1] and then moved to security@kernel.org. The related BZ# is 241718. I'm still working in a way to test this, since it requires special conditions (the reporter hit this bug in an embedded device, clock reset every boot, no disk, etc). [1] http://marc.info/?t=118043966600011&r=1&w=2 commit 602b6aeefe8932dd8bb15014e8fe6bb25d736361 Author: Matt Mackall <mpm@selenic.com> random: fix error in entropy extraction Fix cast error in entropy extraction. Add comments explaining the magic 16. Remove extra confusing loop variable. Signed-off-by: Matt Mackall <mpm@selenic.com> Acked-by: "Theodore Ts'o" <tytso@mit.edu> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Index: linux-2.6.18.noarch/drivers/char/random.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/char/random.c +++ linux-2.6.18.noarch/drivers/char/random.c @@ -757,7 +757,7 @@ static size_t account(struct entropy_sto static void extract_buf(struct entropy_store *r, __u8 *out) { - int i, x; + int i; __u32 data[16], buf[5 + SHA_WORKSPACE_WORDS]; sha_init(buf); @@ -769,9 +769,11 @@ static void extract_buf(struct entropy_s * attempts to find previous ouputs), unless the hash * function can be inverted. */ - for (i = 0, x = 0; i < r->poolinfo->poolwords; i += 16, x+=2) { - sha_transform(buf, (__u8 *)r->pool+i, buf + 5); - add_entropy_words(r, &buf[x % 5], 1); + for (i = 0; i < r->poolinfo->poolwords; i += 16) { + /* hash blocks of 16 words = 512 bits */ + sha_transform(buf, (__u8 *)(r->pool + i), buf + 5); + /* feed back portion of the resulting hash */ + add_entropy_words(r, &buf[i % 5], 1); } /* @@ -779,7 +781,7 @@ static void extract_buf(struct entropy_s * portion of the pool while mixing, and hash one * final time. */ - __add_entropy_words(r, &buf[x % 5], 1, data); + __add_entropy_words(r, &buf[i % 5], 1, data); sha_transform(buf, (__u8 *)data, buf + 5); /* This is the second patch of the series, backport of the commit 7f397dcdb78d699a20d96bfcfb595a2411a5bbd2. Related BZ#: 241718 commit 7f397dcdb78d699a20d96bfcfb595a2411a5bbd2 Author: Matt Mackall <mpm@selenic.com> random: fix seeding with zero entropy Add data from zero-entropy random_writes directly to output pools to avoid accounting difficulties on machines without entropy sources. Tested on lguest with all entropy sources disabled. Signed-off-by: Matt Mackall <mpm@selenic.com> Acked-by: "Theodore Ts'o" <tytso@mit.edu> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Index: linux-2.6.18.noarch/drivers/char/random.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/char/random.c +++ linux-2.6.18.noarch/drivers/char/random.c @@ -1019,37 +1019,44 @@ random_poll(struct file *file, poll_tabl return mask; } -static ssize_t -random_write(struct file * file, const char __user * buffer, - size_t count, loff_t *ppos) +static int +write_pool(struct entropy_store *r, const char __user *buffer, size_t count) { - int ret = 0; size_t bytes; __u32 buf[16]; const char __user *p = buffer; - size_t c = count; - while (c > 0) { - bytes = min(c, sizeof(buf)); + while (count > 0) { + bytes = min(count, sizeof(buf)); + if (copy_from_user(&buf, p, bytes)) + return -EFAULT; - bytes -= copy_from_user(&buf, p, bytes); - if (!bytes) { - ret = -EFAULT; - break; - } - c -= bytes; + count -= bytes; p += bytes; - add_entropy_words(&input_pool, buf, (bytes + 3) / 4); - } - if (p == buffer) { - return (ssize_t)ret; - } else { - struct inode *inode = file->f_dentry->d_inode; - inode->i_mtime = current_fs_time(inode->i_sb); - mark_inode_dirty(inode); - return (ssize_t)(p - buffer); + add_entropy_words(r, buf, (bytes + 3) / 4); } + + return 0; +} + +static ssize_t +random_write(struct file * file, const char __user * buffer, + size_t count, loff_t *ppos) +{ + size_t ret; + struct inode *inode = file->f_dentry->d_inode; + + ret = write_pool(&blocking_pool, buffer, count); + if (ret) + return ret; + ret = write_pool(&nonblocking_pool, buffer, count); + if (ret) + return ret; + + inode->i_mtime = current_fs_time(inode->i_sb); + mark_inode_dirty(inode); + return (ssize_t)count; } static int @@ -1088,8 +1095,8 @@ random_ioctl(struct inode * inode, struc return -EINVAL; if (get_user(size, p++)) return -EFAULT; - retval = random_write(file, (const char __user *) p, - size, &file->f_pos); + retval = write_pool(&input_pool, (const char __user *)p, + size); if (retval < 0) return retval; credit_entropy_store(&input_pool, ent_count);