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

SipHash24_TestVectors passes len=0 to SipHash_Update for the empty-message vector, panicking INVARIANTS kernels

Summary

The test module's vector loop starts at i=0 (empty message) and calls SipHash_Update(&ctx, in, 0). SipHash_Update forwards to SipBuf, whose contract KASSERT '(!final && len > 0) || (final && len == 0)' rejects zero-length non-final updates - on any INVARIANTS kernel this is an immediate panic at test start. REPRODUCED live: kld module built from the unmodified source panics the stock INVARIANTS guest ('panic: SipBuf: invalid parameters', backtrace SipHash24_TestVectors -> SipHash_Update -> SipBuf). Fix validated: one-line guard -> module loads, prints result=1 (all 64 vectors pass), unloads cleanly. Reachability today nil (module in no build target, nothing in-tree calls it); impact ceiling: a developer wiring the test into a debug kernel gets a deterministic panic. Vector table integrity CONFIRMED: all 64 entries match the official SipHash-2-4 reference set (proven twice - userland + in-kernel).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3566 Β· 18 files
FileTypeDescriptionSize
README.md β€” 2.8 KB ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
panic.txt β€” 793 B view raw
trigger.c β€” 637 B view raw
siphash_test.c β€” 4.9 KB view raw
siphash_test_fixed.c β€” 5.0 KB view raw
Makefile β€” 103 B ↓ download
siphash_kat.c β€” 436 B view raw
build.log β€” 549 B view raw
kldload_baseline.log β€” 830 B view raw
kldload_fixed.log β€” 449 B view raw
run.userland.log β€” 273 B view raw
run.userland.warn.log β€” 217 B view raw
fix.diff β€” 331 B view raw
env.txt β€” 228 B view raw
code_hashes.txt β€” 157 B view raw
manifest.json β€” 1.1 KB view raw
verdict.json β€” 3.1 KB view raw

DF-3566 β€” SipHash24_TestVectors trips SipHash KASSERT β†’ INVARIANTS-kernel panic

What

sys/crypto/siphash/siphash_test.c runs its 64-vector table starting at the empty-message vector (i=0). For i=0 it calls SipHash_Update(&ctx, in, 0). sys/crypto/siphash/siphash.c asserts (!final && len > 0) || (final && len == 0) in SipBuf() (siphash.c:92) β€” a zero-length non-final update is a contract violation and panics any kernel built with INVARIANTS.

Reachability today: none through any shipped build β€” siphash_test.c is in no build target (not in sys/conf/files, not in sys/crypto/Makefile); nothing in-tree references SipHash24_TestVectors() except its own header declaration. It is developer/test-harness code: anyone who wires it into an INVARIANTS kernel (its documented purpose) gets an immediate panic at module init/test start. Severity Low: local DoS, developer-facing, dead code by default.

Build (module, in-guest)

# /tmp/sipmod/Makefile:
KMOD=   siphashtest
SRCS=   trigger.c siphash_test.c
SYSDIR=/usr/src/sys
.include "${SYSDIR}/conf/kmod.mk"

cd /tmp/sipmod && make

trigger.c is a 25-line DEV/DECLARE_MODULE shim whose MOD_LOAD handler calls SipHash24_TestVectors(). siphash_test.c is the unmodified kernel source (md5 3b580668a665816235b2c715bb293734, identical to the audited tree).

Run (baseline β€” expect panic)

kldload /tmp/sipmod/siphashtest.ko

Expected: panic: SipBuf: invalid parameters, DDB prompt db>, ssh dead. Observed: exactly that β€” see panic.txt for the full serial-console trace (SipHash24_TestVectors β†’ SipHash_Update β†’ SipBuf β†’ panic).

Run (patched β€” expect all vectors pass, no panic)

Same module with siphash_test_fixed.c (if (i > 0) guard around the Update):

kldload /tmp/sipmod_fixed/siphashtest_fixed.ko

Observed: siphashtest: calling SipHash24_TestVectors() / siphashtest: result=1 (no panic) in dmesg, clean kldunload, guest healthy (kldload_fixed.log). result=1 means all 64 vectors match in-kernel.

Userland cross-check

siphash_kat.c compiles the unmodified siphash.c + siphash_test.c as userland objects (shim for sys/systm.h/sys/libkern.h only):

  • abort-style KASSERT shim β†’ KASSERT FAILED: (!final && len > 0) ..., SIGABRT (run.userland.log) β€” same trip, no kernel needed.
  • warn-style shim β†’ KASSERT-WARN once, then SIPHASH24_TESTVECTORS=1 (ALL 64 VECTORS MATCH) (run.userland.warn.log): with the assertion bypassed the empty-message vector computes correctly (SipBuf is a functional no-op for len==0), so the vector table itself is sound and the one-line guard is the complete fix.

Environment

DragonFly dfbsd 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS) β€” see env.txt. Guest restored with vm.sh reset with-src after the panic runs.

