diff options
author | Jiri Kosina <jkosina@suse.cz> | 2013-05-24 18:55:33 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-05-24 19:22:52 -0400 |
commit | 10b3a32d292c21ea5b3ad5ca5975e88bb20b8d68 (patch) | |
tree | 773d7546880a333ccdabf72a2480a9e9aabe112e /drivers/char/random.c | |
parent | 1e7e2e05c179a68aaf8830fe91547a87f4589e53 (diff) |
random: fix accounting race condition with lockless irq entropy_count update
Commit 902c098a3663 ("random: use lockless techniques in the interrupt
path") turned IRQ path from being spinlock protected into lockless
cmpxchg-retry update.
That commit removed r->lock serialization between crediting entropy bits
from IRQ context and accounting when extracting entropy on userspace
read path, but didn't turn the r->entropy_count reads/updates in
account() to use cmpxchg as well.
It has been observed, that under certain circumstances this leads to
read() on /dev/urandom to return 0 (EOF), as r->entropy_count gets
corrupted and becomes negative, which in turn results in propagating 0
all the way from account() to the actual read() call.
Convert the accounting code to be the proper lockless counterpart of
what has been partially done by 902c098a3663.
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Greg KH <greg@kroah.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char/random.c')
-rw-r--r-- | drivers/char/random.c | 24 |
1 files changed, 16 insertions, 8 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c index 73e52b7796f9..35487e8ded59 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c | |||
@@ -865,16 +865,24 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, | |||
865 | if (r->entropy_count / 8 < min + reserved) { | 865 | if (r->entropy_count / 8 < min + reserved) { |
866 | nbytes = 0; | 866 | nbytes = 0; |
867 | } else { | 867 | } else { |
868 | int entropy_count, orig; | ||
869 | retry: | ||
870 | entropy_count = orig = ACCESS_ONCE(r->entropy_count); | ||
868 | /* If limited, never pull more than available */ | 871 | /* If limited, never pull more than available */ |
869 | if (r->limit && nbytes + reserved >= r->entropy_count / 8) | 872 | if (r->limit && nbytes + reserved >= entropy_count / 8) |
870 | nbytes = r->entropy_count/8 - reserved; | 873 | nbytes = entropy_count/8 - reserved; |
871 | 874 | ||
872 | if (r->entropy_count / 8 >= nbytes + reserved) | 875 | if (entropy_count / 8 >= nbytes + reserved) { |
873 | r->entropy_count -= nbytes*8; | 876 | entropy_count -= nbytes*8; |
874 | else | 877 | if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) |
875 | r->entropy_count = reserved; | 878 | goto retry; |
879 | } else { | ||
880 | entropy_count = reserved; | ||
881 | if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig) | ||
882 | goto retry; | ||
883 | } | ||
876 | 884 | ||
877 | if (r->entropy_count < random_write_wakeup_thresh) | 885 | if (entropy_count < random_write_wakeup_thresh) |
878 | wakeup_write = 1; | 886 | wakeup_write = 1; |
879 | } | 887 | } |
880 | 888 | ||