diff mbox

crypto: marvell: properly handle CRYPTO_TFM_REQ_MAY_BACKLOG-flagged requests

Message ID 1442589936-18042-1-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive)
State Accepted
Delegated to: Herbert Xu
Headers show

Commit Message

Thomas Petazzoni Sept. 18, 2015, 3:25 p.m. UTC
The mv_cesa_queue_req() function calls crypto_enqueue_request() to
enqueue a request. In the normal case (i.e the queue isn't full), this
function returns -EINPROGRESS. The current Marvell CESA crypto driver
takes this into account and cleans up the request only if an error
occured, i.e if the return value is not -EINPROGRESS.

Unfortunately this causes problems with
CRYPTO_TFM_REQ_MAY_BACKLOG-flagged requests. When such a request is
passed to crypto_enqueue_request() and the queue is full,
crypto_enqueue_request() will return -EBUSY, but will keep the request
enqueued nonetheless. This situation was not properly handled by the
Marvell CESA driver, which was anyway cleaning up the request in such
a situation. When later on the request was taken out of the backlog
and actually processed, a kernel crash occured due to the internal
driver data structures for this structure having been cleaned up.

To avoid this situation, this commit adds a
mv_cesa_req_needs_cleanup() helper function which indicates if the
request needs to be cleaned up or not after a call to
crypto_enqueue_request(). This helper allows to do the cleanup only in
the appropriate cases, and all call sites of mv_cesa_queue_req() are
fixed to use this new helper function.

Reported-by: Vincent Donnefort <vdonnefort@gmail.com>
Fixes: db509a45339fd ("crypto: marvell/cesa - add TDMA support")
Cc: <stable@vger.kernel.org> # v4.2+
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 drivers/crypto/marvell/cesa.h   | 27 +++++++++++++++++++++++++++
 drivers/crypto/marvell/cipher.c |  7 +++----
 drivers/crypto/marvell/hash.c   |  8 +++-----
 3 files changed, 33 insertions(+), 9 deletions(-)

Comments

Boris BREZILLON Sept. 18, 2015, 3:40 p.m. UTC | #1
Hi Thomas,

On Fri, 18 Sep 2015 17:25:36 +0200
Thomas Petazzoni <thomas.petazzoni@free-electrons.com> wrote:

> The mv_cesa_queue_req() function calls crypto_enqueue_request() to
> enqueue a request. In the normal case (i.e the queue isn't full), this
> function returns -EINPROGRESS. The current Marvell CESA crypto driver
> takes this into account and cleans up the request only if an error
> occured, i.e if the return value is not -EINPROGRESS.
> 
> Unfortunately this causes problems with
> CRYPTO_TFM_REQ_MAY_BACKLOG-flagged requests. When such a request is
> passed to crypto_enqueue_request() and the queue is full,
> crypto_enqueue_request() will return -EBUSY, but will keep the request
> enqueued nonetheless. This situation was not properly handled by the
> Marvell CESA driver, which was anyway cleaning up the request in such
> a situation. When later on the request was taken out of the backlog
> and actually processed, a kernel crash occured due to the internal
> driver data structures for this structure having been cleaned up.
> 
> To avoid this situation, this commit adds a
> mv_cesa_req_needs_cleanup() helper function which indicates if the
> request needs to be cleaned up or not after a call to
> crypto_enqueue_request(). This helper allows to do the cleanup only in
> the appropriate cases, and all call sites of mv_cesa_queue_req() are
> fixed to use this new helper function.
> 
> Reported-by: Vincent Donnefort <vdonnefort@gmail.com>
> Fixes: db509a45339fd ("crypto: marvell/cesa - add TDMA support")
> Cc: <stable@vger.kernel.org> # v4.2+
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
>  drivers/crypto/marvell/cesa.h   | 27 +++++++++++++++++++++++++++
>  drivers/crypto/marvell/cipher.c |  7 +++----
>  drivers/crypto/marvell/hash.c   |  8 +++-----
>  3 files changed, 33 insertions(+), 9 deletions(-)
> 

[...]

>  
>  static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
> diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
> index 0745cf3..3df2f4e 100644
> --- a/drivers/crypto/marvell/cipher.c
> +++ b/drivers/crypto/marvell/cipher.c
> @@ -189,7 +189,6 @@ static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
>  {
>  	struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
>  	struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
> -

Nitpick, but you're removing an empty line, and this doesn't seem
related to the bug itself.

Anyway, Thanks for fixing that.

Best Regards,

Boris
Vincent Donnefort Sept. 21, 2015, 9:15 a.m. UTC | #2
Hi Thomas,

Your patch fixes the problem on my side.

Tested-by: Vincent Donnefort <vdonnefort@gmail.com>

On Fri, Sep 18, 2015 at 05:25:36PM +0200, Thomas Petazzoni wrote:
> The mv_cesa_queue_req() function calls crypto_enqueue_request() to
> enqueue a request. In the normal case (i.e the queue isn't full), this
> function returns -EINPROGRESS. The current Marvell CESA crypto driver
> takes this into account and cleans up the request only if an error
> occured, i.e if the return value is not -EINPROGRESS.
> 
> Unfortunately this causes problems with
> CRYPTO_TFM_REQ_MAY_BACKLOG-flagged requests. When such a request is
> passed to crypto_enqueue_request() and the queue is full,
> crypto_enqueue_request() will return -EBUSY, but will keep the request
> enqueued nonetheless. This situation was not properly handled by the
> Marvell CESA driver, which was anyway cleaning up the request in such
> a situation. When later on the request was taken out of the backlog
> and actually processed, a kernel crash occured due to the internal
> driver data structures for this structure having been cleaned up.
> 
> To avoid this situation, this commit adds a
> mv_cesa_req_needs_cleanup() helper function which indicates if the
> request needs to be cleaned up or not after a call to
> crypto_enqueue_request(). This helper allows to do the cleanup only in
> the appropriate cases, and all call sites of mv_cesa_queue_req() are
> fixed to use this new helper function.
> 
> Reported-by: Vincent Donnefort <vdonnefort@gmail.com>
> Fixes: db509a45339fd ("crypto: marvell/cesa - add TDMA support")
> Cc: <stable@vger.kernel.org> # v4.2+
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
>  drivers/crypto/marvell/cesa.h   | 27 +++++++++++++++++++++++++++
>  drivers/crypto/marvell/cipher.c |  7 +++----
>  drivers/crypto/marvell/hash.c   |  8 +++-----
>  3 files changed, 33 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h
> index b60698b..bc2a55b 100644
> --- a/drivers/crypto/marvell/cesa.h
> +++ b/drivers/crypto/marvell/cesa.h
> @@ -687,6 +687,33 @@ static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
>  
>  int mv_cesa_queue_req(struct crypto_async_request *req);
>  
> +/*
> + * Helper function that indicates whether a crypto request needs to be
> + * cleaned up or not after being enqueued using mv_cesa_queue_req().
> + */
> +static inline int mv_cesa_req_needs_cleanup(struct crypto_async_request *req,
> +					    int ret)
> +{
> +	/*
> +	 * The queue still had some space, the request was queued
> +	 * normally, so there's no need to clean it up.
> +	 */
> +	if (ret == -EINPROGRESS)
> +		return false;
> +
> +	/*
> +	 * The queue had not space left, but since the request is
> +	 * flagged with CRYPTO_TFM_REQ_MAY_BACKLOG, it was added to
> +	 * the backlog and will be processed later. There's no need to
> +	 * clean it up.
> +	 */
> +	if (ret == -EBUSY && req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG)
> +		return false;
> +
> +	/* Request wasn't queued, we need to clean it up */
> +	return true;
> +}
> +
>  /* TDMA functions */
>  
>  static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
> diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
> index 0745cf3..3df2f4e 100644
> --- a/drivers/crypto/marvell/cipher.c
> +++ b/drivers/crypto/marvell/cipher.c
> @@ -189,7 +189,6 @@ static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
>  {
>  	struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
>  	struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
> -
>  	creq->req.base.engine = engine;
>  
>  	if (creq->req.base.type == CESA_DMA_REQ)
> @@ -431,7 +430,7 @@ static int mv_cesa_des_op(struct ablkcipher_request *req,
>  		return ret;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS)
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ablkcipher_cleanup(req);
>  
>  	return ret;
> @@ -551,7 +550,7 @@ static int mv_cesa_des3_op(struct ablkcipher_request *req,
>  		return ret;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS)
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ablkcipher_cleanup(req);
>  
>  	return ret;
> @@ -693,7 +692,7 @@ static int mv_cesa_aes_op(struct ablkcipher_request *req,
>  		return ret;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS)
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ablkcipher_cleanup(req);
>  
>  	return ret;
> diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
> index ae9272e..e8d0d71 100644
> --- a/drivers/crypto/marvell/hash.c
> +++ b/drivers/crypto/marvell/hash.c
> @@ -739,10 +739,8 @@ static int mv_cesa_ahash_update(struct ahash_request *req)
>  		return 0;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS) {
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ahash_cleanup(req);
> -		return ret;
> -	}
>  
>  	return ret;
>  }
> @@ -766,7 +764,7 @@ static int mv_cesa_ahash_final(struct ahash_request *req)
>  		return 0;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS)
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ahash_cleanup(req);
>  
>  	return ret;
> @@ -791,7 +789,7 @@ static int mv_cesa_ahash_finup(struct ahash_request *req)
>  		return 0;
>  
>  	ret = mv_cesa_queue_req(&req->base);
> -	if (ret && ret != -EINPROGRESS)
> +	if (mv_cesa_req_needs_cleanup(&req->base, ret))
>  		mv_cesa_ahash_cleanup(req);
>  
>  	return ret;
> -- 
> 2.5.2
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Herbert Xu Sept. 21, 2015, 3:04 p.m. UTC | #3
On Fri, Sep 18, 2015 at 05:25:36PM +0200, Thomas Petazzoni wrote:
> The mv_cesa_queue_req() function calls crypto_enqueue_request() to
> enqueue a request. In the normal case (i.e the queue isn't full), this
> function returns -EINPROGRESS. The current Marvell CESA crypto driver
> takes this into account and cleans up the request only if an error
> occured, i.e if the return value is not -EINPROGRESS.
> 
> Unfortunately this causes problems with
> CRYPTO_TFM_REQ_MAY_BACKLOG-flagged requests. When such a request is
> passed to crypto_enqueue_request() and the queue is full,
> crypto_enqueue_request() will return -EBUSY, but will keep the request
> enqueued nonetheless. This situation was not properly handled by the
> Marvell CESA driver, which was anyway cleaning up the request in such
> a situation. When later on the request was taken out of the backlog
> and actually processed, a kernel crash occured due to the internal
> driver data structures for this structure having been cleaned up.
> 
> To avoid this situation, this commit adds a
> mv_cesa_req_needs_cleanup() helper function which indicates if the
> request needs to be cleaned up or not after a call to
> crypto_enqueue_request(). This helper allows to do the cleanup only in
> the appropriate cases, and all call sites of mv_cesa_queue_req() are
> fixed to use this new helper function.
> 
> Reported-by: Vincent Donnefort <vdonnefort@gmail.com>
> Fixes: db509a45339fd ("crypto: marvell/cesa - add TDMA support")
> Cc: <stable@vger.kernel.org> # v4.2+
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Applied to crypto.
diff mbox

Patch

diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h
index b60698b..bc2a55b 100644
--- a/drivers/crypto/marvell/cesa.h
+++ b/drivers/crypto/marvell/cesa.h
@@ -687,6 +687,33 @@  static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
 
 int mv_cesa_queue_req(struct crypto_async_request *req);
 
+/*
+ * Helper function that indicates whether a crypto request needs to be
+ * cleaned up or not after being enqueued using mv_cesa_queue_req().
+ */
+static inline int mv_cesa_req_needs_cleanup(struct crypto_async_request *req,
+					    int ret)
+{
+	/*
+	 * The queue still had some space, the request was queued
+	 * normally, so there's no need to clean it up.
+	 */
+	if (ret == -EINPROGRESS)
+		return false;
+
+	/*
+	 * The queue had not space left, but since the request is
+	 * flagged with CRYPTO_TFM_REQ_MAY_BACKLOG, it was added to
+	 * the backlog and will be processed later. There's no need to
+	 * clean it up.
+	 */
+	if (ret == -EBUSY && req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG)
+		return false;
+
+	/* Request wasn't queued, we need to clean it up */
+	return true;
+}
+
 /* TDMA functions */
 
 static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
index 0745cf3..3df2f4e 100644
--- a/drivers/crypto/marvell/cipher.c
+++ b/drivers/crypto/marvell/cipher.c
@@ -189,7 +189,6 @@  static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
 {
 	struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
 	struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
-
 	creq->req.base.engine = engine;
 
 	if (creq->req.base.type == CESA_DMA_REQ)
@@ -431,7 +430,7 @@  static int mv_cesa_des_op(struct ablkcipher_request *req,
 		return ret;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS)
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ablkcipher_cleanup(req);
 
 	return ret;
@@ -551,7 +550,7 @@  static int mv_cesa_des3_op(struct ablkcipher_request *req,
 		return ret;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS)
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ablkcipher_cleanup(req);
 
 	return ret;
@@ -693,7 +692,7 @@  static int mv_cesa_aes_op(struct ablkcipher_request *req,
 		return ret;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS)
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ablkcipher_cleanup(req);
 
 	return ret;
diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
index ae9272e..e8d0d71 100644
--- a/drivers/crypto/marvell/hash.c
+++ b/drivers/crypto/marvell/hash.c
@@ -739,10 +739,8 @@  static int mv_cesa_ahash_update(struct ahash_request *req)
 		return 0;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS) {
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ahash_cleanup(req);
-		return ret;
-	}
 
 	return ret;
 }
@@ -766,7 +764,7 @@  static int mv_cesa_ahash_final(struct ahash_request *req)
 		return 0;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS)
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ahash_cleanup(req);
 
 	return ret;
@@ -791,7 +789,7 @@  static int mv_cesa_ahash_finup(struct ahash_request *req)
 		return 0;
 
 	ret = mv_cesa_queue_req(&req->base);
-	if (ret && ret != -EINPROGRESS)
+	if (mv_cesa_req_needs_cleanup(&req->base, ret))
 		mv_cesa_ahash_cleanup(req);
 
 	return ret;