aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorJarod Wilson <jarod@redhat.com>2013-05-24 18:55:31 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-24 19:22:52 -0400
commit1e7e2e05c179a68aaf8830fe91547a87f4589e53 (patch)
tree181e088925c447edbe88e05095effbcdfb71da4c /drivers/char
parent348f9f05e0266822fa048f7fb3b039692a0cafbc (diff)
drivers/char/random.c: fix priming of last_data
Commit ec8f02da9ea5 ("random: prime last_data value per fips requirements") added priming of last_data per fips requirements. Unfortuantely, it did so in a way that can lead to multiple threads all incrementing nbytes, but only one actually doing anything with the extra data, which leads to some fun random corruption and panics. The fix is to simply do everything needed to prime last_data in a single shot, so there's no window for multiple cpus to increment nbytes -- in fact, we won't even increment or decrement nbytes anymore, we'll just extract the needed EXTRACT_SIZE one time per pool and then carry on with the normal routine. All these changes have been tested across multiple hosts and architectures where panics were previously encoutered. The code changes are are strictly limited to areas only touched when when booted in fips mode. This change should also go into 3.8-stable, to make the myriads of fips users on 3.8.x happy. Signed-off-by: Jarod Wilson <jarod@redhat.com> Tested-by: Jan Stancek <jstancek@redhat.com> Tested-by: Jan Stodola <jstodola@redhat.com> Cc: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: Neil Horman <nhorman@tuxdriver.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Matt Mackall <mpm@selenic.com> Cc: "Theodore Ts'o" <tytso@mit.edu> 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')
-rw-r--r--drivers/char/random.c30
1 files changed, 15 insertions, 15 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c
index cd9a6211dcad..73e52b7796f9 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -957,10 +957,23 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
957{ 957{
958 ssize_t ret = 0, i; 958 ssize_t ret = 0, i;
959 __u8 tmp[EXTRACT_SIZE]; 959 __u8 tmp[EXTRACT_SIZE];
960 unsigned long flags;
960 961
961 /* if last_data isn't primed, we need EXTRACT_SIZE extra bytes */ 962 /* if last_data isn't primed, we need EXTRACT_SIZE extra bytes */
962 if (fips_enabled && !r->last_data_init) 963 if (fips_enabled) {
963 nbytes += EXTRACT_SIZE; 964 spin_lock_irqsave(&r->lock, flags);
965 if (!r->last_data_init) {
966 r->last_data_init = true;
967 spin_unlock_irqrestore(&r->lock, flags);
968 trace_extract_entropy(r->name, EXTRACT_SIZE,
969 r->entropy_count, _RET_IP_);
970 xfer_secondary_pool(r, EXTRACT_SIZE);
971 extract_buf(r, tmp);
972 spin_lock_irqsave(&r->lock, flags);
973 memcpy(r->last_data, tmp, EXTRACT_SIZE);
974 }
975 spin_unlock_irqrestore(&r->lock, flags);
976 }
964 977
965 trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_); 978 trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
966 xfer_secondary_pool(r, nbytes); 979 xfer_secondary_pool(r, nbytes);
@@ -970,19 +983,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
970 extract_buf(r, tmp); 983 extract_buf(r, tmp);
971 984
972 if (fips_enabled) { 985 if (fips_enabled) {
973 unsigned long flags;
974
975
976 /* prime last_data value if need be, per fips 140-2 */
977 if (!r->last_data_init) {
978 spin_lock_irqsave(&r->lock, flags);
979 memcpy(r->last_data, tmp, EXTRACT_SIZE);
980 r->last_data_init = true;
981 nbytes -= EXTRACT_SIZE;
982 spin_unlock_irqrestore(&r->lock, flags);
983 extract_buf(r, tmp);
984 }
985
986 spin_lock_irqsave(&r->lock, flags); 986 spin_lock_irqsave(&r->lock, flags);
987 if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) 987 if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
988 panic("Hardware RNG duplicated output!\n"); 988 panic("Hardware RNG duplicated output!\n");