[BUG] nfc: llcp: u16 size truncation in nfc_llcp_allocate_snl() leads to skb_over_panic
flat view
From: Guikun Yang <hidden>
Date: 2026-10-24 15:58:17
Also in:
lkml, oe-linux-nfc
Hi David, nfc_llcp_allocate_snl() computes the SNL skb size in a u16 while taking the TLV payload length as size_t: static struct sk_buff *nfc_llcp_allocate_snl(struct nfc_llcp_local *local, size_t tlv_length) { struct sk_buff *skb; struct nfc_dev *dev; u16 size = 0; ... size += LLCP_HEADER_SIZE; size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE; size += tlv_length; skb = alloc_skb(size, GFP_KERNEL); The addition silently wraps when tlv_length + the constant part exceeds 65535. The caller then writes the full tlv_length bytes into the undersized skb: hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { skb_put_data(skb, sdreq->tlv, sdreq->tlv_len); ... } skb_put() detects the overrun and calls skb_over_panic(), i.e. BUG(). tlv_length originates from a control-plane request and is not bounded anywhere on this path: nfc_genl_llc_sdreq() walks every NFC_SDP_ATTR_URI sub-attribute of the NFC_ATTR_LLC_SDP nest, builds one TLV per URI and accumulates its length: uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]); ... sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len); ... tlvs_len += sdreq->tlv_len; tlvs_len is a size_t, so the accumulation itself is fine; only the conversion to u16 in nfc_llcp_allocate_snl() truncates. Reachability: nfc_llcp_build_sdreq_tlv() caps a single TLV at 254 bytes (uri_len <= U8_MAX - 4 = 251, plus 3 bytes of TLV header). The only limit on the number of TLVs is the netlink message size, which netlink_sendmsg() bounds by sk_sndbuf - 32 (212992 - 32 by default), so a message of roughly 66 KB is accepted. With N SDP attributes carrying 251-byte URIs (256 bytes each on the wire), N = 259 yields tlvs_len = 259 * 254 = 65786 size = 12 + 65786 = 65798, which truncates to 294 alloc_skb(294) succeeds, and the second skb_put_data() already walks past skb->end. NFC_CMD_LLC_SDREQ is GENL_ADMIN_PERM, so this needs CAP_NET_ADMIN in the netns where the request is issued, plus an NFC device with an activated LLCP link (dev->dep_link_up must be set). nfcsim provides such a device from software, with tx_headroom = DIGITAL_MAX_HEADER_LEN (7) and tx_tailroom = DIGITAL_CRC_LEN (2), which is exactly the constant part used above. Suggested fix: declare the accumulator as size_t (or unsigned int) and let alloc_skb() fail cleanly for absurd sizes, e.g. - u16 size = 0; + size_t size = 0; The same u16 pattern exists in nfc_llcp_send_symm() and nfc_llcp_send_dm(), but there the payload is a fixed 0 or 1 bytes, so only nfc_llcp_allocate_snl() is affected by a large tlv_length. Checked against 7.3.0-rc7 and linux-next (2026-10-24); both are unchanged. syzbot has no matching report (its NFC coverage shows no llcp entries), and I found no prior report of this particular overflow on lore/marc. The earlier SNL TLV work (27256cdb290e, and the follow-up "bound the remaining LLCP TLV parsers to their buffers") addresses out-of-bounds reads while parsing received TLV lists and does not touch the size computation here. Best, Guikun Yang