Linux Bluetooth L2CAP Security Hardening

Linux Bluetooth L2CAP Security Hardening

Problem

The Linux Bluetooth stack is a full-featured implementation of the Bluetooth protocol suite built into the mainline kernel. At the heart of it sits L2CAP — the Logical Link Control and Adaptation Protocol — which acts as the multiplexing and transport layer for all higher-level Bluetooth protocols. L2CAP provides both connection-oriented and connectionless data channels over a single Bluetooth radio link, carrying RFCOMM (serial port emulation), BNEP (network encapsulation for Bluetooth PAN), HID (keyboards, mice), and A2DP (audio streaming) simultaneously. It handles fragmentation of outbound data into Basic Frame (B-frame) or Enhanced Retransmission Mode (ERTM) packets and reassembles inbound fragments back into Service Data Units (SDUs) before passing them up the stack. Because L2CAP is the point where fragmented wire bytes become kernel-managed reassembly buffers, it is also the point where length validation errors have the most impact on kernel memory safety.

CVE-2026-31512, disclosed in April 2026, is a missing SDU length validation vulnerability in the Linux kernel’s Bluetooth L2CAP implementation located in net/bluetooth/l2cap_core.c. When L2CAP reassembles fragmented Bluetooth packets, it uses the SDU length field carried in the first fragment’s header to pre-allocate a kernel reassembly buffer of exactly that size. The vulnerability arose because the kernel did not cross-validate the declared SDU length against the actual combined payload sizes of subsequent fragments. An attacker could send a crafted first fragment declaring a small SDU length, causing a small buffer to be allocated, and then send a second fragment that caused the reassembly logic to read beyond the end of the allocated buffer. This out-of-bounds (OOB) read does not corrupt kernel memory — it reads it, making the flaw a kernel memory disclosure rather than a code execution primitive. However, the leaked memory region may contain cryptographic key material held in nearby slab allocations, KASLR-defeating kernel pointer values, or credential data from other processes, all of which are useful for chaining into a more severe exploitation path.

The Bluetooth attack surface differs from almost every other category of kernel vulnerability. Exploiting a local privilege escalation requires an account on the machine. Exploiting a network vulnerability requires routing, firewall traversal, or at minimum a connection to an open port. Bluetooth vulnerabilities can be triggered wirelessly from physical proximity — typically 10 to 100 metres for Class 1 and Class 2 devices — with no prior authentication, no open port, and no user interaction required beyond the target system having Bluetooth powered on. A device that is merely connectable (not necessarily discoverable) can still be reached by an attacker who already knows or has scanned its Bluetooth MAC address. This threat model applies to laptops in coffee shops, developer workstations in open-plan offices, and any Linux server deployed in a physically accessible environment such as a retail location, kiosk, or edge compute node.

Bluetooth has a long and well-documented history of serious kernel vulnerabilities. BlueBorne (2017) demonstrated remote code execution without any pairing requirement across multiple operating systems. BIAS (Bluetooth Impersonation AttackS, 2020) showed that the Secure Simple Pairing handshake could be bypassed to impersonate any already-paired device. BrakTooth (2021) uncovered a family of denial-of-service and potential code execution bugs in Bluetooth controller firmware. CVE-2026-31512 continues this pattern, and the Linux Bluetooth subsystem’s track record suggests it will not be the last significant vulnerability in this area. The consistent pattern is not that the Bluetooth protocol itself is broken, but that the complexity of correctly implementing fragmentation, reassembly, length validation, and state machine transitions across dozens of L2CAP channel types creates a persistent supply of subtle memory safety errors.

The open-source nature of the Linux Bluetooth subsystem creates a specific and exploitable intelligence gap. The Bluetooth subsystem is maintained in two upstream trees: git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git for stable fixes and bluetooth-next.git for development work. Fixes flow from the Bluetooth tree into Linus Torvalds’s mainline tree on a pull-request basis, typically during the merge window or as urgent fixes between merge windows. This means security-relevant commits to net/bluetooth/l2cap_core.c are publicly visible in the Bluetooth tree before they appear in mainline, and they are visible in mainline before distribution kernel packages are built and shipped. For CVE-2026-31512, the SDU length bounds-check commit was observable in the Bluetooth tree by anyone watching commit activity on l2cap_core.c. The CVE identifier was not assigned until the fix was already several days old in the tree.

