| From ef0579b64e93188710d48667cb5e014926af9f1b Mon Sep 17 00:00:00 2001 | 
 | From: Herbert Xu <herbert@gondor.apana.org.au> | 
 | Date: Mon, 10 Apr 2017 17:27:57 +0800 | 
 | Subject: crypto: ahash - Fix EINPROGRESS notification callback | 
 |  | 
 | From: Herbert Xu <herbert@gondor.apana.org.au> | 
 |  | 
 | commit ef0579b64e93188710d48667cb5e014926af9f1b upstream. | 
 |  | 
 | 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...") | 
 | Reported-by: Sabrina Dubroca <sd@queasysnail.net> | 
 | Tested-by: Sabrina Dubroca <sd@queasysnail.net> | 
 | Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> | 
 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 
 |  | 
 | --- | 
 |  crypto/ahash.c                 |   79 +++++++++++++++++++++++++---------------- | 
 |  include/crypto/internal/hash.h |   10 +++++ | 
 |  2 files changed, 60 insertions(+), 29 deletions(-) | 
 |  | 
 | --- a/crypto/ahash.c | 
 | +++ b/crypto/ahash.c | 
 | @@ -31,6 +31,7 @@ struct ahash_request_priv { | 
 |  	crypto_completion_t complete; | 
 |  	void *data; | 
 |  	u8 *result; | 
 | +	u32 flags; | 
 |  	void *ubuf[] CRYPTO_MINALIGN_ATTR; | 
 |  }; | 
 |   | 
 | @@ -252,6 +253,8 @@ static int ahash_save_req(struct ahash_r | 
 |  	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 | 
 | @@ -266,38 +269,44 @@ static int ahash_save_req(struct ahash_r | 
 |  	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. | 
 | @@ -308,7 +317,7 @@ static void ahash_op_unaligned_done(stru | 
 |  	 */ | 
 |   | 
 |  	/* 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); | 
 | @@ -324,7 +333,12 @@ static int ahash_op_unaligned(struct aha | 
 |  		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; | 
 |  } | 
 | @@ -359,25 +373,14 @@ int crypto_ahash_digest(struct ahash_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); | 
 |  } | 
 | @@ -388,11 +391,15 @@ static int ahash_def_finup_finish1(struc | 
 |  		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; | 
 |  } | 
 |   | 
 | @@ -400,7 +407,16 @@ static void ahash_def_finup_done1(struct | 
 |  { | 
 |  	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); | 
 |  } | 
 | @@ -415,6 +431,11 @@ static int ahash_def_finup(struct ahash_ | 
 |  		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); | 
 |  } | 
 |   | 
 | --- a/include/crypto/internal/hash.h | 
 | +++ b/include/crypto/internal/hash.h | 
 | @@ -166,6 +166,16 @@ static inline struct ahash_instance *aha | 
 |  	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) | 
 |  { |