[PATCH net v2] nfc: llcp: fix integer underflow and missing bounds checks in TLV parsing
Doruk Tan Ozturk <[email protected]> Tue, 26 May 2026 22:32:26 +0200
| Newsgroups | dev.linux.lists.oe-linux-nfc,org.kernel.vger.netdev,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
Multiple out-of-bounds read vulnerabilities exist in the NFC LLCP TLV
parsers:
1. In nfc_llcp_recv_snl(), when an SDREQ TLV has length == 0,
service_name_len = length - 1 underflows to SIZE_MAX (size_t is
unsigned). The subsequent strncmp() and nfc_llcp_sock_from_sn()
calls then read unbounded kernel heap memory.
2. All LLCP TLV parsing loops (nfc_llcp_recv_snl, nfc_llcp_connect_sn,
nfc_llcp_parse_gb_tlv, nfc_llcp_parse_connection_tlv) read tlv[0]
and tlv[1] without first verifying that at least 2 bytes remain in
the buffer.
A nearby malicious NFC device can trigger these without authentication --
LLCP link activation happens automatically after NFC-DEP.
Fix by adding a minimum length check before the subtraction in the
SDREQ case, and adding bounds validation at the top of each TLV loop
iteration.
Found by 0sec (https://0sec.ai) using automated source analysis.
Fixes: 19cfe5843e86 ("NFC: Initial SNL support")
Cc: [email protected]
Reported-by: Doruk Tan Ozturk <[email protected]>
Closes: https://lore.kernel.org/netdev/[email protected]/
Signed-off-by: Doruk Tan Ozturk <[email protected]>
---
v2:
- mark as net fix
- add Fixes: tag and Cc: stable
- add Closes: tag
Link: https://lore.kernel.org/netdev/[email protected]/
net/nfc/llcp_core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index XXXXXXX..YYYYYYY 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1302,6 +1302,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
while (offset < tlv_len) {
+ if (offset + 2 > tlv_len)
+ break;
+
type = tlv[0];
length = tlv[1];
@@ -1307,6 +1310,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
switch (type) {
case LLCP_TLV_SDREQ:
+ if (length < 1)
+ break;
+
tid = tlv[2];
service_name = (char *) &tlv[3];
service_name_len = length - 1;
--
2.45.0