This upstream-first visibility creates a patch-gap window. The timeline is consistent across Bluetooth CVEs: upstream fix committed to bluetooth.git, pulled into mainline, backported to stable-rc, stable release cut, distribution package built, distribution advisory published. The window from upstream commit to Ubuntu LTS or RHEL kernel package availability is typically two to four weeks. During that window, a researcher who monitors the Bluetooth tree can identify the exact file, function, and nature of a fix and probe unpatched systems. Security teams that monitor only distribution advisories are systematically behind this curve. The Bluetooth subsystem also has a significant shadow vulnerability population — commits like “l2cap: fix potential null deref in l2cap_sock_recvmsg” or “bluetooth: validate packet length before processing” that are clearly security-relevant boundary condition fixes but are filed as plain bug fixes and never receive a CVE. Operators who depend solely on CVE tracking miss this entire category. Monitoring https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git/log/ for commits touching net/bluetooth/l2cap_*.c and net/bluetooth/hci_*.c is the only way to maintain awareness of the full fix population.

Target systems: Linux kernel with CONFIG_BT=y — included by default in all standard desktop and server distribution kernels — running Ubuntu 22.04+/24.04+, Debian 12+, Fedora 38+, and RHEL 9+. Any system with Bluetooth hardware present and bluetoothd running is in scope for CVE-2026-31512. Server hardware without Bluetooth controllers is unaffected, but the kernel module may still be loaded if CONFIG_BT=m is set and the module is not explicitly blacklisted.

Threat Model

  1. CVE-2026-31512 — wireless proximity attacker: An attacker within Bluetooth range (10–100 metres) equips a Bluetooth adapter and transmits crafted L2CAP fragmented packets targeting a connectable Linux system. The first fragment carries a small declared SDU length; subsequent fragments contain additional payload bytes. The kernel’s reassembly path in l2cap_core.c reads beyond the end of the allocated slab buffer, disclosing adjacent kernel memory. Depending on allocation timing and what objects neighbor the L2CAP reassembly buffer, the leaked data may include KASLR offsets (kernel pointer values), cryptographic key material from an adjacent TLS session buffer, or PAM credential fragments. No prior pairing is required if the target is connectable. No user interaction is required on the target. The attacker needs only a Bluetooth adapter and a crafted packet sequence, both achievable with commodity hardware and publicly available PoC tooling once a PoC circulates.

  2. Bluetooth device pairing attack — impersonation and channel escalation: An attacker scans for the target’s Bluetooth MAC address during a brief discoverability window or captures it passively from advertising frames. The attacker then spoofs a trusted device MAC (obtained from a previous pairing scan or social engineering) and initiates a pairing request. Using a BIAS-class authentication bypass or by brute-forcing a six-digit PIN on a legacy pairing implementation, the attacker completes pairing and establishes an L2CAP connection. From this authenticated L2CAP channel the attacker can send crafted payloads to exercise any RFCOMM, BNEP, or HID processing code, not just the SDU reassembly path. Authenticated channels are trusted by higher-level protocol handlers and receive less defensive scrutiny in the kernel than unauthenticated connection attempts.

  3. Patch-gap attacker — upstream tree monitoring: An attacker monitors the bluetooth.git log for new commits to net/bluetooth/l2cap_core.c. When the SDU bounds-check commit appears, the attacker reads the diff, reconstructs the pre-condition (fragment count, declared versus actual SDU length), and writes a PoC. The attacker then scans for connectable Linux systems during the two-to-four week window before distribution kernel packages are released. Systems running Ubuntu 24.04 LTS or RHEL 9 without live patching are particularly exposed.

  4. Local attacker using AF_BLUETOOTH socket APIs: An unprivileged local user can create an AF_BLUETOOTH socket — Bluetooth socket creation is not restricted to root on standard distributions. The attacker sends crafted L2CAP payloads through a BTPROTO_L2CAP socket, exercising the kernel’s L2CAP parsing code via syscall rather than over the air, with no physical Bluetooth hardware required. Several historical Bluetooth CVEs were exploitable via local sockets before being demonstrated wirelessly.

Any of these adversaries who triggers the CVE-2026-31512 OOB read obtains leaked kernel memory. In isolation this is a medium-severity event — no code executes, no files are modified. The blast radius expands when the leaked data contains KASLR offsets enabling a follow-on memory-corruption exploit, or key material enabling service impersonation. The most constrained blast radius is achieved by ensuring Bluetooth is disabled where unused, the kernel is patched, and AF_BLUETOOTH socket access is restricted.

Configuration / Implementation

Disabling Bluetooth When Not Needed

The strongest mitigation for all Bluetooth kernel vulnerabilities is eliminating the attack surface entirely. On any server, CI worker, or container host that does not use Bluetooth peripherals, Bluetooth should be disabled at multiple layers.

Block the radio immediately using rfkill:

# Block all Bluetooth radio interfaces
rfkill block bluetooth

# Verify the block is in effect
rfkill list bluetooth
# Expected output:
# 0: hci0: Bluetooth
#         Soft blocked: yes
#         Hard blocked: no

Disable the bluetoothd service so it does not restart on reboot:

systemctl disable --now bluetooth.service

For systems where Bluetooth should never be used, blacklist the kernel modules to prevent them from loading even if the service is re-enabled:

# Blacklist Bluetooth kernel modules
cat >> /etc/modprobe.d/blacklist-bluetooth.conf << 'EOF'
blacklist bluetooth
blacklist btusb
blacklist btrtl
blacklist btbcm
blacklist btintel
blacklist btmtk
EOF

# Rebuild the initramfs so the blacklist takes effect at early boot
update-initramfs -u     # Debian/Ubuntu
# or
dracut --force          # RHEL/Fedora

Verify that no Bluetooth interfaces are present after a reboot:

hciconfig -a
# Expected: no output (no Bluetooth controllers registered)

lsmod | grep -E '^bt|^bluetooth'
# Expected: no output if blacklist is effective

If the system has a hardware Bluetooth controller that is soldered rather than USB-attached, the rfkill block approach is more reliable than blacklisting btusb because the hardware enumeration path differs. Use both.

Reducing Bluetooth Discoverability and Connectability

On systems where Bluetooth must remain enabled (developer workstations, IoT gateways with Bluetooth peripherals), reduce the attack surface by disabling discoverability and page scan:

# Disable both inquiry scan (discoverable) and page scan (connectable from unknown devices)
hciconfig hci0 noscan

# Verify the flags are cleared
hciconfig hci0
# The output should NOT contain INQUIRY_SCAN or PAGE_SCAN
# Expected flags: UP RUNNING

For systems managed through the BlueZ management interface, use btmgmt:

# Power off Bluetooth via btmgmt (for managed Bluetooth stacks)
btmgmt power off

# To keep Bluetooth on but non-discoverable and non-connectable:
btmgmt discov off
btmgmt connectable off

# Verify current settings
btmgmt info

To persist the noscan setting across reboots, create a systemd service that applies it after bluetooth.service starts:

cat > /etc/systemd/system/bluetooth-noscan.service << 'EOF'
[Unit]
Description=Disable Bluetooth inquiry and page scan
After=bluetooth.service
Requires=bluetooth.service

[Service]
Type=oneshot
ExecStart=/usr/bin/hciconfig hci0 noscan
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now bluetooth-noscan.service

Kernel Patching for CVE-2026-31512

Check the running kernel version and determine patch status:

# Check current kernel version
uname -r

# On Ubuntu/Debian, check if the patched kernel package is available
apt-cache policy linux-image-$(uname -r)

# Apply the patched kernel
apt-get update && apt-get install --only-upgrade linux-image-generic

# On RHEL/Fedora
dnf check-update kernel && dnf upgrade kernel

# On systems with kpatch live patching, query for Bluetooth patches
kpatch list
# Look for a patch entry referencing l2cap or CVE-2026-31512

For systems that cannot tolerate a reboot, check whether a kpatch or livepatch module for CVE-2026-31512 is available through your distribution’s live patching subscription. Systems with Bluetooth physically disabled via rfkill and module blacklisting are not exploitable via the wireless vector regardless of kernel patch status, though the local AF_BLUETOOTH socket vector remains until the kernel is updated.

Confirm the fix is present by checking the kernel changelog for the installed package: apt changelog linux-image-$(uname -r) | grep -i bluetooth or rpm -q --changelog kernel | grep -i l2cap.

Restricting AF_BLUETOOTH Socket Access

Unprivileged AF_BLUETOOTH socket creation should be blocked for any process that does not need Bluetooth. The most portable mechanism is a seccomp profile that rejects socket(AF_BLUETOOTH, ...) calls. AF_BLUETOOTH has the numeric value 31 on Linux.

For a standalone process, the seccomp filter in JSON (libseccomp format) looks like:

{
  "defaultAction": "SCMP_ACT_ALLOW",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_AARCH64"],
  "syscalls": [
    {
      "names": ["socket"],
      "action": "SCMP_ACT_ERRNO",
      "errnoRet": 1,
      "args": [
        {
          "index": 0,
          "value": 31,
          "op": "SCMP_CMP_EQ"
        }
      ]
    }
  ]
}

