From 403648373816ae8eb76fe4a393836b0d65fa8f85 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 31 Aug 2026 10:38:30 +0200 Subject: [PATCH] s390/crypto: Fix return code handling at skcipher_walk_done in PAES algorithms All the 4 PAES cipher processing loops were not checking the return value of skcipher_walk_done() immediately after calling it. This could lead to error masking when both the walk operation failed and a subsequent key conversion was needed (k < n condition). Add immediate error checks after skcipher_walk_done() in all main processing loops (ECB, CBC, CTR, XTS modes) to ensure walk errors are properly propagated and not masked by subsequent operations. With that comes a slight rework around the skcipher_walk_done() invocation. It is now necessary to check if the walk has already been finalized (walk->nbytes is then 0) or not to avoid double de-allocation of resources held by the walk. Fixes: 6cd87cb5ef6c ("s390/crypto: Rework protected key AES for true asynch support") Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Cc: stable@vger.kernel.org # 6.16+ Signed-off-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/crypto/paes_s390.c | 38 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c index 973436592318..89785ab95e6b 100644 --- a/arch/s390/crypto/paes_s390.c +++ b/arch/s390/crypto/paes_s390.c @@ -432,8 +432,11 @@ static int ecb_paes_do_crypt(struct s390_paes_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -495,7 +498,7 @@ static int ecb_paes_crypt(struct skcipher_request *req, unsigned long modifier) atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (rc != -EINPROGRESS && walk->nbytes) skcipher_walk_done(walk, rc); out: @@ -549,7 +552,7 @@ static int ecb_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ecb_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -690,6 +693,8 @@ static int cbc_paes_do_crypt(struct s390_paes_ctx *ctx, if (k) { memcpy(walk->iv, param->iv, AES_BLOCK_SIZE); rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; } if (k < n) { if (!maysleep) { @@ -752,7 +757,7 @@ static int cbc_paes_crypt(struct skcipher_request *req, unsigned long modifier) atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (rc != -EINPROGRESS && walk->nbytes) skcipher_walk_done(walk, rc); out: @@ -806,7 +811,7 @@ static int cbc_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = cbc_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -968,6 +973,11 @@ static int ctr_paes_do_crypt(struct s390_paes_ctx *ctx, AES_BLOCK_SIZE); crypto_inc(walk->iv, AES_BLOCK_SIZE); rc = skcipher_walk_done(walk, nbytes - k); + if (rc) { + if (locked) + mutex_unlock(&ctrblk_lock); + goto out; + } } if (k < n) { if (!maysleep) { @@ -1061,7 +1071,7 @@ static int ctr_paes_crypt(struct skcipher_request *req) atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (rc != -EINPROGRESS && walk->nbytes) skcipher_walk_done(walk, rc); out: @@ -1105,7 +1115,7 @@ static int ctr_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ctr_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -1283,8 +1293,11 @@ static int xts_paes_do_crypt_fullkey(struct s390_pxts_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param->key + offset, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -1377,8 +1390,11 @@ static int xts_paes_do_crypt_2keys(struct s390_pxts_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param->key + offset, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -1485,7 +1501,7 @@ static inline int xts_paes_crypt(struct skcipher_request *req, unsigned long mod atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (rc != -EINPROGRESS && walk->nbytes) skcipher_walk_done(walk, rc); out: @@ -1539,7 +1555,7 @@ static int xts_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = xts_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); }