Uninitialized cmd.ident sent in Command Reject: remote 1-byte kernel stack info leak
Summary
l2cap_recv_signal(:70-164): l2cap_cmd_hdr_t cmd declared uninitialized on stack(:70). First test if(m_pkthdr.len<sizeof(cmd)=4) goto reject(:76-77) fires BEFORE m_copydata populates cmd(m_copydata at :79). reject: label calls l2cap_send_command_rej(link,cmd.ident,L2CAP_REJ_NOT_UNDERSTOOD)(:164) embedding uninitialized cmd.ident into L2CAP_COMMAND_REJ response transmitted over BT. Remote unauth peer: L2CAP length=1-3, payload that short -> dispatcher jumps to reject without writing cmd -> 1 byte kernel stack leaked per request in ident field. Repeated probing leaks fixed stack offset. Fix: l2cap_cmd_hdr_t cmd={0} or send reject with ident 0 for short packets.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0540 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| build.sh | build-script | reports netbt not compiled | 268 B | view raw |
| run.sh | run-script | probes netbt/BT absence | 478 B | view raw |
| run.log | run-log | reachability probe: netbt not in kernel, no BT HW | 687 B | view raw |
| env.txt | environment | uname, netbt/BT absence, build rule | 353 B | view raw |
| fix.diff | suggested-fix | zero-init l2cap_cmd_hdr_t cmd = { 0 }; applies cleanly | 697 B | view raw |
| VERDICT.md | verdict | full source trace + reachability + fix | 3.0 KB | β raw |
| README.md | readme | evidence pack index | 1.4 KB | β 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-0540 β Uninitialized cmd.ident leaked in L2CAP Command Reject
Verdict: LATENT on this guest (source bug real; not runtime-exercisable).
Fix authored (fix.diff, applies cleanly; not_testable β netbt not built).
Bug
l2cap_recv_signal() (sys/netbt/l2cap_signal.c:67) declares
l2cap_cmd_hdr_t cmd; uninitialized on the stack (:70). If the incoming
L2CAP packet is shorter than a 4-byte command header, m->m_pkthdr.len <
sizeof(cmd) at :76 jumps to reject: at :163 before m_copydata
(:79) populates cmd. l2cap_send_command_rej(link, cmd.ident, β¦) (:164)
then embeds the uninitialized cmd.ident (1 byte of kernel stack) into the
outgoing L2CAP_COMMAND_REJ packet β confirmed sink at l2cap_send_signal:972
(cmd->ident = ident; then hci_acl_send). One byte of kernel stack leaked to
the remote Bluetooth peer per short packet.
Reachability
Latent on this guest. l2cap_signal.c is optional bluetooth
(sys/conf/files:1615); X86_64_GENERIC has no options bluetooth, so
l2cap_recv_signal is not compiled into the running kernel (0 symbols), and
there is no Bluetooth HCI hardware to inject a short L2CAP packet.
Fix
fix.diff: l2cap_cmd_hdr_t cmd = { 0 }; (:70) β guarantees cmd.ident == 0
on the early-reject path. Trivial, minimal, correct. Applies cleanly; not
build-validated (netbt requires options bluetooth + BT hardware).
See VERDICT.md for the full line-cited trace.
DF-0540 β Uninitialized cmd.ident leaked in L2CAP Command Reject: LATENT
Verdict: NOT REPRODUCED on this guest (LATENT). Source bug REAL; fix authored, applies cleanly; not build/runtime-testable here (netbt not compiled into GENERIC, no Bluetooth hardware).
SOURCE TRACE (bug is real)
File: sys/netbt/l2cap_signal.c (the NetBT Bluetooth stack; optional bluetooth)
l2cap_recv_signal(m, link) (:67-167): 70: l2cap_cmd_hdr_t cmd; / UNINITIALISED on stack / 72: for(;;) { 73: if (m->m_pkthdr.len == 0) goto finish; 76: if (m->m_pkthdr.len < sizeof(cmd)) / sizeof = 4 / 77: goto reject; / jumps BEFORE m_copydata / 79: m_copydata(m, 0, sizeof(cmd), &cmd); / only here is cmd written / ... 163:reject: 164: l2cap_send_command_rej(link, cmd.ident, L2CAP_REJ_NOT_UNDERSTOOD);
If an incoming L2CAP signalling packet (CID 0x0001) is shorter than a 4-byte
command header (len = 1..3), control jumps to reject at :163 BEFORE
m_copydata populates cmd. l2cap_send_command_rej then embeds the
UNINITIALISED cmd.ident (1 byte of kernel stack) into the outgoing
L2CAP_COMMAND_REJ packet. The sink is confirmed:
l2cap_send_signal (sys/netbt/l2cap_signal.c:937):
972: cmd->ident = ident; / written into the on-wire L2CAP header /
985: return hci_acl_send(m, link, NULL);
So each short L2CAP packet leaks one byte of kernel stack to the remote peer over Bluetooth. Repeated probing leaks a fixed stack offset. (No write, no corruption β pure 1-byte info leak per request; remote-unauth over BT RF.)
REACHABILITY ON THIS GUEST (latent)
- l2cap_signal.c is
optional bluetooth(sys/conf/files:1615). X86_64_GENERIC has NOoptions bluetooth(grep count = 0), so the entire netbt stack β including l2cap_recv_signal β is NOT compiled into the running kernel (nm count for l2cap_recv_signal = 0). - Even with the option, NetBT's l2cap_recv_signal is fed by a netbt hci_link (real Bluetooth hardware via bt_input.c). QEMU provides no Bluetooth adapter and there is no netbt HCI unit.
- => The vulnerable function is dead code on this guest AND there is no RF path to deliver a short L2CAP packet. A valid "genuinely not reachable on this kernel AND no harness can exercise it" case. The bug is proven at source level.
FIX
fix.diff zero-initialises the declaration: l2cap_cmd_hdr_t cmd = { 0 };
(:70). This guarantees cmd.ident == 0 on the early-goto reject path, so the
Command Reject carries ident 0 instead of a stack byte. Trivial, minimal,
correct. git-apply-able; applies cleanly.
NOT TESTABLE
fix_status = not_testable: netbt is not compiled into the default kernel and there is no Bluetooth hardware on the guest to inject a short L2CAP packet. The diff applies and the fix is correct-by-inspection.
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. l2cap_signal cmd.ident uninitialized on early reject -> 1B stack leak. netbt not in GENERIC, no BT HW.
No comments yet.