This allows all other socket() calls (TCP, UDP, Unix domain sockets) while returning EPERM when the first argument equals AF_BLUETOOTH (31).

For Kubernetes workloads, apply a seccomp profile via the pod’s securityContext. Save the above JSON to a node-local path (e.g., /var/lib/kubelet/seccomp/bluetooth-deny.json) and reference it in the pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: hardened-workload
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: bluetooth-deny.json
  containers:
    - name: app
      image: myapp:latest
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]

Additionally, ensure that pods do not hold CAP_NET_RAW, which is required for raw Bluetooth socket operations. Drop it explicitly even in workloads that do not use Bluetooth, since it provides no benefit to most application containers:

securityContext:
  capabilities:
    drop:
      - NET_RAW
      - NET_ADMIN

Monitoring the Bluetooth Kernel Tree

Set up an automated monitoring script that watches the Bluetooth upstream tree for commits to security-sensitive files and alerts when new commits appear:

#!/usr/bin/env bash
# /usr/local/bin/bluetooth-tree-monitor.sh
# Clone the Bluetooth tree once: git clone \
#   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git \
#   /opt/bluetooth-tree

set -euo pipefail

TREE_PATH="/opt/bluetooth-tree"
ALERT_EMAIL="security-team@example.com"
WATCH_PATHS="net/bluetooth/l2cap_core.c net/bluetooth/l2cap_sock.c net/bluetooth/hci_conn.c net/bluetooth/smp.c net/bluetooth/hci_event.c"
DAYS_BACK=7

# Fetch latest commits
git -C "${TREE_PATH}" fetch origin --quiet

# Check for new commits in monitored files
NEW_COMMITS=$(git -C "${TREE_PATH}" log \
  --oneline \
  "origin/master" \
  --since="${DAYS_BACK} days ago" \
  -- ${WATCH_PATHS})

if [[ -n "${NEW_COMMITS}" ]]; then
  echo "Bluetooth kernel tree: new commits in security-sensitive paths (last ${DAYS_BACK} days):"
  echo "${NEW_COMMITS}"
  # Send alert
  echo "${NEW_COMMITS}" | mail -s "[ALERT] Bluetooth kernel tree changes" "${ALERT_EMAIL}"
fi

Schedule this with a weekly cron job:

0 8 * * 1 /usr/local/bin/bluetooth-tree-monitor.sh >> /var/log/bluetooth-monitor.log 2>&1

