[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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help