public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: "George Spelvin" <linux@horizon.com>
To: hpa@linux.intel.com, price@mit.edu, tytso@mit.edu
Cc: linux@horizon.com, linux-kernel@vger.kernel.org, mingo@kernel.org
Subject: [RFC PATCH] drivers/char/random.c: Is reducing locking range like this safe?
Date: 8 Jun 2014 20:05:24 -0400	[thread overview]
Message-ID: <20140609000524.19476.qmail@ns.horizon.com> (raw)

I have a few assorted cleanups in-work, but I was thinking about the
following and it's led to some thinking about the (non-)usefulness
of the out[] buffer from _mix_pool_bytes.

The problem I started trying to solve is readers (entropy extractors)
monopolizing the pool lock and stalling writers (entropy mixers), which
are supposed to be fast and low overhead.

So I thought I could rely on the "out" buffer returned from
_mix_pool_bytes to make concurrent extractors produce different output.
That led to the patch below, which drops the lock during the main pool
hash and only locks around the mix-back.  (Using the mix_pool_bytes
wrapper rather than explicit locking and __mix_pool_bytes.)
But I'm not sure it's quite right.

Suppose we have a pool that's mostly all-zero, but has a small dense
high-entropy section, and no arch_get_random_long().  Narrowing the
lock lets concurrent readers get to the __mix_pool_bytes() mix-back
(last hunk of the diff) with identical SHA states.

With the original code, which fills out[] with data from just *after*
the add_ptr (i.e. the oldest portion of the pool), it's possible for
multiple readers to obtain all-zero out[] buffers and return identical
hashes.

(Or, in a less extreme case, very low-entropy out[] buffers and
strongly correlated outputs.  I'll keep using "all-zero" to make
the examples simpler, and assume people can see the extrapolation.)

Well, okay, I think, easily fixed (included in the patch below):
change it to return the most recently modified section just *before*
the add_ptr, which includes the previus extractor's SHA hash.  That
way, I'm guaranteed to get different data on multiple concurrent
calls and everything is okay.

But is it?

Suppose I have three concurrent callers.  (I need three because the
hash folding covers up the problem with two.)  The pool contains
30 bytes of entropy, so it should be possible to return uncorrelated
outputs to each of them, but as mentioned that's in a small dense
region of the pool and the area around add_ptr is all zeros.

Each hashes the pool to obtain identical 20-byte SHA-1 hashes.  Then they
serialize arond calls to _mix_pool_bytes.  The first gets zeros in out[],
the second gets the first's SHA-1 hash and so generates a different hash,
and the third gets the second's SHA-1 hash.

So they all produce different outputs, but the outputs (totalling 30
bytes) passed through a 20-byte choke point and so have at most 20 bytes
of entropy between them.  This violates the /dev/random security goals.


Here are the solutions I've been thinking about:

1) Continue to lock the entire hashing operation.

   As long as we do this, the out[] parameter to _mix_pool_bytes is
   unnecessary and should be deleted.  Since the pool can't change
   between the bulk hash and the hash of out[], there's no point to
   re-hashing some data that was just hashed.  (You are sampling the
   add_ptr, which differs between callers, but that's negligible.)

   One way to address my concern would be to split r->lock into
   r->mix_lock and r->extract_lock.  Extractors would obtain the
   latter for the entire hash, and only obtain the mix_lock
   for the brief mix-back operation.

   The downside, of course, is a second lock on the extract path, but
   given that the efficiency of the extract path is not a design goal,
   perhaps that's fine.

2) Shrink the lock and add a nonce (like a CPUID) to the *initial* SHA state.

   This is fun because of its greater cryptographic cleverness,
   but that means it should be discussed carefully first.

   Due to the non-linearity of SHA hashing, this would result in
   the concurrent hashes sampling different parts of the pool's
   entropy, and the 20-byte entropy bottleneck would disappear.

   But in this case, the out[] buffer also does not contribute to
   the security guarantee and could also disappear.

Thoughts, anyone?

diff --git a/drivers/char/random.c b/drivers/char/random.c
index 102c50d..d847367 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -499,6 +499,15 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
 	input_rotate = ACCESS_ONCE(r->input_rotate);
 	i = ACCESS_ONCE(r->add_ptr);
 
+	/*
+	 * Out gets a copy of the portion of the pool most recently
+	 * modified by previous writers.  Since add_ptr counts down,
+	 * this is at positive offsets from the initial value.
+	 */
+	if (out)
+		for (j = 0; j < 16; j++)
+			((__u32 *)out)[j] = r->pool[(i + j) & wordmask];
+
 	/* mix one byte at a time to simplify size handling and churn faster */
 	while (nbytes--) {
 		w = rol32(*bytes++, input_rotate);
@@ -527,10 +536,6 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in,
 	ACCESS_ONCE(r->input_rotate) = input_rotate;
 	ACCESS_ONCE(r->add_ptr) = i;
 	smp_wmb();
-
-	if (out)
-		for (j = 0; j < 16; j++)
-			((__u32 *)out)[j] = r->pool[(i - j) & wordmask];
 }
 
 static void __mix_pool_bytes(struct entropy_store *r, const void *in,
@@ -1043,7 +1048,6 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
 	}
 
 	/* Generate a hash across the pool, 16 words (512 bits) at a time */
-	spin_lock_irqsave(&r->lock, flags);
 	for (i = 0; i < r->poolinfo->poolwords; i += 16)
 		sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
 
@@ -1056,8 +1060,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
 	 * brute-forcing the feedback as hard as brute-forcing the
 	 * hash.
 	 */
-	__mix_pool_bytes(r, hash.w, sizeof(hash.w), extract);
-	spin_unlock_irqrestore(&r->lock, flags);
+	mix_pool_bytes(r, hash.w, sizeof(hash.w), extract);
 
 	/*
 	 * To avoid duplicates, we atomically extract a portion of the

             reply	other threads:[~2014-06-09  0:05 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-09  0:05 George Spelvin [this message]
2014-06-09  1:35 ` [RFC PATCH] drivers/char/random.c: Is reducing locking range like this safe? Theodore Ts'o
2014-06-09  2:10   ` George Spelvin
2014-06-09  2:18     ` George Spelvin
2014-06-09  4:03       ` George Spelvin
2014-06-09  9:23         ` George Spelvin
2014-06-09 13:34         ` Theodore Ts'o
2014-06-09 15:04           ` George Spelvin
2014-06-09 15:50             ` Theodore Ts'o
2014-06-09 16:11               ` George Spelvin
2014-06-10  0:20               ` drivers/char/random.c: more ruminations George Spelvin
2014-06-10  1:20                 ` Theodore Ts'o
2014-06-10  3:10                   ` George Spelvin
2014-06-10 15:25                     ` Theodore Ts'o
2014-06-10 20:40                       ` George Spelvin
2014-06-10 21:20                         ` Theodore Ts'o
2014-06-11  0:10                           ` George Spelvin
2014-06-11  2:08                             ` Theodore Ts'o
2014-06-11  3:58                               ` George Spelvin
2014-06-11 13:11                                 ` Theodore Ts'o
2014-06-12  0:42                                   ` George Spelvin
2014-06-12  1:03                                   ` H. Peter Anvin
2014-06-11  4:34                               ` George Spelvin
2014-06-11 13:09                                 ` Theodore Ts'o
2014-06-11  2:21                             ` Theodore Ts'o
2014-06-09 13:17   ` drivers/char/random.c: More futzing about George Spelvin
2014-06-11 16:38     ` Theodore Ts'o
2014-06-11 16:48       ` H. Peter Anvin
2014-06-11 19:25         ` Theodore Ts'o
2014-06-11 20:41           ` H. Peter Anvin
2014-06-12  0:44             ` H. Peter Anvin
2014-06-12  1:51               ` George Spelvin
2014-06-12  0:32       ` George Spelvin
2014-06-12  3:22         ` Theodore Ts'o
2014-06-12  4:13           ` random: Benchamrking fast_mix2 George Spelvin
2014-06-12 11:18             ` George Spelvin
2014-06-12 20:17               ` Theodore Ts'o
2014-06-12 20:46               ` Theodore Ts'o
2014-06-13  0:23                 ` George Spelvin
2014-06-13 15:52                   ` Theodore Ts'o
2014-06-14  2:10                     ` George Spelvin
2014-06-14  3:06                       ` Theodore Ts'o
2014-06-14  5:25                         ` George Spelvin
2014-06-14  6:24                           ` Theodore Ts'o
2014-06-14  8:03                             ` George Spelvin
2014-06-14 11:14                               ` George Spelvin
2014-06-14 15:13                                 ` George Spelvin
2014-06-14 16:33                                   ` Theodore Ts'o
2014-06-15  0:23                                     ` George Spelvin
2014-06-15  1:17                                       ` Theodore Ts'o
2014-06-15  6:58                                         ` George Spelvin
2014-06-15 13:01                                           ` Theodore Ts'o
2014-06-14  6:27                           ` Theodore Ts'o
2014-06-14  4:55                     ` [RFC] random: is the IRQF_TIMER test working as intended? George Spelvin
2014-06-14  6:43                       ` Theodore Ts'o
2014-06-14  7:23                         ` George Spelvin
2014-06-12  3:43       ` drivers/char/random.c: More futzing about George Spelvin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140609000524.19476.qmail@ns.horizon.com \
    --to=linux@horizon.com \
    --cc=hpa@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=price@mit.edu \
    --cc=tytso@mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox