hw.oceN.firmware_version served via unbounded strlen over a possibly non-NUL-terminated device string β adjacent softc bytes (incl. kernel pointers) leak to unprivileged readers
Summary
oce_get_fw_version bcopy's exactly 32 device bytes into fw_version[32] with no NUL guarantee; the string-sysctl handler serves strlen+1 bytes with strlen NOT bounded. A device returning 32 non-NUL bytes makes the read walk into macaddr and DMA pointers and copy those bytes to any unprivileged sysctl reader. Guest guarded replica: strlen walked 87 bytes (+55 past the buffer). Terminate-the-string fix (1 hunk). Also flags the repo-wide unbounded-strlen string-sysctl pattern.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4386 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 2.6 KB | β raw | |
| trigger.c | β | 2.0 KB | view raw | |
| build.sh | β | 65 B | view raw | |
| run.sh | β | 89 B | view raw | |
| run.log | β | 34 B | view raw | |
| t6_guarded_run.log | β | 220 B | view raw | |
| fix.diff | β | 451 B | view raw | |
| env.txt | β | 478 B | view raw | |
| manifest.json | β | 734 B | view raw | |
| verdict.json | β | 2.1 KB | view raw | |
| trigger_replica.log | β | 654 B | view raw |
DF-4386 β hw.oceN.firmware_version can serve an unbounded strlen() over a non-NUL-terminated device string (kernel heap bytes to unprivileged readers)
- Registration:
sys/dev/netif/oce/oce_sysctl.c:84-89(SYSCTL_ADD_STRING(.., &sc->fw_version, sizeof(sc->fw_version), ..)) - Fill:
sys/dev/netif/oce/oce_mbox.c:289βbcopy(fwcmd->params.rsp.fw_ver_str, sc->fw_version, 32);copies exactly 32 device-controlled bytes; the device is not required to NUL-terminate. - Sink:
sys/kern/kern_sysctl.c:1171βSYSCTL_OUT(req, arg1, strlen((char *)arg1)+1);β strlen is not bounded byarg2(32).
If all 32 bytes are non-NUL, strlen walks out of fw_version into the rest
of struct oce_softc (macaddr, then OCE_DMA_MEM bsmbx β kernel virtual
pointers), and SYSCTL_OUT copies strlen+1 bytes to any unprivileged
user running sysctl hw.oceN.firmware_version. Reading sysctls needs no
privilege on DragonFly.
Build / run
./run.sh β hardware gate (no oce NIC on this guest) + guarded replica of
the strlen walk: 32 non-NUL device bytes followed by non-zero adjacent
fields β strlen walks +55 bytes past the buffer.
Expected
t6-style output: strlen() walked: 87 bytes (+55 past the buffer),
RESULT: strlen OOB confirmed. Kernel-path confirmation needs an Emulex
adapter whose GET_FW_VERSION response fills all 32 bytes.
DF-4386 VERDICT
Trace
oce_get_fw_version()(sys/dev/netif/oce/oce_mbox.c:260-292) issues OPCODE_COMMON_GET_FW_VERSION; the response payloadfwcmd->params.rsp.fw_ver_str(uint8_t [32], oce_hw.h:1509) is device-DMA'd.:289bcopy(fw_ver_str, sc->fw_version, 32)copies exactlysizeof(sc->fw_version)bytes β a full-width copy overwrites any NUL, so termination depends entirely on device bytes.oce_add_sysctls()(sys/dev/netif/oce/oce_sysctl.c:84-89) publishes the buffer asCTLFLAG_RDSYSCTL_ADD_STRING.- On read,
sysctl_handle_string()(sys/kern/kern_sysctl.c:1171) doesSYSCTL_OUT(req, arg1, strlen((char *)arg1)+1)β strlen is unbounded by the oid'sarg2. (Contrast the write path at :1176-1181, which is bounded byarg2.) - Layout:
fw_version(oce_if.h:835) is followed bystruct mac_address_format macaddrandOCE_DMA_MEM bsmbx(bus_dma_tag_t/vaddr/paddr kernel pointers). The leaked window runs until the first zero byte; kernel pointers contain zero bytes so the walk stops within tens of bytes β but those tens of bytes (mac, DMA pointers) are copied to userspace verbatim.
Threat model: requires a device (or device firmware compromise) returning a
non-NUL-terminated version string β malicious-peripheral class. The reader
needs no privilege: sysctl hw.oceN.firmware_version is world-readable.
Verification (Phase V)
- Kernel path hardware-gated: no
19a2:PCI device, nohw.ocesysctl tree on the guest (run.log). trigger.cguarded replica on the guest (t6_guarded_run.log): 32 non-NUL bytes + adjacent non-zero fields βstrlen() walked: 87 bytes (+55 past the buffer)β the exact sink behavior at kern_sysctl.c:1171.fix.diff(ocean_mbox.c): forcesc->fw_version[31] = '\0'after the bcopy;git apply --checkOK; patchedoce_mbox.csyntax-validated on the guest kernel toolchain (0 errors).
Bottom line
Certain code-wise, device-gated exploit-wise: Low-severity kernel info leak (adjacent softc bytes incl. kernel pointers) to unprivileged readers. The same unbounded-strlen string-sysctl pattern should be audited repo-wide (kern_sysctl.c:1171 is the sink every CTLFLAG_RD string oid relies on).
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
not_testablenot_testable at runtime (no oce NIC); fix.diff git-apply OK, patched oce_mbox.c syntax-validated with guest kernel toolchain (0 errors).
fix.diff; guest cc -fsyntax-only of patched oce_mbox.c: 0 errors.
Confirmed kernel references
Detail
Evidence (decisive lines)
t6_guarded_run.log (strlen walked 87 bytes, +55 OOB), VERDICT.md trace, fix.diff (git apply --check OK, syntax-validated)
PoC changes
n/a - pass-2 finding authored fresh; first replica hit gcc constant-folding, rewritten with volatile+guard-page model
Verified recommended fix
terminate: sc->fw_version[sizeof(sc->fw_version)-1] = 0 after the bcopy (fix.diff)
Verdict
oce_get_fw_version (oce_mbox.c:289) bcopy's exactly 32 device bytes into sc->fw_version[32] with no NUL guarantee; oce_sysctl.c:84-89 publishes it as a read-only string sysctl whose handler (kern_sysctl.c:1171) serves strlen(arg1)+1 bytes UNBOUNDED by arg2. A device returning 32 non-NUL bytes makes strlen walk into adjacent struct oce_softc fields (macaddr, OCE_DMA_MEM kernel pointers) and copy them to any unprivileged sysctl reader. Hardware-gated on guest (no oce NIC); sink behavior proven by guarded replica (+55 bytes past buffer). Low: malicious-peripheral class, defense-in-depth; also flags the unbounded-strlen string-sysctl pattern.
No comments yet.