β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5762

RFCOMM_CHANNEL() mask 0x2f is missing bit 4: inbound server channels 16..30 remap onto 0..14 β€” incoming connections match the WRONG listening service and channel 16 is permanently unreachable

Summary

A 6-bit DLCI yields a 5-bit channel so the mask must be 0x1f (the inverse MKDLCI correctly uses it): with 0x2f, channels >=16 wrap - PN for channel 17 matched the CHANNEL-1 listener and the accepted DLC was stamped bt_channel=1 (cross-service connection/data mis-attribution on one multiplexer: a trust-boundary crossing - serial-port client connected to the audio service socket); channel 16 maps to 0 and is unreachable. RFCOMM_CHANNEL_MAX=30 so 16..30 are legal listener configurations. one-character mask fix validated (correct listener + bt_channel=17).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5762 Β· 13 files
FileTypeDescriptionSize
harness.c β€” 16.4 KB view raw
l2cap_stub.c β€” 3.3 KB view raw
Makefile β€” 511 B ↓ download
build.sh β€” 99 B view raw
run.sh β€” 152 B view raw
setup.log β€” 393 B view raw
build.log β€” 4.5 KB view raw
run.log β€” 8.1 KB view raw
run.fix.log β€” 60.2 KB view raw
fix.diff β€” 433 B view raw
env.txt β€” 312 B view raw
VERDICT.md β€” 3.0 KB ↓ raw
verdict.json β€” 4.0 KB view raw
VERDICT.md
↓ download raw

DF-5762 VERDICT

Status: reproduced (rig, production code verbatim; deterministic) Impact: dos / cross-service mis-attribution β€” inbound RFCOMM connections for server channels 17..30 are matched to the WRONG listening socket (channel N&0x2f) and channel 16 is permanently unreachable.

Root cause

sys/netbt/rfcomm.h:217: #define RFCOMM_CHANNEL(dlci) (((dlci) >> 1) & 0x2f) β€” 0x2f (0b101111) lacks bit 4, so 5-bit channel values β‰₯ 16 wrap: 16&0x2f=0, 17&0x2f=1 … 30&0x2f=14. The inverse macro RFCOMM_MKDLCI (rfcomm.h:224) correctly uses & 0x1f, so only inbound extraction is broken. RFCOMM_CHANNEL_MAX is 30 (rfcomm.h:84), i.e. channels 16..30 are fully legal β€” rfcomm_listen() accepts them (rfcomm_upper.c:320-323) and local rfcomm_connect() builds correct DLCIs for them; only the incoming path is broken.

Sinks: rfcomm_dlc_newconn() (sys/netbt/rfcomm_dlc.c:88 chan = RFCOMM_CHANNEL(dlci), matching at :100/:107, stamping at :133 new->rd_laddr.bt_channel = chan).

Reproduced behaviour (run.log)

Same rig as DF-5760 (verbatim production RFCOMM sources + stub L2CAP):

  • PN command, DLCI 34 (channel 17, correct initiator direction): MASK-CHECK: DLCI 34 (channel 17) planted, rd_laddr.bt_channel = 1 and the newconn callback tag shows the channel-1 listener matched ("listener upper 0x1"). In production the channel-17 client would be connected to the channel-1 server socket, with data flowing to the wrong service.
  • PN command, DLCI 33 (channel 16): DM β€” 16 & 0x2f = 0 matches no listener ("channel 16 NOT planted"), channel 16 is unreachable.
  • The DF-5760 sweep lines show every channel β‰₯ 17 matching a shifted-down listener (dlci 0x3d = ch30 β†’ "listener upper 0xe").

Fix validation (run.fix.log)

fix.diff: 0x2f β†’ 0x1f in rfcomm.h, applied in the guest /usr/src, rig rebuilt from the patched verbatim sources:

  • MASK-BUG: RFCOMM_CHANNEL(33)=16 ... (35)=17 ... (61)=30 (correct extraction).
  • newconn #2 (listener upper 0x11) β€” the channel-17 listener now matches; MASK-CHECK: ... rd_laddr.bt_channel = 17.
  • Channel-16 DM gone (not re-tested standalone β€” channel 16 poison is direction-rejected by the DF-5760 fix in the same patched build; the mask arithmetic itself is printed and correct).

Threat

Remote peer of an established RFCOMM session connecting to services listening on channels 16..30: data mis-delivery between unrelated local services (e.g. audio service socket receiving the serial-port service's connection β€” a trust-boundary crossing on one multiplexer) and total unreachability of channel 16. No memory corruption. Severity Low.

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

fixed
baseline reproduced→ patch + rebuild →patched clean

One-line mask fix (0x2f -> 0x1f in sys/netbt/rfcomm.h) applied to the guest's /usr/src copy, rig rebuilt from the patched verbatim sources: RFCOMM_CHANNEL(33)=16 / (35)=17 / (61)=30 (printed), the channel-17 PN now matches the channel-17 listener ('newconn #2 (listener upper 0x11)') and the DLC is stamped rd_laddr.bt_channel = 17. Guest alive.

run.fix.log: 'MASK-BUG: RFCOMM_CHANNEL(33)=16 (16 & 0x2f), RFCOMM_CHANNEL(35)=17 ...' (post-fix values), '[upper] newconn #2 (listener upper 0x11)', 'MASK-CHECK: DLCI 34 (channel 17) planted, rd_laddr.bt_channel = 17 (17 == correct match)'.
↓ fix.diffper-fix-DF-5762

Confirmed kernel references

Detail

Exploit chain

Remote peer of an established RFCOMM session connects to server channel N in 17..30 -> rfcomm_dlc_newconn matches the listener on channel (N & 0x2f) -> the wrong server socket accepts the connection and receives the data (cross-service trust-boundary crossing on one multiplexer). Channel 16 services are unreachable. No memory corruption.

Evidence (decisive lines)

run.log: 'MASK-CHECK: DLCI 34 (channel 17) planted, rd_laddr.bt_channel = 1' + the matching 'newconn #N (listener upper 0x1)' tag; 'channel 16 NOT planted' (DM, chan 0); 'MASK-BUG: RFCOMM_CHANNEL(33)=0 (16 & 0x2f), RFCOMM_CHANNEL(35)=1 (17 & 0x2f), RFCOMM_CHANNEL(61)=14 (30 & 0x2f)'. run.fix.log: 'MASK-BUG: RFCOMM_CHANNEL(33)=16 ... (35)=17 ... (61)=30', 'newconn #2 (listener upper 0x11)' (the correct channel-17 listener), 'MASK-CHECK: ... rd_laddr.bt_channel = 17'.

PoC changes

Found BY the DF-5760 rig: the channel sweep planted ch17..30 against shifted listeners (dlci 0x23/ch17 -> 'listener upper 0x1', ..., dlci 0x3d/ch30 -> 'listener upper 0xe') and ch16 drew a DM; a dedicated MASK-CHECK PN (correct-direction DLCI 34) was added to make the mis-attribution explicit. Shares the DF-5760 rig files verbatim (see DF-5760/ for harness.c and stubs).

Verified recommended fix

Change the RFCOMM_CHANNEL mask from 0x2f to 0x1f (one line, see fix.diff).

Verdict

Reproduced with the DF-5760 rig (verbatim production RFCOMM sources): RFCOMM_CHANNEL()'s 0x2f mask lacks bit 4, so inbound DLCIs for server channels 16..30 are remapped (16->0, 17->1 ... 30->14). An incoming PN/SABM for channel 17 (DLCI 34/35) matches and is served by the CHANNEL-1 listener and the accepted DLC is stamped rd_laddr.bt_channel = 1 ('MASK-CHECK: DLCI 34 (channel 17) planted, rd_laddr.bt_channel = 1' in run.log) - in production this mis-delivers one local service's connections/data to another service's socket on the same multiplexer; channel 16 (DLCI 32/33 -> chan 0) can never match a listener and is permanently unreachable (DM). Local listeners/connections use the correct MKDLCI (0x1f) - only the inbound extraction is broken. RFCOMM_CHANNEL_MAX is 30, so channels 16..30 are fully legal configurations.