hpt_get_logical_devices writes one DEVICEID before the count check (4-byte heap overflow when nMaxCount=0)
- File:
sys/dev/raid/hptmv/gui_lib.c - Lines: 479, 486, 487, 500, 502, 503, 504, 1181, 1186, 1187, 1188, 1190, 1191
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U:C:N/I:L/A:L - CWE: CWE-783 Operator Precedence Logic Error
- Confidence: likely
Summary
In hpt_get_logical_devices(), the bound check
if (count>=nMaxCount) goto done; is performed AFTER
pIds[count++] = VDEV_TO_ID(pLogical);.
When the caller passes nMaxCount=0 (perfectly legal β the input-size and
output-size checks at gui_lib.c:1186-1188 accept nMax=0 because
sizeof(DWORD)+sizeof(DWORD)*0 == 4), the loop body still writes one 4-byte
DEVICEID at pIds[0] before the check fires.
pIds points one DWORD past the start of the output buffer, so with the
minimum 4-byte output allocation this writes 4 bytes off the end of the
kmalloc'd heap buffer.
Root cause
gui_lib.c:500-504:
for (j=0; j<count; j++)
if (pIds[j]==VDEV_TO_ID(pLogical)) goto next;
pIds[count++] = VDEV_TO_ID(pLogical); <-- WRITE
if (count>=nMaxCount) goto done; <-- CHECK (too late)
Caller side (gui_lib.c:1181-1193):
nMax = *(DWORD *)lpInBuffer;
if (nOutBufferSize < sizeof(DWORD)+sizeof(DWORD)*nMax) return -1;
pIds = ((DEVICEID *)lpOutBuffer)+1;
β nMax=0 is permitted, requiring only nOutBufferSize>=4.
The proc/sysctl shim (hptproc.c:297) allocates
kmalloc(nInBufferSize+nOutBufferSize, M_DEVBUF, M_NOWAIT) and sets
lpOutBuffer = ke_area + nInBufferSize.
With nInBufferSize=4 and nOutBufferSize=4 the allocation is 8 bytes [0..7];
pIds[0] writes at ke_area[8..11] β a 4-byte heap overflow.
The overflow contains the truncated-to-32-bits kernel address of the first
enumerated non-spare PVDevice.
Threat
Attacker position: root (sysctl write).
Triggering additionally requires hptmv hardware to be present with at least one
non-spare logical device (otherwise the loop never finds a device and the write
never happens).
Impact: a single 4-byte heap overflow of M_DEVBUF, contents = a partial kernel
pointer.
With slab grooming of M_DEVBUF allocations adjacent to a victim object, this
could corrupt a length/refcount field and be escalated, but the prerequisites
(root + specific RAID hardware + at least one disk) make real-world impact low.
Primary value is as a correctness defect and a minor heap-corruption stepping-stone.
Exploit / PoC
DWORD nMax = 0; /* ask for 0 entries */
uint8_t out[4]; /* minimum: just the count DWORD */
DWORD ret = 0;
struct hpt_ioctl_param p = { HPT_IOCTL_MAGIC, HPT_CTL_CODE(4) /*GET_LOGICAL_DEVICES*/,
&nMax, sizeof nMax, out, sizeof out, &ret };
sysctlbyname("hw.hptmv.status", NULL, NULL, &p, sizeof p);
/* The kernel wrote 4 bytes past the top of the 8-byte kmalloc done in
hptproc.c:297. Verify with a witness allocation pattern or a
DEBUG_MEMGUARD kernel that flags the 4-byte overrun. */
Build: cc -o poc poc.c. Run: ./poc as root.
Success: under DEBUG_MALLOC or memguard, an out-of-range write is flagged;
otherwise the corruption is silent until a neighboring allocation is reused.
Recommended fix
Move the bound check above the write.
--- a/sys/dev/raid/hptmv/gui_lib.c
+++ b/sys/dev/raid/hptmv/gui_lib.c
@@ -499,8 +499,9 @@ static int hpt_get_logical_devices(DEVICEID * pIds, int nMaxCount)
for (j=0; j<count; j++)
if (pIds[j]==VDEV_TO_ID(pLogical)) goto next;
- pIds[count++] = VDEV_TO_ID(pLogical);
- if (count>=nMaxCount) goto done;
+ if (count >= nMaxCount)
+ goto done;
+ pIds[count++] = VDEV_TO_ID(pLogical);
next:;
}
}
Related findings
- DF-1529 (sibling):
hpt_create_array_v2unvalidated Members[] write primitive. - DF-1530 (sibling):
hpt_get_channel_infoOOB read + pointer leak.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1531 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Fix for hptmv gui_lib write-before-check | 307 B | view raw |
| VERDICT.md | verdict | Source-only verification verdict | 804 B | β raw |
| build.sh | build-script | No-op (source-only) | 109 B | view raw |
| run.sh | run-script | No-op (source-only) | 107 B | view raw |
VERDICT DF-1531: hptmv gui_lib write-before-check
Verdict
REPRODUCED (source-confirmed). Bug confirmed at source level; HW/module-gated on this QEMU guest.
Mechanism
pIds[count++]=VDEV_TO_ID writes before count>=nMaxCount check; OOB write on last element.
Source reference: sys/dev/raid/hptmv/gui_lib.c:500-504.
Reproduction
Source-only confirmation: the cited code path was traced line-by-line in sys/ and confirmed.
The bug is real but requires specific hardware (GPU/NIC/HBA) or a loaded kernel module not present
on the QEMU/virtio guest. The finding is HW-gated.
Fix
Validated by combined kernel build: all 41 fix.diffs applied to /usr/src and built with
make -j6 nativekernel KERNCONF=X86_64_GENERIC β rc=0, -Werror clean.
See fix.diff for the git-apply-able patch.
Fix verification
fixedCombined kernel build with all 41 fix.diffs: rc=0, -Werror clean. Runtime test HW-gated.
'>>> Kernel build for X86_64_GENERIC completed' with 0 errors.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- r
- a
- i
- d
- /
- h
- p
- t
- m
- v
- /
- g
- u
- i
- _
- l
- i
- b
- .
- c
- :
- 5
- 0
- 0
Detail
Exploit chain
none
Evidence (decisive lines)
Source confirmed: sys/dev/raid/hptmv/gui_lib.c:500. Combined 41-fix kernel build rc=0 -Werror clean.
PoC changes
fix.diff authored; validated by combined kernel build.
Verified recommended fix
Swap check-before-write. Matches finding.
Verdict
REPRODUCED (source-confirmed). pIds[count++] before count>=nMaxCount check -> OOB write. Cited path verified at sys/dev/raid/hptmv/gui_lib.c:500. HW/module-gated on QEMU guest.
No comments yet.