Commit 2060e284 authored by Eric Biggers's avatar Eric Biggers Committed by Herbert Xu

crypto: x86/morus - fix handling chunked inputs and MAY_SLEEP

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: 56e8e57f ("crypto: morus - Add common SIMD glue code for MORUS")
Cc: <stable@vger.kernel.org> # v4.18+
Cc: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Reviewed-by: default avatarOndrej Mosnacek <omosnace@redhat.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent ba6771c0
...@@ -85,31 +85,20 @@ static void crypto_morus1280_glue_process_ad( ...@@ -85,31 +85,20 @@ static void crypto_morus1280_glue_process_ad(
static void crypto_morus1280_glue_process_crypt(struct morus1280_state *state, static void crypto_morus1280_glue_process_crypt(struct morus1280_state *state,
struct morus1280_ops ops, struct morus1280_ops ops,
struct aead_request *req) struct skcipher_walk *walk)
{ {
struct skcipher_walk walk; while (walk->nbytes >= MORUS1280_BLOCK_SIZE) {
u8 *cursor_src, *cursor_dst; ops.crypt_blocks(state, walk->src.virt.addr,
unsigned int chunksize, base; walk->dst.virt.addr,
round_down(walk->nbytes,
ops.skcipher_walk_init(&walk, req, false); MORUS1280_BLOCK_SIZE));
skcipher_walk_done(walk, walk->nbytes % MORUS1280_BLOCK_SIZE);
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);
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, ...@@ -147,12 +136,15 @@ static void crypto_morus1280_glue_crypt(struct aead_request *req,
struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct crypto_aead *tfm = crypto_aead_reqtfm(req);
struct morus1280_ctx *ctx = crypto_aead_ctx(tfm); struct morus1280_ctx *ctx = crypto_aead_ctx(tfm);
struct morus1280_state state; struct morus1280_state state;
struct skcipher_walk walk;
ops.skcipher_walk_init(&walk, req, true);
kernel_fpu_begin(); kernel_fpu_begin();
ctx->ops->init(&state, &ctx->key, req->iv); ctx->ops->init(&state, &ctx->key, req->iv);
crypto_morus1280_glue_process_ad(&state, ctx->ops, req->src, req->assoclen); 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); ctx->ops->final(&state, tag_xor, req->assoclen, cryptlen);
kernel_fpu_end(); kernel_fpu_end();
......
...@@ -85,31 +85,19 @@ static void crypto_morus640_glue_process_ad( ...@@ -85,31 +85,19 @@ static void crypto_morus640_glue_process_ad(
static void crypto_morus640_glue_process_crypt(struct morus640_state *state, static void crypto_morus640_glue_process_crypt(struct morus640_state *state,
struct morus640_ops ops, struct morus640_ops ops,
struct aead_request *req) struct skcipher_walk *walk)
{ {
struct skcipher_walk walk; while (walk->nbytes >= MORUS640_BLOCK_SIZE) {
u8 *cursor_src, *cursor_dst; ops.crypt_blocks(state, walk->src.virt.addr,
unsigned int chunksize, base; walk->dst.virt.addr,
round_down(walk->nbytes, MORUS640_BLOCK_SIZE));
ops.skcipher_walk_init(&walk, req, false); skcipher_walk_done(walk, walk->nbytes % MORUS640_BLOCK_SIZE);
}
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);
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, ...@@ -143,12 +131,15 @@ static void crypto_morus640_glue_crypt(struct aead_request *req,
struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct crypto_aead *tfm = crypto_aead_reqtfm(req);
struct morus640_ctx *ctx = crypto_aead_ctx(tfm); struct morus640_ctx *ctx = crypto_aead_ctx(tfm);
struct morus640_state state; struct morus640_state state;
struct skcipher_walk walk;
ops.skcipher_walk_init(&walk, req, true);
kernel_fpu_begin(); kernel_fpu_begin();
ctx->ops->init(&state, &ctx->key, req->iv); ctx->ops->init(&state, &ctx->key, req->iv);
crypto_morus640_glue_process_ad(&state, ctx->ops, req->src, req->assoclen); 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); ctx->ops->final(&state, tag_xor, req->assoclen, cryptlen);
kernel_fpu_end(); kernel_fpu_end();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment