[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