[PATCH 23/30] crypto: sa2ul - fix stack overflow in sa_prepare_iopads
From: Manorit Chawdhry
Date: Tue Sep 15 2026 - 06:08:34 EST
The sa_export_shash() function uses a fixed-size stack union to hold
the hash state during export. This union was sized at 104 bytes,
covering sha1_state and sha256_state but no longer including
sha512_state as it did when the AEAD support was first added, leaving
zero margin once crypto_shash_export() needs to write more than 104
bytes:
BUG: KASAN: stack-out-of-bounds in __crypto_sha256_export.isra.0+0xf4/0x138
Write of size 1 at addr ffff800084c46d78 by task cryptomgr_test/219
Hardware name: Texas Instruments J721S2 EVM (DT)
Call trace:
show_stack+0x18/0x24 (C)
dump_stack_lvl+0x68/0x94
print_report+0x118/0x200
kasan_report+0xa8/0xe4
__asan_store1
__crypto_sha256_export.isra.0
crypto_sha256_export
__crypto_shash_export
crypto_shash_export
sa_export_shash+0xa4/0x198
sa_prepare_iopads+0x234/0x460
sa_init_sc+0x268/0x730
sa_aead_setkey+0x20c/0x390
sa_aead_cbc_sha256_setkey+0xa4/0xe4
crypto_aead_setkey
test_aead_vec_cfg
test_aead_vec
alg_test_aead
The buggy address belongs to stack of task cryptomgr_test/219,
offset 152 in frame: sa_export_shash+0x0/0x198
This frame has 1 object: [48, 152) 'sha'
This reproduced on every board tested (311 occurrences on a single
J721S2 EVM boot log alone), always via the authenc(hmac(sha256),
cbc(aes))-sa2ul AEAD self-test. It surfaced after upstream sha256
changes (e.g. commit 3bf533787910 ("crypto: sha256 - Use the partial
block API")) altered which sha256 shash implementation gets selected
for hmac(sha256), but the underlying union has been undersized ever
since it was shrunk to drop sha512_state.
Fix this by replacing the fixed-size stack union with a dynamically
allocated buffer sized via crypto_shash_statesize(). This ensures the
buffer is always large enough for the current hash algorithm's state,
regardless of future changes to the state structures. The buffer is
allocated with kmalloc(GFP_KERNEL) and freed with kfree_sensitive() on
all exit paths to prevent information leakage.
Fixes: ad0bb4e4d226 ("crypto: sa2ul - Reduce stack usage")
Assisted-by: Sisyphus:claude-sonnet-5
Signed-off-by: Manorit Chawdhry <m-chawdhry@xxxxxx>
---
drivers/crypto/sa2ul.c | 52 +++++++++++++++++++++++++++++---------------------
1 file changed, 30 insertions(+), 22 deletions(-)
diff --git a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c
index 36c8403b5713..265d1afaad81 100644
--- a/drivers/crypto/sa2ul.c
+++ b/drivers/crypto/sa2ul.c
@@ -18,6 +18,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <crypto/aes.h>
#include <crypto/authenc.h>
@@ -392,39 +393,51 @@ static void prepare_kopad(u8 *k_opad, const u8 *key, u16 key_sz)
k_opad[i] = 0x5c;
}
-static int sa_export_shash(void *state, struct shash_desc *hash,
- int digest_size, __be32 *out)
+static int sa_export_shash(struct shash_desc *hash, int digest_size,
+ __be32 *out)
{
- struct sha1_state *sha1;
- struct sha256_state *sha256;
u32 *result;
int ret = 0;
+ int state_size;
+ u8 *sha;
+
+ state_size = crypto_shash_statesize(hash->tfm);
+ if (state_size <= 0) {
+ dev_err(sa_k3_dev, "%s: invalid state_size=%d\n", __func__,
+ state_size);
+ return -EINVAL;
+ }
+
+ sha = kmalloc(state_size, GFP_KERNEL);
+ if (!sha)
+ return -ENOMEM;
/* Export the intermediate digest to program into SA2UL */
- ret = crypto_shash_export(hash, state);
+ ret = crypto_shash_export(hash, sha);
if (ret) {
dev_err(sa_k3_dev, "%s: crypto_shash_export failed\n",
__func__);
+ kfree_sensitive(sha);
return ret;
}
switch (digest_size) {
case SHA1_DIGEST_SIZE:
- sha1 = state;
- result = sha1->state;
+ result = (u32 *)sha;
break;
case SHA256_DIGEST_SIZE:
- sha256 = state;
- result = sha256->state;
+ result = (u32 *)sha;
break;
default:
dev_err(sa_k3_dev, "%s: bad digest_size=%d\n", __func__,
digest_size);
+ kfree_sensitive(sha);
return -EINVAL;
}
cpu_to_be32_array(out, result, digest_size / 4);
+ kfree_sensitive(sha);
return ret;
}
@@ -435,16 +448,11 @@ static int sa_prepare_iopads(struct algo_data *data, const u8 *key,
int block_size = crypto_shash_blocksize(data->ctx->shash);
int digest_size = crypto_shash_digestsize(data->ctx->shash);
int ret = 0;
-
- union {
- struct sha1_state sha1;
- struct sha256_state sha256;
- u8 k_pad[SHA1_BLOCK_SIZE];
- } sha;
+ u8 k_pad[SHA1_BLOCK_SIZE];
shash->tfm = data->ctx->shash;
- prepare_kipad(sha.k_pad, key, key_sz);
+ prepare_kipad(k_pad, key, key_sz);
ret = crypto_shash_init(shash);
if (ret) {
@@ -452,20 +460,20 @@ static int sa_prepare_iopads(struct algo_data *data, const u8 *key,
__func__, __LINE__, ret);
return ret;
}
- ret = crypto_shash_update(shash, sha.k_pad, block_size);
+ ret = crypto_shash_update(shash, k_pad, block_size);
if (ret) {
dev_err(sa_k3_dev, "%s: %d: crypto_shash_update for ipad failed, ret=%d\n",
__func__, __LINE__, ret);
return ret;
}
- ret = sa_export_shash(&sha, shash, digest_size, ipad);
+ ret = sa_export_shash(shash, digest_size, ipad);
if (ret) {
dev_err(sa_k3_dev, "%s: %d: sa_export_shash for ipad failed, ret=%d\n",
__func__, __LINE__, ret);
return ret;
}
- prepare_kopad(sha.k_pad, key, key_sz);
+ prepare_kopad(k_pad, key, key_sz);
ret = crypto_shash_init(shash);
if (ret) {
@@ -473,20 +481,20 @@ static int sa_prepare_iopads(struct algo_data *data, const u8 *key,
__func__, __LINE__, ret);
return ret;
}
- ret = crypto_shash_update(shash, sha.k_pad, block_size);
+ ret = crypto_shash_update(shash, k_pad, block_size);
if (ret) {
dev_err(sa_k3_dev, "%s: %d: crypto_shash_update for opad failed, ret=%d\n",
__func__, __LINE__, ret);
return ret;
}
- ret = sa_export_shash(&sha, shash, digest_size, opad);
+ ret = sa_export_shash(shash, digest_size, opad);
if (ret) {
dev_err(sa_k3_dev, "%s: %d: sa_export_shash for opad failed, ret=%d\n",
__func__, __LINE__, ret);
return ret;
}
- memzero_explicit(&sha, sizeof(sha));
+ memzero_explicit(k_pad, SHA1_BLOCK_SIZE);
return ret;
}
--
2.43.0