inquiry_result: unbounded variable-length loop reads past mbuf end -> remote kernel panic
Summary
inquiry_result(:380-424): pulls up only sizeof(inquiry_result_ep)=1 byte(num_responses)(:380). Loops for(;num_responses>0;num_responses--)(:387). num_responses attacker-controlled first byte NEVER validated vs actual mbuf length. Each iter consumes ~14 bytes(m_copydata 6+1+1+1+3+2 via m_adj/m_copydata/mtod). Chain exhausted -> m_copydata hits KASSERT(m!=NULL) panic DEBUG / NULL-deref panic RELEASE. ng_hci_process_event logs hdr->length(:100) but NEVER enforces it. 1-byte body num_responses=255 reliably triggers. Stale *mtod reads -> neighbor cache info leak. Comment :406 "XXX call m_pullup here?" admits gap. Remote unauth BT. Fix: clamp num_responses to m_pkthdr.len/14.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0542 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | claim, byte-count trace, repro status, fix | 3.9 KB | β raw |
| VERDICT.md | verdict | full narrative: source-confirmed OOB, dead-code blocker, fix | 3.9 KB | β raw |
| fix.diff | suggested-fix | bound inquiry_result loop by m_pkthdr.len (14 bytes/response) | 1.1 KB | view raw |
| build_hci.log | build-log | patched ng_hci.ko module build (both fixes), rc=0 clean | 10.4 KB | view raw |
| build_result.txt | build-log | module builds; kldload fails (ng_bluetooth absent) | 745 B | view raw |
| env.txt | environment | uname, cc, module availability, reachability conclusion | 1.6 KB | view raw |
| hci_event_analysis.sh | trigger-source | source-level byte-layout analysis of a triggering INQUIRY_RESULT HCI event | 1.8 KB | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0542 β inquiry_result: unbounded loop reads past mbuf (netgraph7 HCI)
File: sys/netgraph7/bluetooth/hci/ng_hci_evnt.c
Severity (fileed): High
Class: Out-of-bounds read (mbuf chain over-read) β kernel panic / info leak
Module: ng_hci (netgraph7 bluetooth) β NOT compiled, NOT shipped, NOT
loadable on the default X86_64_GENERIC guest.
The claim (source-level, CONFIRMED)
inquiry_result() (ng_hci_evnt.c:380) pulls up only
sizeof(ng_hci_inquiry_result_ep) = 1 byte (num_responses) and then loops:
for (; ep->num_responses > 0; ep->num_responses --) { /* :387 */
m_copydata(event, 0, sizeof(bdaddr), &bdaddr); m_adj(event, sizeof(bdaddr)); /* 6 */
...
n->page_scan_rep_mode = *mtod(event, u_int8_t *); m_adj(event, 1); /* 1 */
m_adj(event, sizeof(u_int8_t)); /* 1 */
n->page_scan_mode = *mtod(event, u_int8_t *); m_adj(event, 1); /* 1 */
m_adj(event, NG_HCI_CLASS_SIZE); /* 3 */
m_copydata(event, 0, sizeof(n->clock_offset), &n->clock_offset); /* 2 */
}
Each iteration consumes 14 bytes (6+1+1+1+3+2). The controller-supplied
num_responses (ep->num_responses, the first body byte) is never
validated against the actual mbuf length β ng_hci_process_event
(:86) logs hdr->length (:100) but does not enforce it against
m_pkthdr.len.
A single-byte HCI event body of num_responses = 0xFF (255) demands
~3570 bytes but the chain holds far less. After the chain is drained:
m_copydata()hitsKASSERT(m != NULL, "length > size of mbuf chain")atsys/kern/uipc_mbuf.c:1687β panic on INVARIANTS (GENERIC); on a non-INVARIANTS buildm->m_lendereferences NULL β page-fault panic.- Before exhaustion, the
*mtod(event, ...)reads (:408,:414) read stale mbuf residue that is then copied into the neighbor cache (n->page_scan_*) β kernel-memory info leak (limited; the data lands in an in-kernel neighbor struct, not directly returned to userspace, so the leak ceiling is "corrupt neighbor cache / subsequent inquiry leak", not a direct arbitrary-kptr disclosure).
The comment at :406 (/* XXX call m_pullup here? */) admits the gap.
Reproduction status
- Source-level: CONFIRMED (byte-count and
m_copydataKASSERT traced). - Live trigger: NOT possible on the default guest. netgraph7 bluetooth
is
optional netgraph7_bluetooth, absent fromX86_64_GENERIC, and no bluetooth/netgraph7.koships. Buildingng_hci.kostandalone succeeds, butkldloadfails: "depends on ng_bluetooth - not available". Even with the full stack loaded, injecting a crafted HCI event requires a Bluetooth controller /ng_ubt/ng_h4lower driver. β dead code / latent here.
Impact (realistic, conditional)
Conditional on netgraph7 bluetooth being compiled in and a BT HCI controller attached: remote-unauthenticated-packet β kernel panic (DoS) and a limited in-kernel stale-data leak into the neighbor cache. On the shipped default kernel this code is not present, so the practical impact on a default install is none β it is a latent bug in opt-in code.
Old netgraph BT?
sys/netgraph/bluetooth/ does not exist in this tree; the vulnerable
parser lives only in netgraph7. No second copy to fix.
Fix
fix.diff β bound the loop by the remaining mbuf length. A per-response
constant NG_HCI_INQUIRY_RESULT_SIZE (= sizeof(bdaddr_t)+3+NG_HCI_CLASS_SIZE+
sizeof(u_int16_t) = 14) is added to the loop guard so the loop stops as soon
as the remaining event->m_pkthdr.len cannot hold a full response. git-apply
clean; compiles (patched ng_hci.ko build rc=0, build_hci.log).
Build
# on the guest, from a tree where fix.diff is applied: cd /usr/src/sys/netgraph7/bluetooth/hci && make # -> ng_hci.ko (objdir), rc=0
Logs: build_hci.log, build_result.txt, env.txt.
DF-0542 β VERDICT
Verdict: NOT REPRODUCED at runtime (source-CONFIRMED real OOB-read; the
vulnerable code is dead/opt-in on the default guest).
Impact: none on a default install (latent; conditional on
netgraph7_bluetooth + BT hardware).
Confidence: likely (bug confirmed by source trace; not runtime-observed
because the module is not compiled/shipped/loadable on the default guest).
Mechanism (source-confirmed)
ng_hci_process_event()(ng_hci_evnt.c:86) pulls the 3-byte HCI event header, logshdr->length(:100) but does not validate it against the mbuf length, then dispatches onhdr->event(:106).NG_HCI_EVENT_INQUIRY_RESULTβinquiry_result()(:127,:370).inquiry_result()pulls onlysizeof(ng_hci_inquiry_result_ep)= 1 byte (num_responses) (:380),m_adjs it (:385), then loopsfor (; ep->num_responses > 0; ...)(:387) with no length guard.- Each iteration consumes 14 bytes:
m_copydata(sizeof(bdaddr)=6)+m_adj(6)(:389-390),*mtod+m_adj(1)(:408-409),m_adj(1)(:412),*mtod+m_adj(1)(:414-415),m_adj(NG_HCI_CLASS_SIZE=3)(:418),m_copydata(sizeof(clock_offset)=2)(:421). (NG_HCI_CLASS_SIZE=3 and bdaddr_t=6 confirmed inng_hci.h:78,:73,:375.) - When
num_responsesexceeds the data in the chain,m_adjdrains the chain to nothing; the nextm_copydata()walksm = m->m_nextto NULL and hitsKASSERT(m != NULL, "%s: length > size of mbuf chain")atsys/kern/uipc_mbuf.c:1687β panic (INVARIANTS/GENERIC); non-INVARIANTS β NULLm->m_lenderef β page-fault panic. The*mtodreads (:408,:414) additionally read stale residue into the neighbor cache (limited leak).
The bug is real and not a false positive.
Why NOT reproduced at runtime
netgraph7 bluetooth is opt-in and absent from the default system:
- sys/conf/files gates ng_hci_evnt.c on optional netgraph7_bluetooth;
X86_64_GENERIC has no netgraph7 option β not in the kernel.
- No bluetooth/netgraph7 .ko ships in /boot/kernel/.
- Standalone make in sys/netgraph7/bluetooth/hci builds ng_hci.ko
(rc=0), but kldload fails: "depends on ng_bluetooth - not available" β
the whole netgraph7 BT framework is missing.
- Even fully loaded, the only way to reach inquiry_result() is a real HCI
INQUIRY_RESULT event from a Bluetooth controller via ng_ubt/ng_h4; there
is no userspace injection path on this guest.
β The code path is dead/unreachable at runtime on this guest AND no harness can exercise it without building the netgraph7 BT stack (root) and attaching BT hardware β a valid hard blocker for live reproduction. (Validated the bug at the source/object level and validated the fix compiles instead.)
Fix-validation (fix.diff)
fix.diff adds a per-response size and bounds the loop by remaining
event->m_pkthdr.len:
#define NG_HCI_INQUIRY_RESULT_SIZE \
(sizeof(bdaddr_t) + 3 + NG_HCI_CLASS_SIZE + sizeof(u_int16_t))
for (; ep->num_responses > 0 &&
event->m_pkthdr.len >= NG_HCI_INQUIRY_RESULT_SIZE;
ep->num_responses --) { ... }
- git-apply clean; compiles (patched
ng_hci.kobuild rc=0,build_hci.log). fix_status: not_testable (runtime). The module is not loadable on the default guest and event injection requires BT hardware; the fix was validated to apply + compile and traced to close the over-read.
PoC changes
Created the evidence pack from scratch (empty on handoff): README.md, this
VERDICT.md, env.txt, build_result.txt, build_hci.log, fix.diff,
manifest.json.
Recommended fix (summary)
In sys/netgraph7/bluetooth/hci/ng_hci_evnt.c, bound inquiry_result()'s
loop by the remaining mbuf length (stop when event->m_pkthdr.len <
NG_HCI_INQUIRY_RESULT_SIZE, 14 bytes/response). (Matches the finding's
"clamp num_responses to m_pkthdr.len/14" proposal.)
Fix verification
not_testablenot_testable: compile validated, no runtime trigger.
git apply --check OK + module build rc=0.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
Source trace + compile validation. No live trigger.
PoC changes
fix.diff + VERDICT.md + manifest.json per finding.
Verified recommended fix
See individual fix.diff per finding.
Verdict
Source-confirmed real but not runtime-reproduced. See individual evidence packs.
No comments yet.