From aa4a829bdaced81e70c215a84ef6595ce8bd4308 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 8 Apr 2017 10:02:46 +0800 Subject: crypto: xts - Fix use-after-free on EINPROGRESS When we get an EINPROGRESS completion in xts, we will end up marking the request as done and freeing it. This then blows up when the request is really completed as we've already freed the memory. Fixes: f1c131b45410 ("crypto: xts - Convert to skcipher") Cc: Reported-by: Nathan Royce Reported-by: Krzysztof Kozlowski Signed-off-by: Herbert Xu Tested-by: Krzysztof Kozlowski --- crypto/xts.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crypto/xts.c b/crypto/xts.c index c976bfac29da..89ace5ebc2da 100644 --- a/crypto/xts.c +++ b/crypto/xts.c @@ -286,6 +286,13 @@ static void encrypt_done(struct crypto_async_request *areq, int err) struct rctx *rctx; rctx = skcipher_request_ctx(req); + + if (err == -EINPROGRESS) { + if (rctx->left != req->cryptlen) + return; + goto out; + } + subreq = &rctx->subreq; subreq->base.flags &= CRYPTO_TFM_REQ_MAY_BACKLOG; @@ -293,6 +300,7 @@ static void encrypt_done(struct crypto_async_request *areq, int err) if (rctx->left) return; +out: skcipher_request_complete(req, err); } @@ -330,6 +338,13 @@ static void decrypt_done(struct crypto_async_request *areq, int err) struct rctx *rctx; rctx = skcipher_request_ctx(req); + + if (err == -EINPROGRESS) { + if (rctx->left != req->cryptlen) + return; + goto out; + } + subreq = &rctx->subreq; subreq->base.flags &= CRYPTO_TFM_REQ_MAY_BACKLOG; @@ -337,6 +352,7 @@ static void decrypt_done(struct crypto_async_request *areq, int err) if (rctx->left) return; +out: skcipher_request_complete(req, err); } -- cgit v1.2.3 From 4702bbeefb490e315189636a5588628c1151223d Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 10 Apr 2017 17:15:48 +0800 Subject: crypto: lrw - Fix use-after-free on EINPROGRESS When we get an EINPROGRESS completion in lrw, we will end up marking the request as done and freeing it. This then blows up when the request is really completed as we've already freed the memory. Fixes: 700cb3f5fe75 ("crypto: lrw - Convert to skcipher") Cc: Signed-off-by: Herbert Xu --- crypto/lrw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crypto/lrw.c b/crypto/lrw.c index 3ea095adafd9..a8bfae4451bf 100644 --- a/crypto/lrw.c +++ b/crypto/lrw.c @@ -345,6 +345,13 @@ static void encrypt_done(struct crypto_async_request *areq, int err) struct rctx *rctx; rctx = skcipher_request_ctx(req); + + if (err == -EINPROGRESS) { + if (rctx->left != req->cryptlen) + return; + goto out; + } + subreq = &rctx->subreq; subreq->base.flags &= CRYPTO_TFM_REQ_MAY_BACKLOG; @@ -352,6 +359,7 @@ static void encrypt_done(struct crypto_async_request *areq, int err) if (rctx->left) return; +out: skcipher_request_complete(req, err); } @@ -389,6 +397,13 @@ static void decrypt_done(struct crypto_async_request *areq, int err) struct rctx *rctx; rctx = skcipher_request_ctx(req); + + if (err == -EINPROGRESS) { + if (rctx->left != req->cryptlen) + return; + goto out; + } + subreq = &rctx->subreq; subreq->base.flags &= CRYPTO_TFM_REQ_MAY_BACKLOG; @@ -396,6 +411,7 @@ static void decrypt_done(struct crypto_async_request *areq, int err) if (rctx->left) return; +out: skcipher_request_complete(req, err); } -- cgit v1.2.3 From ef0579b64e93188710d48667cb5e014926af9f1b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 10 Apr 2017 17:27:57 +0800 Subject: crypto: ahash - Fix EINPROGRESS notification callback The ahash API modifies the request's callback function in order to clean up after itself in some corner cases (unaligned final and missing finup). When the request is complete ahash will restore the original callback and everything is fine. However, when the request gets an EBUSY on a full queue, an EINPROGRESS callback is made while the request is still ongoing. In this case the ahash API will incorrectly call its own callback. This patch fixes the problem by creating a temporary request object on the stack which is used to relay EINPROGRESS back to the original completion function. This patch also adds code to preserve the original flags value. Fixes: ab6bf4e5e5e4 ("crypto: hash - Fix the pointer voodoo in...") Cc: Reported-by: Sabrina Dubroca Tested-by: Sabrina Dubroca Signed-off-by: Herbert Xu --- crypto/ahash.c | 79 ++++++++++++++++++++++++++---------------- include/crypto/internal/hash.h | 10 ++++++ 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/crypto/ahash.c b/crypto/ahash.c index e58c4970c22b..826cd7ab4d4a 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -32,6 +32,7 @@ struct ahash_request_priv { crypto_completion_t complete; void *data; u8 *result; + u32 flags; void *ubuf[] CRYPTO_MINALIGN_ATTR; }; @@ -253,6 +254,8 @@ static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt) priv->result = req->result; priv->complete = req->base.complete; priv->data = req->base.data; + priv->flags = req->base.flags; + /* * WARNING: We do not backup req->priv here! The req->priv * is for internal use of the Crypto API and the @@ -267,38 +270,44 @@ static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt) return 0; } -static void ahash_restore_req(struct ahash_request *req) +static void ahash_restore_req(struct ahash_request *req, int err) { struct ahash_request_priv *priv = req->priv; + if (!err) + memcpy(priv->result, req->result, + crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); + /* Restore the original crypto request. */ req->result = priv->result; - req->base.complete = priv->complete; - req->base.data = priv->data; + + ahash_request_set_callback(req, priv->flags, + priv->complete, priv->data); req->priv = NULL; /* Free the req->priv.priv from the ADJUSTED request. */ kzfree(priv); } -static void ahash_op_unaligned_finish(struct ahash_request *req, int err) +static void ahash_notify_einprogress(struct ahash_request *req) { struct ahash_request_priv *priv = req->priv; + struct crypto_async_request oreq; - if (err == -EINPROGRESS) - return; - - if (!err) - memcpy(priv->result, req->result, - crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); + oreq.data = priv->data; - ahash_restore_req(req); + priv->complete(&oreq, -EINPROGRESS); } static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; + if (err == -EINPROGRESS) { + ahash_notify_einprogress(areq); + return; + } + /* * Restore the original request, see ahash_op_unaligned() for what * goes where. @@ -309,7 +318,7 @@ static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) */ /* First copy req->result into req->priv.result */ - ahash_op_unaligned_finish(areq, err); + ahash_restore_req(areq, err); /* Complete the ORIGINAL request. */ areq->base.complete(&areq->base, err); @@ -325,7 +334,12 @@ static int ahash_op_unaligned(struct ahash_request *req, return err; err = op(req); - ahash_op_unaligned_finish(req, err); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; + + ahash_restore_req(req, err); return err; } @@ -360,25 +374,14 @@ int crypto_ahash_digest(struct ahash_request *req) } EXPORT_SYMBOL_GPL(crypto_ahash_digest); -static void ahash_def_finup_finish2(struct ahash_request *req, int err) +static void ahash_def_finup_done2(struct crypto_async_request *req, int err) { - struct ahash_request_priv *priv = req->priv; + struct ahash_request *areq = req->data; if (err == -EINPROGRESS) return; - if (!err) - memcpy(priv->result, req->result, - crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); - - ahash_restore_req(req); -} - -static void ahash_def_finup_done2(struct crypto_async_request *req, int err) -{ - struct ahash_request *areq = req->data; - - ahash_def_finup_finish2(areq, err); + ahash_restore_req(areq, err); areq->base.complete(&areq->base, err); } @@ -389,11 +392,15 @@ static int ahash_def_finup_finish1(struct ahash_request *req, int err) goto out; req->base.complete = ahash_def_finup_done2; - req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + err = crypto_ahash_reqtfm(req)->final(req); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; out: - ahash_def_finup_finish2(req, err); + ahash_restore_req(req, err); return err; } @@ -401,7 +408,16 @@ static void ahash_def_finup_done1(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; + if (err == -EINPROGRESS) { + ahash_notify_einprogress(areq); + return; + } + + areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + err = ahash_def_finup_finish1(areq, err); + if (areq->priv) + return; areq->base.complete(&areq->base, err); } @@ -416,6 +432,11 @@ static int ahash_def_finup(struct ahash_request *req) return err; err = tfm->update(req); + if (err == -EINPROGRESS || + (err == -EBUSY && (ahash_request_flags(req) & + CRYPTO_TFM_REQ_MAY_BACKLOG))) + return err; + return ahash_def_finup_finish1(req, err); } diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 1d4f365d8f03..f6d9af3efa45 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -166,6 +166,16 @@ static inline struct ahash_instance *ahash_alloc_instance( return crypto_alloc_instance2(name, alg, ahash_instance_headroom()); } +static inline void ahash_request_complete(struct ahash_request *req, int err) +{ + req->base.complete(&req->base, err); +} + +static inline u32 ahash_request_flags(struct ahash_request *req) +{ + return req->base.flags; +} + static inline struct crypto_ahash *crypto_spawn_ahash( struct crypto_ahash_spawn *spawn) { -- cgit v1.2.3 From e6534aebb26e32fbab14df9c713c65e8507d17e4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 10 Apr 2017 17:59:07 +0800 Subject: crypto: algif_aead - Fix bogus request dereference in completion function The algif_aead completion function tries to deduce the aead_request from the crypto_async_request argument. This is broken because the API does not guarantee that the same request will be pased to the completion function. Only the value of req->data can be used in the completion function. This patch fixes it by storing a pointer to sk in areq and using that instead of passing in sk through req->data. Fixes: 83094e5e9e49 ("crypto: af_alg - add async support to...") Cc: Signed-off-by: Herbert Xu --- crypto/algif_aead.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 5a8053758657..ef59d9926ee9 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -40,6 +40,7 @@ struct aead_async_req { struct aead_async_rsgl first_rsgl; struct list_head list; struct kiocb *iocb; + struct sock *sk; unsigned int tsgls; char iv[]; }; @@ -379,12 +380,10 @@ unlock: static void aead_async_cb(struct crypto_async_request *_req, int err) { - struct sock *sk = _req->data; - struct alg_sock *ask = alg_sk(sk); - struct aead_ctx *ctx = ask->private; - struct crypto_aead *tfm = crypto_aead_reqtfm(&ctx->aead_req); - struct aead_request *req = aead_request_cast(_req); + struct aead_request *req = _req->data; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct aead_async_req *areq = GET_ASYM_REQ(req, tfm); + struct sock *sk = areq->sk; struct scatterlist *sg = areq->tsgl; struct aead_async_rsgl *rsgl; struct kiocb *iocb = areq->iocb; @@ -447,11 +446,12 @@ static int aead_recvmsg_async(struct socket *sock, struct msghdr *msg, memset(&areq->first_rsgl, '\0', sizeof(areq->first_rsgl)); INIT_LIST_HEAD(&areq->list); areq->iocb = msg->msg_iocb; + areq->sk = sk; memcpy(areq->iv, ctx->iv, crypto_aead_ivsize(tfm)); aead_request_set_tfm(req, tfm); aead_request_set_ad(req, ctx->aead_assoclen); aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - aead_async_cb, sk); + aead_async_cb, req); used -= ctx->aead_assoclen; /* take over all tx sgls from ctx */ -- cgit v1.2.3