diff mbox

crypto: AF_ALG - wait for data at beginning of recvmsg

Message ID 1970731.rxTGvFCgCz@positron.chronox.de (mailing list archive)
State Superseded
Delegated to: Herbert Xu
Headers show

Commit Message

Stephan Mueller Nov. 29, 2017, 10:17 a.m. UTC
The wait for data is a non-atomic operation that can sleep and therefore
potentially release the socket lock. The release of the socket lock
allows another thread to modify the context data structure. The waiting
operation for new data therefore must be called at the beginning of
recvmsg. This prevents a race condition where checks of the members of
the context data structure are performed by recvmsg while there is a
potential for modification of these values.

For skcipher, ctx->used is used as an indicator whether to wait for new
data, because skcipher can operate on a subset of the overall data
to be processed.

In contrast, aead must check ctx->more which is a flag set by user space
indicating that all data has been sent. It is required for aead to wait
until all data intended to be send by the caller are received as
the authentication operation part of the aead cipher requires the
presence of the whole data.

Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management")
Fixes: d887c52d6ae4 ("crypto: algif_aead - overhaul memory management")
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: <stable@vger.kernel.org> # v4.14+
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
 crypto/af_alg.c         | 6 ------
 crypto/algif_aead.c     | 6 ++++++
 crypto/algif_skcipher.c | 6 ++++++
 3 files changed, 12 insertions(+), 6 deletions(-)

Comments

Herbert Xu Nov. 29, 2017, 10:22 a.m. UTC | #1
On Wed, Nov 29, 2017 at 11:17:26AM +0100, Stephan Müller wrote:
>
> @@ -111,6 +111,12 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
>  	size_t usedpages = 0;		/* [in]  RX bufs to be used from user */
>  	size_t processed = 0;		/* [in]  TX bufs to be consumed */
>  
> +	if (ctx->more) {
> +		err = af_alg_wait_for_data(sk, flags);
> +		if (err)
> +			return err;
> +	}

So what happens when sendmsg hasn't been called at all? In that
case ctx->more would be zero, and we would skip the wait right?

Cheers,
Stephan Mueller Nov. 29, 2017, 10:28 a.m. UTC | #2
Am Mittwoch, 29. November 2017, 11:22:34 CET schrieb Herbert Xu:

Hi Herbert,

> On Wed, Nov 29, 2017 at 11:17:26AM +0100, Stephan Müller wrote:
> > @@ -111,6 +111,12 @@ static int _aead_recvmsg(struct socket *sock, struct
> > msghdr *msg,> 
> >  	size_t usedpages = 0;		/* [in]  RX bufs to be used from user */
> >  	size_t processed = 0;		/* [in]  TX bufs to be consumed */
> > 
> > +	if (ctx->more) {
> > +		err = af_alg_wait_for_data(sk, flags);
> > +		if (err)
> > +			return err;
> > +	}
> 
> So what happens when sendmsg hasn't been called at all? In that
> case ctx->more would be zero, and we would skip the wait right?

Right, but wouldn't that be the correct order of operation? If somebody does 
not call sendmsg, he simply did not send any data. And that is yet a proper 
operation (for encryption) as it obtains the tag for the "null" data.

In case we have decryption and yet we received "null" data (e.g. sendmsg was 
not called) which implies the kernel received too little data (decryption at 
least requires the tag), aead_sufficient_data will return the error.

Ciao
Stephan
Herbert Xu Nov. 29, 2017, 10:42 a.m. UTC | #3
On Wed, Nov 29, 2017 at 11:28:43AM +0100, Stephan Mueller wrote:
> Am Mittwoch, 29. November 2017, 11:22:34 CET schrieb Herbert Xu:
> 
> Hi Herbert,
> 
> > On Wed, Nov 29, 2017 at 11:17:26AM +0100, Stephan Müller wrote:
> > > @@ -111,6 +111,12 @@ static int _aead_recvmsg(struct socket *sock, struct
> > > msghdr *msg,> 
> > >  	size_t usedpages = 0;		/* [in]  RX bufs to be used from user */
> > >  	size_t processed = 0;		/* [in]  TX bufs to be consumed */
> > > 
> > > +	if (ctx->more) {
> > > +		err = af_alg_wait_for_data(sk, flags);
> > > +		if (err)
> > > +			return err;
> > > +	}
> > 
> > So what happens when sendmsg hasn't been called at all? In that
> > case ctx->more would be zero, and we would skip the wait right?
> 
> Right, but wouldn't that be the correct order of operation? If somebody does 
> not call sendmsg, he simply did not send any data. And that is yet a proper 
> operation (for encryption) as it obtains the tag for the "null" data.

Well no.  Up until now we have supported (although to a limited
extent) having two threads do recvmsg/sendmsg in parallel.  So
if the first thread executed recvmsg it should wait until the
second thread executes sendmsg.

Obviously this support is not complete as we have seen with ctx->enc
overwrites but we shouldn't break what used to work.

Cheers,
diff mbox

Patch

diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index e720dfe962db..e75e188b145b 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -1138,12 +1138,6 @@  int af_alg_get_rsgl(struct sock *sk, struct msghdr *msg, int flags,
 		if (!af_alg_readable(sk))
 			break;
 
-		if (!ctx->used) {
-			err = af_alg_wait_for_data(sk, flags);
-			if (err)
-				return err;
-		}
-
 		seglen = min_t(size_t, (maxsize - len),
 			       msg_data_left(msg));
 
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 7d2d162666e5..97243068af15 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -111,6 +111,12 @@  static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
 	size_t usedpages = 0;		/* [in]  RX bufs to be used from user */
 	size_t processed = 0;		/* [in]  TX bufs to be consumed */
 
+	if (ctx->more) {
+		err = af_alg_wait_for_data(sk, flags);
+		if (err)
+			return err;
+	}
+
 	/*
 	 * Data length provided by caller via sendmsg/sendpage that has not
 	 * yet been processed.
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 30cff827dd8f..6fb595cd63ac 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -72,6 +72,12 @@  static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
 	int err = 0;
 	size_t len = 0;
 
+	if (!ctx->used) {
+		err = af_alg_wait_for_data(sk, flags);
+		if (err)
+			return err;
+	}
+
 	/* Allocate cipher request for current operation. */
 	areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) +
 				     crypto_skcipher_reqsize(tfm));