Re: [PATCH v2 04/15] crypto: x86/morus - fix handling chunked inputs and MAY_SLEEP

From: Ondrej Mosnacek
Date: Tue Feb 05 2019 - 04:32:30 EST


On Fri, Feb 1, 2019 at 8:52 AM Eric Biggers <ebiggers@xxxxxxxxxx> wrote:
> From: Eric Biggers <ebiggers@xxxxxxxxxx>
>
> The x86 MORUS implementations all fail the improved AEAD tests because
> they produce the wrong result with some data layouts. The issue is that
> they assume that if the skcipher_walk API gives 'nbytes' not aligned to
> the walksize (a.k.a. walk.stride), then it is the end of the data. In
> fact, this can happen before the end.
>
> Also, when the CRYPTO_TFM_REQ_MAY_SLEEP flag is given, they can
> incorrectly sleep in the skcipher_walk_*() functions while preemption
> has been disabled by kernel_fpu_begin().
>
> Fix these bugs.
>
> Fixes: 56e8e57fc3a7 ("crypto: morus - Add common SIMD glue code for MORUS")
> Cc: <stable@xxxxxxxxxxxxxxx> # v4.18+
> Cc: Ondrej Mosnacek <omosnace@xxxxxxxxxx>
> Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>

Reviewed-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx>

> ---
> arch/x86/crypto/morus1280_glue.c | 40 +++++++++++++-------------------
> arch/x86/crypto/morus640_glue.c | 39 ++++++++++++-------------------
> 2 files changed, 31 insertions(+), 48 deletions(-)
>
> diff --git a/arch/x86/crypto/morus1280_glue.c b/arch/x86/crypto/morus1280_glue.c
> index 0dccdda1eb3a1..7e600f8bcdad8 100644
> --- a/arch/x86/crypto/morus1280_glue.c
> +++ b/arch/x86/crypto/morus1280_glue.c
> @@ -85,31 +85,20 @@ static void crypto_morus1280_glue_process_ad(
>
> static void crypto_morus1280_glue_process_crypt(struct morus1280_state *state,
> struct morus1280_ops ops,
> - struct aead_request *req)
> + struct skcipher_walk *walk)
> {
> - struct skcipher_walk walk;
> - u8 *cursor_src, *cursor_dst;
> - unsigned int chunksize, base;
> -
> - ops.skcipher_walk_init(&walk, req, false);
> -
> - while (walk.nbytes) {
> - cursor_src = walk.src.virt.addr;
> - cursor_dst = walk.dst.virt.addr;
> - chunksize = walk.nbytes;
> -
> - ops.crypt_blocks(state, cursor_src, cursor_dst, chunksize);
> -
> - base = chunksize & ~(MORUS1280_BLOCK_SIZE - 1);
> - cursor_src += base;
> - cursor_dst += base;
> - chunksize &= MORUS1280_BLOCK_SIZE - 1;
> -
> - if (chunksize > 0)
> - ops.crypt_tail(state, cursor_src, cursor_dst,
> - chunksize);
> + while (walk->nbytes >= MORUS1280_BLOCK_SIZE) {
> + ops.crypt_blocks(state, walk->src.virt.addr,
> + walk->dst.virt.addr,
> + round_down(walk->nbytes,
> + MORUS1280_BLOCK_SIZE));
> + skcipher_walk_done(walk, walk->nbytes % MORUS1280_BLOCK_SIZE);
> + }
>
> - skcipher_walk_done(&walk, 0);
> + if (walk->nbytes) {
> + ops.crypt_tail(state, walk->src.virt.addr, walk->dst.virt.addr,
> + walk->nbytes);
> + skcipher_walk_done(walk, 0);
> }
> }
>
> @@ -147,12 +136,15 @@ static void crypto_morus1280_glue_crypt(struct aead_request *req,
> struct crypto_aead *tfm = crypto_aead_reqtfm(req);
> struct morus1280_ctx *ctx = crypto_aead_ctx(tfm);
> struct morus1280_state state;
> + struct skcipher_walk walk;
> +
> + ops.skcipher_walk_init(&walk, req, true);
>
> kernel_fpu_begin();
>
> ctx->ops->init(&state, &ctx->key, req->iv);
> crypto_morus1280_glue_process_ad(&state, ctx->ops, req->src, req->assoclen);
> - crypto_morus1280_glue_process_crypt(&state, ops, req);
> + crypto_morus1280_glue_process_crypt(&state, ops, &walk);
> ctx->ops->final(&state, tag_xor, req->assoclen, cryptlen);
>
> kernel_fpu_end();
> diff --git a/arch/x86/crypto/morus640_glue.c b/arch/x86/crypto/morus640_glue.c
> index 7b58fe4d9bd1a..cb3a817320160 100644
> --- a/arch/x86/crypto/morus640_glue.c
> +++ b/arch/x86/crypto/morus640_glue.c
> @@ -85,31 +85,19 @@ static void crypto_morus640_glue_process_ad(
>
> static void crypto_morus640_glue_process_crypt(struct morus640_state *state,
> struct morus640_ops ops,
> - struct aead_request *req)
> + struct skcipher_walk *walk)
> {
> - struct skcipher_walk walk;
> - u8 *cursor_src, *cursor_dst;
> - unsigned int chunksize, base;
> -
> - ops.skcipher_walk_init(&walk, req, false);
> -
> - while (walk.nbytes) {
> - cursor_src = walk.src.virt.addr;
> - cursor_dst = walk.dst.virt.addr;
> - chunksize = walk.nbytes;
> -
> - ops.crypt_blocks(state, cursor_src, cursor_dst, chunksize);
> -
> - base = chunksize & ~(MORUS640_BLOCK_SIZE - 1);
> - cursor_src += base;
> - cursor_dst += base;
> - chunksize &= MORUS640_BLOCK_SIZE - 1;
> -
> - if (chunksize > 0)
> - ops.crypt_tail(state, cursor_src, cursor_dst,
> - chunksize);
> + while (walk->nbytes >= MORUS640_BLOCK_SIZE) {
> + ops.crypt_blocks(state, walk->src.virt.addr,
> + walk->dst.virt.addr,
> + round_down(walk->nbytes, MORUS640_BLOCK_SIZE));
> + skcipher_walk_done(walk, walk->nbytes % MORUS640_BLOCK_SIZE);
> + }
>
> - skcipher_walk_done(&walk, 0);
> + if (walk->nbytes) {
> + ops.crypt_tail(state, walk->src.virt.addr, walk->dst.virt.addr,
> + walk->nbytes);
> + skcipher_walk_done(walk, 0);
> }
> }
>
> @@ -143,12 +131,15 @@ static void crypto_morus640_glue_crypt(struct aead_request *req,
> struct crypto_aead *tfm = crypto_aead_reqtfm(req);
> struct morus640_ctx *ctx = crypto_aead_ctx(tfm);
> struct morus640_state state;
> + struct skcipher_walk walk;
> +
> + ops.skcipher_walk_init(&walk, req, true);
>
> kernel_fpu_begin();
>
> ctx->ops->init(&state, &ctx->key, req->iv);
> crypto_morus640_glue_process_ad(&state, ctx->ops, req->src, req->assoclen);
> - crypto_morus640_glue_process_crypt(&state, ops, req);
> + crypto_morus640_glue_process_crypt(&state, ops, &walk);
> ctx->ops->final(&state, tag_xor, req->assoclen, cryptlen);
>
> kernel_fpu_end();
> --
> 2.20.1
>


--
Ondrej Mosnacek <omosnace at redhat dot com>
Associate Software Engineer, Security Technologies
Red Hat, Inc.