nfc_llcp_parse_gb_tlv() and nfc_llcp_parse_connection_tlv() iterate over a TLV array supplied by the remote peer. Each iteration reads the type and length from tlv[0]/tlv[1] and then advances by length + 2, but the loop only tests offset < tlv_array_len and never checks that the 2-byte header fits in the remaining space. A truncated trailing entry is enough to read past the buffer on the tlv[1] access. The length byte then makes it worse. offset is a u8, so length + 2 wraps at 256: length == 0xff advances by 0x101 and leaves offset unchanged, so the loop stays live while tlv keeps moving forward. The parse walks well past the buffer, and the next iteration's tlv[0]/tlv[1] read lands out of bounds rather than the walk stopping one entry over. nfc_llcp_parse_gb_tlv() runs on the general bytes of an activated NFC-DEP link. An RF_INTF_ACTIVATED_NTF in listen mode reaches it via nci_ntf_packet() -> nfc_tm_activated() -> nfc_llcp_set_remote_gb(), which copies the peer's general bytes into the 48-byte remote_gb[] field of the heap-allocated struct nfc_llcp_local; the wrapped walk runs off the end of that allocation. nfc_llcp_parse_connection_tlv() is reached the same way from CONNECT/CC PDUs, over tlv data taken from the received skb. Bound each iteration to tlv_array_len before touching the header, and widen offset to u16 so the advance can no longer wrap. Well-formed TLVs are parsed as before; a malformed trailing entry ends the loop. BUG: KASAN: slab-out-of-bounds in nfc_llcp_parse_gb_tlv (net/nfc/llcp_commands.c:204) Read of size 1 by task kworker/u8:3 nfc_llcp_parse_gb_tlv (net/nfc/llcp_commands.c:204) nfc_llcp_set_remote_gb (net/nfc/llcp_core.c:681) nfc_tm_activated (net/nfc/core.c:643 net/nfc/core.c:677) nci_ntf_packet (net/nfc/nci/ntf.c:883 net/nfc/nci/ntf.c:1015) nci_rx_work (net/nfc/nci/core.c:1564) Fixes: d646960f7986 ("NFC: Initial LLCP support") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi --- net/nfc/llcp_commands.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index 291f26facbf3..3f0f8eef9890 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -193,17 +193,21 @@ int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local, const u8 *tlv_array, u16 tlv_array_len) { const u8 *tlv = tlv_array; - u8 type, length, offset = 0; + u8 type, length; + u16 offset = 0; pr_debug("TLV array length %d\n", tlv_array_len); if (local == NULL) return -ENODEV; - while (offset < tlv_array_len) { + while (offset + 2 <= tlv_array_len) { type = tlv[0]; length = tlv[1]; + if (offset + 2 + length > tlv_array_len) + break; + pr_debug("type 0x%x length %d\n", type, length); switch (type) { @@ -243,17 +247,21 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock, const u8 *tlv_array, u16 tlv_array_len) { const u8 *tlv = tlv_array; - u8 type, length, offset = 0; + u8 type, length; + u16 offset = 0; pr_debug("TLV array length %d\n", tlv_array_len); if (sock == NULL) return -ENOTCONN; - while (offset < tlv_array_len) { + while (offset + 2 <= tlv_array_len) { type = tlv[0]; length = tlv[1]; + if (offset + 2 + length > tlv_array_len) + break; + pr_debug("type 0x%x length %d\n", type, length); switch (type) { -- 2.43.0