twofish_set_key performs no key-length validation enabling stack/struct OOB writes and mk_tab corruption on out-of-range key_len_bits
Summary
twofish_set_key derives ctx->k_len=key_len_bits/64 with no check key_len_bits is {128 192 256}. For oversized key_len_bits (k_len>=5) key-expansion loop writes past size-4 stack arrays me_key[]/mo_key[] and past s_key[4] into mk_tab[] and reads past caller key buffer. For key_len_bits<=0 loop skipped gen_mk_tab switch falls through leaving mk_tab uninitialised (heap info-leak source). Code defect is genuine memory-corruption primitive currently NOT reachable from any unprivileged syscall because every in-kernel caller (twofish_cbc_setkey twofish_xts_setkey) whitelists key length before calling. Latent/defense-in-depth gap becomes High-severity corruption if future caller forgets to validate. Related to DF-0965. [VERIFIED pass2: reproduced - key_len_bits 320 writes s_key 4 into mk_tab 0 and leaves schedule stale, 0 keeps previous key active, userspace twin SIGSEGVs on in_key guard page and smashes callee frame at k_len 16; length-refusal fix-validated, all in-tree callers pre-validate]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2371 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| build.log | β | 62 B | view raw | |
| build.sh | β | 86 B | view raw | |
| env.txt | β | 412 B | view raw | |
| fix.diff | β | 380 B | view raw | |
| rig | β | 4.0 KB | β download | |
| run.log | β | 112 B | view raw | |
| run.sh | β | 161 B | view raw | |
| run_fixed.log | β | 96 B | view raw | |
| run_fixed_kernel.log | β | 2.8 KB | view raw | |
| run_kernel_baseline.log | β | 172 B | view raw | |
| twofish_rig.c | β | 4.4 KB | view raw | |
| verdict.json | β | 2.3 KB | view raw | |
| fix.diff | β | 380 B | view raw |
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
session baseline + run_fixed_kernel.log (setkey320: mk_tab[0]=ce22885b vs cafebabe, mk_tab[1] stale; setkey0: k_len=0, stale schedule), run.log + run_fixed.log (twin: keyread/stacksmash fault then clean), fix.diff
PoC changes
none; kernel KAT digest 57ff739d4dc92c1b... cross-checked between kernel and userspace twin (identical), the quoted 256-bit KAT suffix in the rig comment was from memory and is superseded by the twin comparison
Verified recommended fix
refuse key_len_bits outside {128,192,256} at the top of twofish_set_key
Verdict
Real in-kernel twofish_set_key with out-of-range key_len_bits: 320 (k_len=5) writes s_key[4] into mk_tab[0] (struct order l_key, s_key, mk_tab) - observed as the magic mk_tab[0] changing while gen_mk_tab switch(5) falls through leaving the rest stale - and 0 leaves the previous key schedule in place (k_len=0, mk_tab from keyA) while l_key is re-derived from uninitialized me_key/mo_key stack. The userspace twin of the verbatim file shows the harder primitives directly: key_len_bits=2048 walks in_key reads 256 bytes into a guard page (SIGSEGV) and k_len=16 smashes the callee frame (SIGBUS on return). With the length check both kernel scenarios leave the ctx untouched (k_len stays 4, magic intact) and the twin stops faulting. All in-tree callers (twofish_cbc_setkey/twofish_xts_setkey in cryptoapi.c:670/:770) pre-validate, so this is a latent API landmine - as filed.
No comments yet.