Kernel panic when using ccm(aes) with the Atmel AES HW accelerator

Romain Izard romain.izard.pro at gmail.com
Fri Oct 27 05:02:06 PDT 2017


2017-10-26 14:34 GMT+02:00 Tudor Ambarus <tudor.ambarus at microchip.com>:
> Hi, Romain,
>
> On 10/18/2017 04:32 PM, Romain Izard wrote:
>>
>> diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
>> index 29e20c37f3a6..f3eabe1f1490 100644
>> --- a/drivers/crypto/atmel-aes.c
>> +++ b/drivers/crypto/atmel-aes.c
>> @@ -80,6 +80,7 @@
>>   #define AES_FLAGS_BUSY         BIT(3)
>>   #define AES_FLAGS_DUMP_REG     BIT(4)
>>   #define AES_FLAGS_OWN_SHA      BIT(5)
>> +#define AES_FLAGS_CIPHERTAIL   BIT(6)
>
>
> not really needed, see below.
>
>>
>>   #define AES_FLAGS_PERSISTENT   (AES_FLAGS_INIT | AES_FLAGS_BUSY)
>>
>> @@ -156,6 +157,7 @@ struct atmel_aes_authenc_ctx {
>>
>>   struct atmel_aes_reqctx {
>>          unsigned long           mode;
>> +       u32                     ciphertail[AES_BLOCK_SIZE / sizeof(u32)];
>
> How about renaming this variable to "lastc"? Ci, i=1..n is the usual
> notation for the ciphertext blocks.
>
OK


> The assumption that the last ciphertext block is of AES_BLOCK_SIZE size
> is correct, this driver is meant just for AES.
>
>>   };
>>
>>   #ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
>> @@ -496,6 +498,11 @@ static void atmel_aes_authenc_complete(struct
>> atmel_aes_dev *dd, int err);
>>
>>   static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
>>   {
>> +       struct ablkcipher_request *req =
>> ablkcipher_request_cast(dd->areq);
>> +       struct crypto_ablkcipher *ablkcipher =
>> crypto_ablkcipher_reqtfm(req);
>> +       struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
>> +       int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
>> +
>>   #ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
>>          atmel_aes_authenc_complete(dd, err);
>>   #endif
>> @@ -503,6 +510,17 @@ static inline int atmel_aes_complete(struct
>> atmel_aes_dev *dd, int err)
>>          clk_disable(dd->iclk);
>>          dd->flags &= ~AES_FLAGS_BUSY;
>>
>> +       if (rctx->mode & AES_FLAGS_CIPHERTAIL) {
>> +               if (rctx->mode & AES_FLAGS_ENCRYPT) {
>> +                       scatterwalk_map_and_copy(req->info, req->dst,
>> +                                                req->nbytes - ivsize,
>> +                                                ivsize, 0);
>> +               } else {
>> +                       memcpy(req->info, rctx->ciphertail, ivsize);
>
>
> why don't we make the same assumption and replace ivsize with
> AES_BLOCK_SIZE? Is there any chance that req->info was allocated with
> less than AES_BLOCK_SIZE size?
>
I do not really know. I'm just getting used to the crypto framework, and the
fact that the iv buffer size is implicit...

>> +                       memset(rctx->ciphertail, 0, ivsize);
>
>
> memset to zero is not necessary.
>
OK

>> +               }
>> +       }
>> +
>>          if (dd->is_async)
>>                  dd->areq->complete(dd->areq, err);
>>
>> @@ -1071,11 +1089,13 @@ static int atmel_aes_ctr_start(struct
>> atmel_aes_dev *dd)
>>
>>   static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long
>> mode)
>>   {
>> +       struct crypto_ablkcipher *ablkcipher;
>>          struct atmel_aes_base_ctx *ctx;
>>          struct atmel_aes_reqctx *rctx;
>>          struct atmel_aes_dev *dd;
>>
>> -       ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
>> +       ablkcipher = crypto_ablkcipher_reqtfm(req);
>> +       ctx = crypto_ablkcipher_ctx(ablkcipher);
>
>
> you can initialize these variables at declaration.
>
OK

>>          switch (mode & AES_FLAGS_OPMODE_MASK) {
>>          case AES_FLAGS_CFB8:
>>                  ctx->block_size = CFB8_BLOCK_SIZE;
>> @@ -1103,7 +1123,15 @@ static int atmel_aes_crypt(struct
>> ablkcipher_request *req, unsigned long mode)
>>                  return -ENODEV;
>>
>>          rctx = ablkcipher_request_ctx(req);
>
>
> while here, please initialize this at declaration.
>
> Also, this one:
> '''
>         dd = atmel_aes_find_dev(ctx);
>         if (!dd)
>                 return -ENODEV;
>
> '''
> should be moved at the beginning of the function. If an initialization
> might fail, let's check it as soon as we can, so that we don't waste
> resources.
>
Most of these initializations are in fact structure casts.

>> -       rctx->mode = mode;
>> +
>> +       if (!(mode & AES_FLAGS_ENCRYPT)) {
>> +               int ivsize = crypto_ablkcipher_ivsize(ablkcipher);
>> +
>> +               scatterwalk_map_and_copy(rctx->ciphertail, req->src,
>> +                               (req->nbytes - ivsize), ivsize, 0);
>> +       }
>> +       rctx->mode = mode | AES_FLAGS_CIPHERTAIL;
>
>
> saving the last ciphertext block is a requirement for skcipher. This
> flag is redundant, there's no need to use it.
>

The idea regarding the ciphertail flag was because atmel_aes_complete
is used by for regular block crypto (ecb, cbc, ctr, etc.) but also for
aead transfers in gcm and authenc cases, which triggered panics in
my code.

I now realize that casting the areq value to an ablkcipher_req is wrong,
as it can also be an aead_request. The ciphertail flag somehow worked
around this issue, but I need to rewrite the patch to take this into account.

Best regards,
-- 
Romain Izard



More information about the linux-arm-kernel mailing list