[PATCH] nfc: llcp: drop socket reference on undelivered UI frames

From: Noah Weinberger

Date: Sun Nov 08 2026 - 17:26:02 EST


Hello,

nfc_llcp_recv_ui() looks up the destination socket with
nfc_llcp_sock_get(), which takes a reference on the match. When the
socket exists but is not SOCK_DGRAM, the function returns at the type
check without calling nfc_llcp_sock_put():

llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
return;

A remote LLCP peer can arrange for non-DGRAM sockets whose (ssap, dsap)
match this lookup (accepted stream sockets take dsap from the peer's
CONNECT ssap; connections by service name use dsap = LLCP_SAP_SDP) and
then send any number of UI PDUs addressed to the socket's ssap. Each
frame leaks one socket reference: the socket is pinned forever, even
after close, which is an unbounded kernel memory leak from an RF peer
(memory-exhaustion DoS, rate-limited only by the LLCP link).

All sibling handlers (hdlc, disc, cc, dm) pair the put on every path;
recv_ui is the exception. The patch below puts the reference on all
paths and only delivers to connectionless sockets.

Affected version/commit: mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07), net/nfc/llcp_core.c:902-936 (nfc_llcp_recv_ui), llcp_core.c:218-251 (nfc_llcp_sock_get/put).
Longstanding code.

This was found during an assisted (AI-supported) static source review of
mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07); every code path cited below was re-verified by the
reporter against that tree. The patch below has NOT been compiled and has
NOT been runtime tested, and no reproducer was produced - the triggering
conditions are described above instead. Please treat this as a report for
verification rather than a battle-tested fix.

Reported-by: Noah Weinberger <noahweinberger2003@xxxxxxxxx>
---
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 74bf817..78f80c4 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -917,9 +917,15 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,

/* We're looking for a bound socket, not a client one */
llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
- if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
+ if (llcp_sock == NULL)
return;

+ /* Only connectionless sockets receive UI frames, but the
+ * reference taken by nfc_llcp_sock_get() must be dropped on
+ * every other path as well.
+ */
+ if (llcp_sock->sk.sk_type == SOCK_DGRAM) {
+
/* There is no sequence with UI frames */
skb_pull(skb, LLCP_HEADER_SIZE);
if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
@@ -932,5 +938,6 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
pr_err("Receive queue is full\n");
}

+ }
nfc_llcp_sock_put(llcp_sock);
}