nfc: fix use-after-free in nfc_get_local_general_bytes

Commit 6709d4b7bc ("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: 6709d4b7bc ("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: LLM
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Ren Wei <weir@nebusec.ai>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/3cbaac3bee23f8ff3a3284ed32d347696eb1d208.1788841683.git.rakukuip@gmail.com
Signed-off-by: David Heidelberg <david@ixit.cz>
This commit is contained in:
Luxiao Xu 2026-09-09 13:19:24 +08:00 committed by David Heidelberg
parent d2acbde7e6
commit dcab71a701
No known key found for this signature in database
GPG Key ID: 60023FC4D3492072
12 changed files with 55 additions and 42 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -6,6 +6,8 @@
* Copyright (C) 2012-2013 Tieto Poland
*/
#include <net/nfc/nfc.h>
#define PN533_DEVICE_STD 0x1
#define PN533_DEVICE_PASORI 0x2
#define PN533_DEVICE_ACR122U 0x3
@ -166,7 +168,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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -652,23 +652,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)

View File

@ -780,15 +780,15 @@ 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, &param.len);
if ((param.val == NULL) || (param.len == 0))
nfc_get_local_general_bytes(nfc_dev, gb, sizeof(gb),
&param.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, &param,

View File

@ -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);