Commit 77dcc24b authored by Jiri Kosina's avatar Jiri Kosina Committed by Greg Kroah-Hartman

random: fix accounting race condition with lockless irq entropy_count update

commit 10b3a32d upstream.

Commit 902c098a ("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 902c098a.
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
Cc: Qiang Huang <h.huangqiang@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 06b08fa4
...@@ -867,16 +867,24 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min, ...@@ -867,16 +867,24 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
if (r->entropy_count / 8 < min + reserved) { if (r->entropy_count / 8 < min + reserved) {
nbytes = 0; nbytes = 0;
} else { } else {
int entropy_count, orig;
retry:
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
/* If limited, never pull more than available */ /* If limited, never pull more than available */
if (r->limit && nbytes + reserved >= r->entropy_count / 8) if (r->limit && nbytes + reserved >= entropy_count / 8)
nbytes = r->entropy_count/8 - reserved; nbytes = entropy_count/8 - reserved;
if (r->entropy_count / 8 >= nbytes + reserved) if (entropy_count / 8 >= nbytes + reserved) {
r->entropy_count -= nbytes*8; entropy_count -= nbytes*8;
else if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
r->entropy_count = reserved; 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) {
wake_up_interruptible(&random_write_wait); wake_up_interruptible(&random_write_wait);
kill_fasync(&fasync, SIGIO, POLL_OUT); kill_fasync(&fasync, SIGIO, POLL_OUT);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment