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)
PoC verification
Evidence pack
findings/poc/DF-4766 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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).
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
fixedHarness-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.
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).
No comments yet.