Re: [PATCH] crypto: crypto4xx - Replace bitwise OR with logical OR in crypto4xx_build_pd

From: Nathan Chancellor
Date: Thu Nov 12 2020 - 16:49:10 EST


On Thu, Nov 12, 2020 at 10:21:35PM +0100, Christian Lamparter wrote:
> Hello,
>
> On 12/11/2020 21:07, Nathan Chancellor wrote:
> > Clang warns:
> >
> > drivers/crypto/amcc/crypto4xx_core.c:921:60: warning: operator '?:' has
> > lower precedence than '|'; '|' will be evaluated first
> > [-Wbitwise-conditional-parentheses]
> > (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
> > drivers/crypto/amcc/crypto4xx_core.c:921:60: note: place parentheses
> > around the '|' expression to silence this warning
> > (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
> > ^
> > )
> > drivers/crypto/amcc/crypto4xx_core.c:921:60: note: place parentheses
> > around the '?:' expression to evaluate it first
> > (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
> > ^
> > (
> > 1 warning generated.
> >
> > It looks like this should have been a logical OR so that
> > PD_CTL_HASH_FINAL gets added to the w bitmask if crypto_tfm_alg_type
> > is either CRYPTO_ALG_TYPE_AHASH or CRYPTO_ALG_TYPE_AEAD.
> Yes. This probably wasn't spotted earlier since the driver doesn't make
> use of CRYPTO_ALG_TYPE_AHASH (yet). This is because the hash accelerator
> setup cost was never worth it.
>
> > Change the operator so that everything works properly.
> I'm curious if this is true. Is there a way to break this somehow on purpose?

I do not really have a way to validate that statement, I just figured
that the operator being wrong meant that something could go wrong that
was not intended.

> I've extracted the code from line 921 and added the defines
> (the CRYPTO_ALG_... from the current 5.10-rc3 crypto.h and the PD_CTL_
> from crypto4xx_reg_def.h) and replaced the u32 with uint32_t
> so it runs in userspace too:
>
> --- crypto4xx_test.c ---
> /* test study - is it possible to break the | vs || in crypto4xx's code */
>
> #include <stdio.h>
> #include <stdint.h>
>
> #define CRYPTO_ALG_TYPE_AEAD 0x00000003
> #define CRYPTO_ALG_TYPE_AHASH 0x0000000f
> #define PD_CTL_HASH_FINAL (1<<4) /* Stand-in for BIT(4) */
> #define PD_CTL_HOST_READY (1<<0) /* BIT(0) */
>
> uint32_t func_with_bitwise_or(uint32_t alg_type)
> {
> return PD_CTL_HOST_READY |
> ((alg_type == CRYPTO_ALG_TYPE_AHASH) |
> (alg_type == CRYPTO_ALG_TYPE_AEAD) ?
> PD_CTL_HASH_FINAL : 0);
> }

Looking at this more, I think the only reason that the code works as is
is because PD_CTL_HOST_READY is 1 AND CRYPTO_ALG_TYPE_AHASH is not used.

(alg_type == CRYPTO_ALG_TYPE_AEAD) ? PD_CTL_HASH_FINAL : 0 is evaluated
first, which results in either PD_CTL_HASH_FINAL or 0.

Then (alg_type == CRYPTO_ALG_TYPE_AHASH) is evaluated, which is
evaluated to either 0 or 1.

Then we mask everything together:

PD_CTL_HOST_READY | (0 || 1) | (PD_CTL_HOST_READY || 0)

If PD_CTL_HOST_READY was anything other than BIT(0), we would have an
extra 0x1 in the mask. That realistically might not matter, I did not
have a full look over the code to see what this might mean. If
CRYPTO_ALG_TYPE_AHASH was used, it could be used over
CRYPTO_ALG_TYPE_AEAD and PD_CTL_HASH_FINAL would never get added to the
mask, which certainly sounds like a bug.

> uint32_t func_with_logical_or(uint32_t alg_type)
> {
> return PD_CTL_HOST_READY |
> ((alg_type == CRYPTO_ALG_TYPE_AHASH) ||
> (alg_type == CRYPTO_ALG_TYPE_AEAD) ?
> PD_CTL_HASH_FINAL : 0);
> }
>
> int main(int arg, char **args)
> {
> uint32_t alg;
>
> for (alg = 0; alg < 0x10; alg++) { /* this is because CRYPTO_ALG_TYPE_MASK is 0xf */
> if (func_with_bitwise_or(alg) != func_with_logical_or(alg)) {
> printf("for alg_type:%d, the bitwise result=%d doesn't match the logical result=%d\n",
> alg, func_with_bitwise_or(alg), func_with_logical_or(alg));
> return 1;
> }
> }
> printf("logical and bitwise always agreed.\n");
>
> return 0;
> }
> --- EOF ---
>
> Both gcc (gcc version 10.2.0 (Debian 10.2.0-17)) or clang (clang version 9.0.1-15)
> version always gave the "logical and bitwise always agreed.". which means there wasn't
> anything wrong and this patch just makes clang happy? Or can you get it to break?
>
> Also, can you please give this patch a try:
> --- extra-bracket.patch
>
> --- a/drivers/crypto/amcc/crypto4xx_core.c
> +++ b/drivers/crypto/amcc/crypto4xx_core.c
> @@ -932,8 +932,8 @@ int crypto4xx_build_pd(struct crypto_async_request *req,
> }
>
> pd->pd_ctl.w = PD_CTL_HOST_READY |
> - ((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AHASH) |
> - (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
> + (((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AHASH) |
> + (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD)) ?
> PD_CTL_HASH_FINAL : 0);
> pd->pd_ctl_len.w = 0x00400000 | (assoclen + datalen);
> pd_uinfo->state = PD_ENTRY_INUSE | (is_busy ? PD_ENTRY_BUSY : 0);
>
> ---
> I'm mostly curious if clang will warn about it too.

It does not with that diff. I guess it is entirely up to you which one
we go with.

> That said:
> Reviewed-by: Christian Lamparter <chunkeey@xxxxxxxxx>

Thank you for all the analysis and taking a look over the patch, I
appreciate it!

Cheers,
Nathan