Skip to content

Commit

Permalink
random: fix accounting race condition with lockless irq entropy_count…
Browse files Browse the repository at this point in the history
… update

Commit 902c098 ("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 902c098.

Signed-off-by: Jiri Kosina <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: Greg KH <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Jiri Kosina authored and torvalds committed May 24, 2013
1 parent 1e7e2e0 commit 10b3a32
Showing 1 changed file with 16 additions and 8 deletions.
24 changes: 16 additions & 8 deletions drivers/char/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,16 +865,24 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
if (r->entropy_count / 8 < min + reserved) {
nbytes = 0;
} else {
int entropy_count, orig;
retry:
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
/* If limited, never pull more than available */
if (r->limit && nbytes + reserved >= r->entropy_count / 8)
nbytes = r->entropy_count/8 - reserved;

if (r->entropy_count / 8 >= nbytes + reserved)
r->entropy_count -= nbytes*8;
else
r->entropy_count = reserved;
if (r->limit && nbytes + reserved >= entropy_count / 8)
nbytes = entropy_count/8 - reserved;

if (entropy_count / 8 >= nbytes + reserved) {
entropy_count -= nbytes*8;
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
goto retry;
} else {
entropy_count = reserved;
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
goto retry;
}

if (r->entropy_count < random_write_wakeup_thresh)
if (entropy_count < random_write_wakeup_thresh)
wakeup_write = 1;
}

Expand Down

0 comments on commit 10b3a32

Please sign in to comment.