Cross-reference new commits against NVD (https://nvd.nist.gov/vuln/search, search “bluetooth l2cap”), the oss-security@openwall.com archive, and distribution security trackers (https://ubuntu.com/security/cves, https://access.redhat.com/security/security-updates).

Container and Kubernetes Hardening

Containers running on a Bluetooth-enabled Linux host share the host kernel, meaning a container process exploiting CVE-2026-31512 via an AF_BLUETOOTH socket reaches the same vulnerable kernel code path as a local user. Kubernetes hardening for Bluetooth:

DaemonSets that legitimately manage Bluetooth devices (e.g., a BlueZ bridge DaemonSet for an IoT gateway) should be placed in a dedicated namespace with a permissive security profile, while all other workloads use the restrictive bluetooth-deny seccomp profile. This limits the blast radius if a non-Bluetooth workload is compromised and attempts to use Bluetooth sockets as a secondary exploitation path.

Setting hostNetwork: false prevents container processes from accessing the host’s PF_BLUETOOTH protocol family through the host network namespace.

Expected Behaviour

Signal Bluetooth enabled, unpatched kernel Bluetooth disabled / patched + hardened
Wireless L2CAP SDU OOB read attempt (CVE-2026-31512) OOB read succeeds; attacker receives leaked kernel memory bytes via crafted fragment sequence response Attack surface absent (rfkill blocked) or vulnerability absent (patched kernel); no memory disclosure occurs
Bluetooth discoverability scan from adjacent device Device appears in scan results; attacker learns Bluetooth MAC address and device name noscan mode: device does not appear in inquiry scan; attacker cannot enumerate the target without prior MAC knowledge
AF_BLUETOOTH socket creation by unprivileged local user Socket opens successfully; user can send crafted L2CAP payloads to kernel Seccomp profile returns EPERM; socket(AF_BLUETOOTH, ...) is blocked; process cannot reach kernel Bluetooth code
Patch detection via bluetooth.git tree monitoring Fix commit visible in bluetooth.git before distribution advisory; attacker can read diff and understand exploit preconditions Security team receives monitoring alert on the same commit; begins emergency patching or confirms rfkill mitigation is in place
Server with no Bluetooth hardware hciconfig -a returns no output; no radio present; CONFIG_BT=m module may still be loadable Module blacklisted in /etc/modprobe.d/; lsmod confirms no bluetooth module loaded; zero attack surface regardless of kernel patch status

Trade-offs

Aspect Benefit Cost Mitigation
Disabling Bluetooth entirely (rfkill block + service disable) Eliminates the entire Bluetooth attack surface, including all future undiscovered L2CAP vulnerabilities Breaks all Bluetooth peripherals: keyboards, mice, headsets, and any Bluetooth-based IPC channels Apply only to servers and systems with no Bluetooth use cases; document hardware inventory to distinguish Bluetooth-dependent systems before applying policy
hciconfig hci0 noscan (noscan mode) Removes discoverability and inhibits connection from devices without a known MAC; reduces wireless attack surface without disabling Bluetooth entirely Breaks Bluetooth pairing workflows — new devices cannot discover or connect to the system; existing paired devices can still reconnect if page scan is separately re-enabled Accept the pairing limitation on production systems; use a temporary whitelist or scripted re-enable for the duration of legitimate pairing operations
AF_BLUETOOTH seccomp block for all non-Bluetooth processes Prevents local exploitation of current and future Bluetooth socket vulnerabilities by containerised or sandboxed processes Breaks any process that legitimately uses BlueZ userspace APIs — including bluetoothd itself, PulseAudio Bluetooth backend, and developer tooling like gatttool Apply the seccomp block only to workloads that have no Bluetooth usage; explicitly exclude bluetoothd and audio daemons from the seccomp policy scope
Delayed kernel patching (Bluetooth-dependent system requires uptime) Avoids unplanned reboots and maintains service availability System remains exposed to CVE-2026-31512 OOB read during the patch gap window Apply rfkill and noscan mitigations immediately to shrink the wireless attack surface; schedule a maintenance reboot at the earliest acceptable window; evaluate kpatch/livepatch availability for the specific CVE

Failure Modes

Failure Symptom Detection Recovery
rfkill block bluetooth state not persisted across kernel update After kernel upgrade and reboot, rfkill list shows Bluetooth as unblocked; Bluetooth service starts automatically and the system is re-exposed to wireless attack Monitor rfkill list bluetooth in a post-boot systemd unit or Prometheus node exporter custom metric; alert on Soft blocked: no when policy requires blocking Add rfkill block bluetooth to a rc.local-equivalent systemd oneshot service with After=network.target; alternatively manage rfkill state through the bluetooth.service drop-in to ensure it is blocked at service start
Seccomp AF_BLUETOOTH block breaks Bluetooth HID input devices in Kubernetes DaemonSet Pod that manages Bluetooth keyboard or barcode scanner fails with EPERM on socket(AF_BLUETOOTH, ...) at startup; input devices stop responding Kubernetes pod logs show socket: Operation not permitted at BlueZ initialisation; input device health check fails Create a separate seccomp profile that permits AF_BLUETOOTH for the specific DaemonSet managing Bluetooth devices; apply the restrictive profile to all other workloads; use Kubernetes namespace-scoped PodSecurity policy to manage the scope
bluetooth.service disabled breaks audio routing on desktop PulseAudio or PipeWire cannot connect to the BlueZ D-Bus interface; Bluetooth audio devices (headphones, speakers) stop functioning; PipeWire logs show Failed to connect to bluetoothd User reports audio device not visible in audio settings; pactl list sinks shows no Bluetooth sink; journalctl -u pipewire shows bluetoothd connection errors Re-enable bluetooth.service (systemctl enable --now bluetooth.service); apply noscan and module parameter hardening instead of full service disable for Bluetooth-dependent desktop systems
CVE-2026-31512 OOB read exploited before patch is applied No immediate crash or visible symptom — OOB reads are typically silent; potential downstream indicators include unexpected process crashes (if leaked pointers are used in follow-on exploit), anomalous kernel memory reads in perf trace, or KASLR bypass followed by a second-stage privilege escalation attempt Enable kernel KASAN (Kernel Address Sanitizer) on non-production systems to detect OOB reads in testing; monitor for anomalous kernel oops messages; correlate Bluetooth adapter activity logs from nearby infrastructure with any system instability Immediately apply rfkill block to remove the wireless attack surface; prioritise emergency kernel patching; audit for signs of follow-on exploitation (unexpected setuid executions, new SUID binaries, unexplained privilege escalation events in audit logs)