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

Non-NUL-terminated arrayname[16] printed with %s: kernel stack overread disclosed through world-readable sysctl hptmv.status

Summary

memcpy of exactly MAX_ARRAY_NAME(16) bytes into char arrayname[16] then %s - reads past the stack buffer until a zero byte; same class at :477 (%-20s on the raw field). Demonstrated leak mechanism via harness (17-char output, 17th from past the buffer); terminator fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4766 Β· 10 files
FileTypeDescriptionSize
README.md β€” 1.1 KB ↓ raw
VERDICT.md β€” 1.1 KB ↓ raw
h1_harness.c β€” 18.7 KB view raw
build.sh β€” 97 B view raw
run.sh β€” 140 B view raw
build.log β€” 1.2 KB view raw
run.log β€” 355 B view raw
run.fixed.log β€” 692 B view raw
env.txt β€” 258 B view raw
fix.diff β€” 2.4 KB view raw

DF-4766 β€” non-NUL-terminated arrayname[16] printed with %s β†’ kernel stack overread in world-readable sysctl

File: sys/dev/raid/hptmv/hptproc.c (pass 2)

./build.sh
kldload /root/h1/h1harness.ko
sysctl h1harness.trigger=3      # baseline: 17-char row (overread)
sysctl h1harness.trigger=6      # fix.diff applied: exactly 16 chars

Baseline (run.log): Channel 1 A 953883MB Normal NNNNNNNNNNNNNNNA β€” the 17th char is stack data read past arrayname[16].

Root cause: hptproc.c:388 memcpy's MAX_ARRAY_NAME(16) bytes of ArrayName (UCHAR[16], array.h:95 β€” nothing guarantees a terminator; the on-disk config field at array.h:146 is copied verbatim) into char arrayname[16] (hptproc.c:375) and prints it with %s (hptproc.c:396) to any local user reading sysctl hptmv.status.

Same class in the same file: hpt_copy_array_info prints pArray->u.array.ArrayName with %-20s (hptproc.c:477) β€” reads past the 16-byte field within the VDevice when unterminated (bounded by the first zero byte in adjacent struct memory).

Fix: fix.diff F3 (size to MAX_ARRAY_NAME+1 + force termination). Validated in-harness (trigger 6).

VERDICT.md
↓ download raw

DF-4766 VERDICT

Status: REPRODUCED (source+harness, guest). The verbatim hpt_copy_disk_info with a 16-byte non-NUL ArrayName printed one byte past the 16-byte stack buffer (17-char row; extra byte from the adjacent name[32] stack object). OOB-read mechanism certain; the production leak content is the sysctl reader thread's adjacent kernel stack (uninitialized locals), disclosed to unprivileged readers of hptmv.status. PoC-level impact recorded as none because the demonstrated byte was adjacent-buffer content, not kernel pointers β€” the finding itself is filed as a Low kernel-stack info-leak exposure. Fix validated in-harness: with arrayname[MAX_ARRAY_NAME]='\0' the row prints exactly 16 characters and stops.

Kernel refs: hptproc.c:375, :388, :396, :477; array.h:95, :146.

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

Harness-level A/B: baseline prints 17 chars (overread); fixed prints exactly 16 and stops (run.fixed.log trigger 6).

run.log vs run.fixed.log; fix.diff F3 hunk applies cleanly.
↓ fix.diffper-fix-DF-4766

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (17-char row, extra 'A' past the 16-byte buffer); run.fixed.log trigger 6 (exactly 16 'N's with the terminator fix).

PoC changes

Trigger 3 of the DF-4764 KLD harness (verbatim function, real driver structs).

Verified recommended fix

arrayname[MAX_ARRAY_NAME + 1] plus arrayname[MAX_ARRAY_NAME] = '\0' after the memcpy (fix.diff F3).

Verdict

REPRODUCED at source+harness (guest): the VERBATIM hpt_copy_disk_info (hptproc.c:372-399) run with a parent array whose ArrayName is 16 non-NUL bytes printed 'Channel 1 A 953883MB Normal NNNNNNNNNNNNNNNNA' - 17 characters, the 17th read from past the 16-byte stack buffer arrayname[16] (observed to be the first byte of the adjacent name[32] stack object). In production the bytes past arrayname are whatever lives on the kernel stack of the sysctl reader thread (uninitialized stack, loop locals) and are disclosed to any local user via the world-readable sysctl hptmv.status; demonstrated leak content here was a benign adjacent buffer byte, so impact is conservatively recorded as 'none' at PoC level while the OOB-read mechanism itself is proven. Reachable whenever an array carries a 16-byte, non-terminated ArrayName (ArrayName is UCHAR[16] in the on-disk config, array.h:146, and is copied verbatim; nothing forces a terminator). Fix (arrayname[MAX_ARRAY_NAME+1] + terminator, fix.diff F3) validated in-harness: trigger 6 prints exactly 16 'N's and stops (run.fixed.log).