β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2364

Silent 64-bit->32-bit length truncation when invoking chacha_encrypt_bytes

Summary

_chacha20poly1305_update and _chacha20poly1305_final typed with size_t in_len but pass directly to chacha_encrypt_bytes(...uint32_t bytes). Implicit narrowing silently truncates lengths>=2^32 while poly1305_update (size_t bytes) does NOT truncate producing internal inconsistency where MAC covers more data than encrypted. For in_len>=2^32 keystream generated for only low 32 bits while MAC computed over full length breaking AEAD invariant. All current callers bounded well under 2^32 (WireGuard fixed sizes mbuf pkthdr.len int32). Latent API misuse risk for future >4GiB caller. [VERIFIED pass2: reproduced - in_len 2^32 passes 0 to chacha and 4294967296 to poly1305 splitting keystream and MAC with no error; KAT control bit-identical to real KPI; KKASSERT guard fix-validated]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2364 Β· 8 files
FileTypeDescriptionSize
env.txt β€” 412 B view raw
fix.diff β€” 1.3 KB view raw
panic_fix.txt β€” 553 B view raw
rig β€” 4.0 KB ↓ download
run_fixed_kernel.log β€” 2.8 KB view raw
run_kernel_baseline.log β€” 172 B view raw
verdict.json β€” 2.1 KB view raw
fix.diff β€” 1.3 KB view raw

Fix verification

not_testable
↓ fix.diffper-fix-DF-2364

Confirmed kernel references

Detail

Evidence (decisive lines)

run_fixed_kernel.log + session baseline (TRUNCATION: chacha saw 0, poly saw 4294967296; KAT MATCH), panic_fix.txt (post-fix assertion), fix.diff

PoC changes

the recorder forwards to the real primitives below a threshold and only records above it (no 4GiB poly pass); initial RFC vector reference was wrong for the 32-byte payload - replaced by twin comparison against the real kernel KPI

Verified recommended fix

KKASSERT in_len <= 0xffffffffU in _chacha20poly1305_update (recommend a runtime clamp upstream)

Verdict

_chacha20poly1305_update with in_len = 2^32 (a legal complete-block multiple for the size_t API) splits silently: the recording interposition on the verbatim TU shows chacha_encrypt_bytes receiving bytes=0 (uint32_t truncation) while poly1305_update receives 4294967296 (size_t, full) - keystream covers the low 32 bits, MAC covers everything, the AEAD invariant breaks with no error. The KAT control (small lengths, recorders forwarding to the real primitives) matches the real kernel KPI output bit-for-bit, proving the rig drives production code. With the KKASSERT guard the same call is rejected loudly on INVARIANTS kernels (assertion text captured). Latent: every in-tree caller is bounded far below 2^32 (mbuf pkthdr.len is int32).