crypto: atmel-aes - Fix IV handling when req->nbytes < ivsize
authorTudor Ambarus <tudor.ambarus@microchip.com>
Fri, 4 Oct 2019 08:55:37 +0000 (08:55 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 13 Dec 2019 07:52:46 +0000 (08:52 +0100)
commit 86ef1dfcb561473fbf5e199d58d18c55554d78be upstream.

commit 394a9e044702 ("crypto: cfb - add missing 'chunksize' property")
adds a test vector where the input length is smaller than the IV length
(the second test vector). This revealed a NULL pointer dereference in
the atmel-aes driver, that is caused by passing an incorrect offset in
scatterwalk_map_and_copy() when atmel_aes_complete() is called.

Do not save the IV in req->info of ablkcipher_request (or equivalently
req->iv of skcipher_request) when req->nbytes < ivsize, because the IV
will not be further used.

While touching the code, modify the type of ivsize from int to
unsigned int, to comply with the return type of
crypto_ablkcipher_ivsize().

Fixes: 91308019ecb4 ("crypto: atmel-aes - properly set IV after {en,de}crypt")
Cc: <stable@vger.kernel.org>
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/crypto/atmel-aes.c

index 801aeab..f662914 100644 (file)
@@ -493,6 +493,29 @@ static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd)
 static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err);
 #endif
 
+static void atmel_aes_set_iv_as_last_ciphertext_block(struct atmel_aes_dev *dd)
+{
+       struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+       struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+       struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req);
+       unsigned int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+
+       if (req->nbytes < ivsize)
+               return;
+
+       if (rctx->mode & AES_FLAGS_ENCRYPT) {
+               scatterwalk_map_and_copy(req->info, req->dst,
+                                        req->nbytes - ivsize, ivsize, 0);
+       } else {
+               if (req->src == req->dst)
+                       memcpy(req->info, rctx->lastc, ivsize);
+               else
+                       scatterwalk_map_and_copy(req->info, req->src,
+                                                req->nbytes - ivsize,
+                                                ivsize, 0);
+       }
+}
+
 static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
 {
 #ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
@@ -503,26 +526,8 @@ static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
        clk_disable(dd->iclk);
        dd->flags &= ~AES_FLAGS_BUSY;
 
-       if (!dd->ctx->is_aead) {
-               struct ablkcipher_request *req =
-                       ablkcipher_request_cast(dd->areq);
-               struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
-               struct crypto_ablkcipher *ablkcipher =
-                       crypto_ablkcipher_reqtfm(req);
-               int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
-
-               if (rctx->mode & AES_FLAGS_ENCRYPT) {
-                       scatterwalk_map_and_copy(req->info, req->dst,
-                               req->nbytes - ivsize, ivsize, 0);
-               } else {
-                       if (req->src == req->dst) {
-                               memcpy(req->info, rctx->lastc, ivsize);
-                       } else {
-                               scatterwalk_map_and_copy(req->info, req->src,
-                                       req->nbytes - ivsize, ivsize, 0);
-                       }
-               }
-       }
+       if (!dd->ctx->is_aead)
+               atmel_aes_set_iv_as_last_ciphertext_block(dd);
 
        if (dd->is_async)
                dd->areq->complete(dd->areq, err);
@@ -1128,10 +1133,12 @@ static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
        rctx->mode = mode;
 
        if (!(mode & AES_FLAGS_ENCRYPT) && (req->src == req->dst)) {
-               int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
+               unsigned int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
 
-               scatterwalk_map_and_copy(rctx->lastc, req->src,
-                       (req->nbytes - ivsize), ivsize, 0);
+               if (req->nbytes >= ivsize)
+                       scatterwalk_map_and_copy(rctx->lastc, req->src,
+                                                req->nbytes - ivsize,
+                                                ivsize, 0);
        }
 
        return atmel_aes_handle_queue(dd, &req->base);