VERDICT.md
↓ download raw

DF-3566 VERDICT β€” SipHash24_TestVectors empty-vector KASSERT panic

Outcome: REPRODUCED (in-kernel panic, INVARIANTS build), fix validated.

Root cause (source-traced): sys/crypto/siphash/siphash_test.c:116 loops i = 0..63 and, at i = 0 (the empty-message reference vector), calls SipHash_Update(&ctx, in, 0) [siphash_test.c:121] SipHash_Update forwards to SipBuf(ctx, &s, 0, 0), whose contract assertion KASSERT((!final && len > 0) || (final && len == 0), ("%s: invalid parameters", func)); [siphash.c:92-93] rejects zero-length non-final updates. SipBuf is otherwise a functional no-op for len==0 (x = MIN(0, 8-0) = 0, no compression, siphash.c:95-109), so with the assert bypassed the empty vector hashes correctly β€” the table itself is sound and the guard is the complete fix.

Reproduction (stock INVARIANTS guest, uname in env.txt): 1. Built siphashtest.ko from the UNMODIFIED kernel source siphash_test.c plus a 25-line module shim (trigger.c) calling SipHash24_TestVectors() at MOD_LOAD (build.log). 2. kldload siphashtest.ko -> panic: SipBuf: invalid parameters SipBuf <- SipBuf <- SipHash_Update <- SipHash24_TestVectors <- siphashtest_modevent <- module_register_init (panic.txt) Guest dropped to DDB; ssh dead; vm.sh status => down. 3. Userland same-source build confirms the identical trip without any kernel involvement: abort-shim => "KASSERT FAILED: (!final && len > 0) || (final && len == 0)", SIGABRT rc=134 (run.userland.log).

Fix validation: Applied the one-line guard (fix.diff) to siphash_test_fixed.c, rebuilt the module, kldload on the same stock INVARIANTS guest: siphashtest: calling SipHash24_TestVectors() siphashtest: result=1 (no panic) (kldload_fixed.log) result=1 == all 64 vectors match IN-KERNEL; clean kldunload; guest healthy.

Reachability / impact ceiling: siphash_test.c is compiled into NOTHING today: absent from sys/conf/files and from sys/crypto/Makefile (the crypto KMOD builds only siphash.c). No in-tree caller of SipHash24_TestVectors(). An unprivileged user cannot reach this code on any shipped kernel; the trigger is a developer building the test into an INVARIANTS kernel (its documented purpose). Impact = local debug-kernel DoS (panic at test start). Hence severity Low, bucket base:*.

Negative results worth recording: - Vector-table integrity: all 64 entries match the official SipHash-2-4 reference set β€” proven twice: userland (warn-shim run) and in-kernel (patched module result=1). - The KASSERT trip is the ONLY defect found in the file; loop bounds (in[64], vectors[64][8], k[16]) are exact; no uninitialized use other than the i=0 semantic; kprintf-only side effects.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff applied to siphash_test_fixed.c, module rebuilt and kldloaded on the same stock INVARIANTS guest: no panic, 'siphashtest: result=1 (no panic)' in dmesg, clean kldunload, guest stayed healthy (kldload_fixed.log). Baseline (unpatched) panicked to DDB on the same guest (panic.txt). fix_patched_reproduced=0 because the patched run by definition produces NO panic β€” the expected bad behavior is gone.

kldload_fixed.log + panic.txt (baseline vs patched on identical guest/kernel)
↓ fix.diffper-fix-DF-3566

Confirmed kernel references

Detail

Evidence (decisive lines)

panic.txt (serial-console panic trace after kldload); kldload_fixed.log (patched module: result=1, no panic, clean unload); run.userland.log (same-source userland build: KASSERT FAILED + SIGABRT at vector 0); VERDICT.md for the source trace

PoC changes

trigger.c is a new 25-line module shim (DECLARE_MODULE + MOD_LOAD handler calling SipHash24_TestVectors and kprintf'ing the result); siphash_test.c itself is the unmodified kernel source (md5 3b580668a665816235b2c715bb293734). No changes to the audited source were needed to reproduce.

Verified recommended fix

Guard the zero-length update: 'if (i > 0) SipHash_Update(&ctx, in, i);' in siphash_test.c

Verdict

SipHash24_TestVectors calls SipHash_Update(ctx, in, 0) for the empty-message vector, violating SipBuf's zero-length contract and panicking any INVARIANTS kernel. Reproduced live on the stock INVARIANTS guest via a kld module built from the unmodified source: 'panic: SipBuf: invalid parameters' with the full backtrace (SipHash24_TestVectors -> SipHash_Update -> SipBuf). The one-line guard (fix.diff) was validated in-kernel: patched module prints result=1 (all 64 vectors pass) and unloads cleanly. Reachability is nil in shipped builds (file is in no build target), so severity is Low; impact ceiling is a developer-configured debug-kernel panic.