* [PATCH net v2 0/1] net: nfc: Fix use-after-free in general bytes retrieval
@ 2026-09-01 14:47 Ren Wei
2026-09-01 14:47 ` [PATCH net v2 1/1] net: nfc: fix use-after-free in nfc_get_local_general_bytes Ren Wei
0 siblings, 1 reply; 3+ messages in thread
From: Ren Wei @ 2026-09-01 14:47 UTC (permalink / raw)
To: oe-linux-nfc, netdev, horms
Cc: david, davem, edumazet, kuba, pabeni, error27, kees, pengpeng,
raoxu, rosenp, dddddd, joe, ian.ray, kuniyu, linma, vega,
rakukuip, weir
From: Luxiao Xu <rakukuip@gmail.com>
Hi,
This patch fixes a use-after-free (UAF) bug in nfc_get_local_general_bytes()
where the local structure could be freed prematurely by nfc_llcp_local_put()
before the returned pointer was accessed by callers.
The fix refactors nfc_llcp_general_bytes() and nfc_get_local_general_bytes()
to accept a caller-provided destination buffer and copies the general bytes
safely before decrementing the reference count.
Changes in v2:
- Fix GCC -Waddress compiler warning in nfc_llcp_general_bytes() by removing
the redundant nullness check on 'local->gb' array.
- Fix line wrap indentation and column width across modified driver files to
conform to the 80-column limit.
v1 Link: https://lore.kernel.org/netdev/cover.1786029423.git.rakukuip@gmail.com/
Thanks,
Ren
Luxiao Xu
Luxiao Xu (1):
net: nfc: fix use-after-free in nfc_get_local_general_bytes
drivers/nfc/microread/microread.c | 6 +++---
drivers/nfc/pn533/pn533.c | 14 ++++++++------
drivers/nfc/pn533/pn533.h | 2 +-
drivers/nfc/pn544/pn544.c | 7 +++----
drivers/nfc/st21nfca/core.c | 8 ++++----
include/net/nfc/hci.h | 2 +-
include/net/nfc/nfc.h | 3 ++-
net/nfc/core.c | 15 +++++++--------
net/nfc/digital_dep.c | 8 ++++----
net/nfc/llcp_core.c | 17 +++++++++++++----
net/nfc/nci/core.c | 16 +++++++---------
net/nfc/nfc.h | 3 ++-
12 files changed, 55 insertions(+), 46 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net v2 1/1] net: nfc: fix use-after-free in nfc_get_local_general_bytes
2026-09-01 14:47 [PATCH net v2 0/1] net: nfc: Fix use-after-free in general bytes retrieval Ren Wei
@ 2026-09-01 14:47 ` Ren Wei
2026-09-05 15:29 ` Simon Horman
0 siblings, 1 reply; 3+ messages in thread
From: Ren Wei @ 2026-09-01 14:47 UTC (permalink / raw)
To: oe-linux-nfc, netdev, horms
Cc: david, davem, edumazet, kuba, pabeni, error27, kees, pengpeng,
raoxu, rosenp, dddddd, joe, ian.ray, kuniyu, linma, vega,
rakukuip, weir
From: Luxiao Xu <rakukuip@gmail.com>
Commit 6709d4b7bc2e ("net: nfc: Fix use-after-free caused by
nfc_llcp_find_local") attempted to fix a use-after-free (UAF) issue by
invoking nfc_llcp_local_put(local) after accessing local->gb. However,
if the reference count drops to zero, local is freed immediately,
leading to a use-after-free when callers access the returned pointer.
Alternative approaches using dynamic allocation (e.g. kmemdup) introduced
memory leaks because callers consistently treat the returned pointer as
borrowed memory.
Fix this properly by refactoring nfc_llcp_general_bytes() and
nfc_get_local_general_bytes() to accept a caller-provided output buffer
(out_gb) and its maximum length (gb_max_len). The general bytes are
safely copied into out_gb before calling nfc_llcp_local_put(local),
ensuring safe lifetime management without ownership transfer complications.
Update all callers across drivers (microread, pn533, pn544, st21nfca,
digital_dep, and nci) to provide their own destination buffers and pass
them to nfc_get_local_general_bytes().
Fixes: 6709d4b7bc2e ("net: nfc: Fix use-after-free caused by nfc_llcp_find_local")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Ren Wei <weir@nebusec.ai>
---
drivers/nfc/microread/microread.c | 6 +++---
drivers/nfc/pn533/pn533.c | 14 ++++++++------
drivers/nfc/pn533/pn533.h | 2 +-
drivers/nfc/pn544/pn544.c | 7 +++----
drivers/nfc/st21nfca/core.c | 8 ++++----
include/net/nfc/hci.h | 2 +-
include/net/nfc/nfc.h | 3 ++-
net/nfc/core.c | 15 +++++++--------
net/nfc/digital_dep.c | 8 ++++----
net/nfc/llcp_core.c | 17 +++++++++++++----
net/nfc/nci/core.c | 16 +++++++---------
net/nfc/nfc.h | 3 ++-
12 files changed, 55 insertions(+), 46 deletions(-)
diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c
index dfa2490db545..2bfafa94e83d 100644
--- a/drivers/nfc/microread/microread.c
+++ b/drivers/nfc/microread/microread.c
@@ -251,9 +251,9 @@ static int microread_start_poll(struct nfc_hci_dev *hdev,
param[1] |= (1 << 1);
if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
- hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
- &hdev->gb_len);
- if (hdev->gb == NULL || hdev->gb_len == 0) {
+ nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+ sizeof(hdev->gb), &hdev->gb_len);
+ if (hdev->gb_len == 0) {
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
}
diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c
index f5a6a7c20d5a..b0133e51dce9 100644
--- a/drivers/nfc/pn533/pn533.c
+++ b/drivers/nfc/pn533/pn533.c
@@ -1357,10 +1357,11 @@ static int pn533_poll_dep(struct nfc_dev *nfc_dev)
u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
- if (!dev->gb) {
- dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
-
- if (!dev->gb || !dev->gb_len) {
+ if (!dev->gb_len) {
+ nfc_get_local_general_bytes(nfc_dev, dev->gb,
+ sizeof(dev->gb),
+ &dev->gb_len);
+ if (!dev->gb_len) {
dev->poll_dep = 0;
queue_work(dev->wq, &dev->rf_work);
}
@@ -1658,8 +1659,9 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
}
if (tm_protocols) {
- dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
- if (dev->gb == NULL)
+ nfc_get_local_general_bytes(nfc_dev, dev->gb,
+ sizeof(dev->gb), &dev->gb_len);
+ if (dev->gb_len == 0)
tm_protocols = 0;
}
diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h
index 09e35b8693f5..d3425fcfd557 100644
--- a/drivers/nfc/pn533/pn533.h
+++ b/drivers/nfc/pn533/pn533.h
@@ -166,7 +166,7 @@ struct pn533 {
struct timer_list listen_timer;
int cancel_listen;
- u8 *gb;
+ u8 gb[NFC_MAX_GT_LEN];
size_t gb_len;
u8 tgt_available_prots;
diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c
index 9d0a16ac465e..c4fa70e45c14 100644
--- a/drivers/nfc/pn544/pn544.c
+++ b/drivers/nfc/pn544/pn544.c
@@ -377,10 +377,9 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
return r;
if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
- hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
- &hdev->gb_len);
- pr_debug("generate local bytes %p\n", hdev->gb);
- if (hdev->gb == NULL || hdev->gb_len == 0) {
+ nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+ sizeof(hdev->gb), &hdev->gb_len);
+ if (hdev->gb_len == 0) {
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
}
diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c
index fd39a05c9622..6bfeb8e7ed89 100644
--- a/drivers/nfc/st21nfca/core.c
+++ b/drivers/nfc/st21nfca/core.c
@@ -351,10 +351,10 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
if (r < 0)
return r;
} else {
- hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
- &hdev->gb_len);
-
- if (hdev->gb == NULL || hdev->gb_len == 0) {
+ nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+ sizeof(hdev->gb),
+ &hdev->gb_len);
+ if (hdev->gb_len == 0) {
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
}
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index 756c11084f65..86ed63e5d533 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -144,7 +144,7 @@ struct nfc_hci_dev {
data_exchange_cb_t async_cb;
void *async_cb_context;
- u8 *gb;
+ u8 gb[NFC_MAX_GT_LEN];
size_t gb_len;
unsigned long quirks;
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index c54df042db6b..bcafab5c53e5 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -273,7 +273,8 @@ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp);
int nfc_set_remote_general_bytes(struct nfc_dev *dev,
const u8 *gt, u8 gt_len);
-u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *out_gb,
+ size_t gb_max_len, size_t *gb_len);
int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
u32 result);
diff --git a/net/nfc/core.c b/net/nfc/core.c
index a92a6566e6a0..f521669293f0 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -279,10 +279,10 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
{
- int rc = 0;
- u8 *gb;
- size_t gb_len;
struct nfc_target *target;
+ u8 gb[NFC_MAX_GT_LEN];
+ size_t gb_len = 0;
+ int rc = 0;
pr_debug("dev_name=%s comm %d\n", dev_name(&dev->dev), comm_mode);
@@ -301,7 +301,7 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
goto error;
}
- gb = nfc_llcp_general_bytes(dev, &gb_len);
+ nfc_get_local_general_bytes(dev, gb, sizeof(gb), &gb_len);
if (gb_len > NFC_MAX_GT_LEN) {
rc = -EINVAL;
goto error;
@@ -644,11 +644,10 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
}
EXPORT_SYMBOL(nfc_set_remote_general_bytes);
-u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len)
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *out_gb,
+ size_t gb_max_len, size_t *gb_len)
{
- pr_debug("dev_name=%s\n", dev_name(&dev->dev));
-
- return nfc_llcp_general_bytes(dev, gb_len);
+ return nfc_llcp_general_bytes(dev, out_gb, gb_max_len, gb_len);
}
EXPORT_SYMBOL(nfc_get_local_general_bytes);
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
index 3982fa084737..968547c306a5 100644
--- a/net/nfc/digital_dep.c
+++ b/net/nfc/digital_dep.c
@@ -1490,14 +1490,14 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
struct digital_atr_req *atr_req)
{
struct digital_atr_res *atr_res;
+ u8 gb[NFC_MAX_GT_LEN];
struct sk_buff *skb;
- u8 *gb, payload_bits;
+ u8 payload_bits;
size_t gb_len;
int rc;
- gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len);
- if (!gb)
- gb_len = 0;
+ nfc_get_local_general_bytes(ddev->nfc_dev, gb, sizeof(gb),
+ &gb_len);
skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len);
if (!skb)
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index cac1b5487064..260e4972083e 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -635,23 +635,32 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
return ret;
}
-u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
+u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *out_gb, size_t gb_max_len,
+ size_t *general_bytes_len)
{
struct nfc_llcp_local *local;
+ if (!out_gb || !general_bytes_len)
+ return NULL;
+
local = nfc_llcp_find_local(dev);
- if (local == NULL) {
+ if (!local) {
*general_bytes_len = 0;
return NULL;
}
nfc_llcp_build_gb(local);
- *general_bytes_len = local->gb_len;
+ if (local->gb_len) {
+ *general_bytes_len = min_t(size_t, local->gb_len, gb_max_len);
+ memcpy(out_gb, local->gb, *general_bytes_len);
+ } else {
+ *general_bytes_len = 0;
+ }
nfc_llcp_local_put(local);
- return local->gb;
+ return out_gb;
}
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 5f46c4b5720f..e1893ae41808 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -780,26 +780,24 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
struct nci_set_config_param param;
+ u8 gb[NFC_MAX_GT_LEN];
int rc;
- param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
- if ((param.val == NULL) || (param.len == 0))
+ nfc_get_local_general_bytes(nfc_dev, gb, sizeof(gb),
+ ¶m.len);
+ if (param.len == 0)
return 0;
- if (param.len > NFC_MAX_GT_LEN)
- return -EINVAL;
-
+ param.val = gb;
param.id = NCI_PN_ATR_REQ_GEN_BYTES;
- rc = nci_request(ndev, nci_set_config_req, ¶m,
- msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+ rc = nci_set_config(ndev, param.id, param.len, param.val);
if (rc)
return rc;
param.id = NCI_LN_ATR_RES_GEN_BYTES;
- return nci_request(ndev, nci_set_config_req, ¶m,
- msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+ return nci_set_config(ndev, param.id, param.len, param.val);
}
static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index 0b1e6466f4fb..82c5dfdad10e 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -49,7 +49,8 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
int nfc_llcp_register_device(struct nfc_dev *dev);
void nfc_llcp_unregister_device(struct nfc_dev *dev);
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len);
-u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
+u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *out_gb, size_t gb_max_len,
+ size_t *general_bytes_len);
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
int nfc_llcp_local_put(struct nfc_llcp_local *local);
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net v2 1/1] net: nfc: fix use-after-free in nfc_get_local_general_bytes
2026-09-01 14:47 ` [PATCH net v2 1/1] net: nfc: fix use-after-free in nfc_get_local_general_bytes Ren Wei
@ 2026-09-05 15:29 ` Simon Horman
0 siblings, 0 replies; 3+ messages in thread
From: Simon Horman @ 2026-09-05 15:29 UTC (permalink / raw)
To: weir
Cc: Simon Horman, oe-linux-nfc, netdev, david, davem, edumazet, kuba,
pabeni, error27, kees, pengpeng, raoxu, rosenp, dddddd, joe,
ian.ray, kuniyu, linma, vega, rakukuip
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev/#/patchset/7023ed1e8d7522917d699222d9d5db7135184abb.1788157546.git.rakukuip@gmail.com?part=1
---
net: nfc: fix use-after-free in nfc_get_local_general_bytes
This commit refactors nfc_get_local_general_bytes to accept a caller-provided
output buffer instead of returning a pointer to local->gb. All callers are
updated to provide their own destination buffers to prevent use-after-free
issues when the local reference count drops to zero.
[ ... ]
> diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
> --- a/net/nfc/nci/core.c
> +++ b/net/nfc/nci/core.c
> @@ -780,26 +780,24 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
> {
> struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
> struct nci_set_config_param param;
> + u8 gb[NFC_MAX_GT_LEN];
> int rc;
>
> - param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
> - if ((param.val == NULL) || (param.len == 0))
> + nfc_get_local_general_bytes(nfc_dev, gb, sizeof(gb),
> + ¶m.len);
> + if (param.len == 0)
> return 0;
>
> - if (param.len > NFC_MAX_GT_LEN)
> - return -EINVAL;
> -
> + param.val = gb;
> param.id = NCI_PN_ATR_REQ_GEN_BYTES;
>
> - rc = nci_request(ndev, nci_set_config_req, ¶m,
> - msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
> + rc = nci_set_config(ndev, param.id, param.len, param.val);
[Severity: High]
Does replacing nci_request with nci_set_config introduce a locking regression
here?
The original code used nci_request, which acquires ndev->req_lock before
proceeding. The new helper nci_set_config directly calls the unlocked
__nci_request:
net/nfc/nci/core.c:nci_set_config() {
...
return __nci_request(ndev, nci_set_config_req, ¶m,
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
}
Because __nci_request modifies ndev->req_status and reinitializes
ndev->req_completion, calling it without synchronization can corrupt the
wait queue if another thread is concurrently executing a locked nci_request.
This polling path can be triggered by unprivileged user space via the netlink
NFC_CMD_START_POLL command. Is there a way to restore the req_lock
synchronization for these configuration updates?
> if (rc)
> return rc;
>
> param.id = NCI_LN_ATR_RES_GEN_BYTES;
>
> - return nci_request(ndev, nci_set_config_req, ¶m,
> - msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
> + return nci_set_config(ndev, param.id, param.len, param.val);
> }
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-05 15:29 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 14:47 [PATCH net v2 0/1] net: nfc: Fix use-after-free in general bytes retrieval Ren Wei
2026-09-01 14:47 ` [PATCH net v2 1/1] net: nfc: fix use-after-free in nfc_get_local_general_bytes Ren Wei
2026-09-05 15:29 ` Simon Horman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox