* [PATCH 0/1] nfc: nci: ignore unexpected CORE_RESET_NTF
@ 2026-09-18 1:33 zjamg
2026-09-18 1:33 ` [PATCH 1/1] " zjamg
0 siblings, 1 reply; 7+ messages in thread
From: zjamg @ 2026-09-18 1:33 UTC (permalink / raw)
To: David Heidelberg
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, oe-linux-nfc, netdev, linux-kernel, Yuchao Zhang
From: Yuchao Zhang <ndaugoing@gmail.com>
Hello,
This patch addresses an issue in the NCI core stack where an unexpected
or unsolicited CORE_RESET_NTF packet can prematurely complete unrelated
in-flight requests with NCI_STATUS_OK and corrupt protocol version state.
Problem Overview:
=================
Commit bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence")
added nci_core_reset_ntf_packet() to handle NCI 2.x CORE_RESET
notifications. When received, it updates ndev->nci_ver and manufacturer
information, and calls nci_req_complete(ndev, NCI_STATUS_OK).
Unlike other notification handlers in ntf.c (which validate ndev->state
before acting), nci_core_reset_ntf_packet() does not verify whether a
core reset request is actually pending.
If an unsolicited or delayed CORE_RESET_NTF is received:
1. If another request is currently in-flight (such as CORE_INIT,
RF_DISCOVER, or CONN_CREATE), it prematurely completes that request
with NCI_STATUS_OK, leading to state desynchronization.
2. Even when no request is in-flight, it unconditionally overwrites
ndev->nci_ver and manufacturer info. Because ndev->nci_ver acts as
a parser and packet format selector (e.g., in nci_open_device() and
nci_core_init_rsp_packet()), unexpectedly modifying it can cause
protocol format confusion.
Solution:
=========
Introduce an NCI_RESET_PENDING flag in enum nci_flag to ensure
CORE_RESET_NTF is only accepted while a reset command is actively
awaiting it.
Testing:
========
Verified with module compilation and checkpatch.pl (0 errors, 0 warnings).
Empirically confirmed that unsolicited CORE_RESET_NTF packets are safely
rejected with a warning while legitimate reset sequences continue to
complete normally.
Thanks,
Yuchao Zhang
Yuchao Zhang (1):
nfc: nci: ignore unexpected CORE_RESET_NTF
include/net/nfc/nci_core.h | 1 +
net/nfc/nci/core.c | 3 +++
net/nfc/nci/ntf.c | 5 +++++
net/nfc/nci/rsp.c | 15 ++++++++++-----
4 files changed, 19 insertions(+), 5 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 1/1] nfc: nci: ignore unexpected CORE_RESET_NTF 2026-09-18 1:33 [PATCH 0/1] nfc: nci: ignore unexpected CORE_RESET_NTF zjamg @ 2026-09-18 1:33 ` zjamg 2026-09-21 12:58 ` Simon Horman 0 siblings, 1 reply; 7+ messages in thread From: zjamg @ 2026-09-18 1:33 UTC (permalink / raw) To: David Heidelberg Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, oe-linux-nfc, netdev, linux-kernel, Yuchao Zhang, stable From: Yuchao Zhang <ndaugoing@gmail.com> Commit bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") added handling of CORE_RESET_NTF in nci_core_reset_ntf_packet(). When received, it updates ndev->nci_ver, ndev->manufact_id, and ndev->manufact_specific_info, and calls nci_req_complete(ndev, NCI_STATUS_OK) to finish the pending reset request. However, unlike other notification handlers in ntf.c (which validate ndev->state before completing requests), nci_core_reset_ntf_packet() does not check whether a core reset request is actually pending. If an unsolicited or delayed CORE_RESET_NTF arrives (e.g. after a reset command times out or from a misbehaving NFCC), it unconditionally: 1. Completes whatever request is currently in-flight (such as CORE_INIT, RF_DISCOVER, or CONN_CREATE) with NCI_STATUS_OK, leading to kernel state desynchronization. 2. Overwrites ndev->nci_ver and manufacturer info. Because ndev->nci_ver is used as a selector for subsequent packet formats and parsers (e.g., in nci_open_device() and nci_core_init_rsp_packet()), unexpectedly modifying it can cause protocol format confusion. Introduce an NCI_RESET_PENDING flag to ensure CORE_RESET_NTF is only processed when a reset command is actively awaiting it: - Set NCI_RESET_PENDING in nci_reset_req() when sending CORE_RESET_CMD. - In nci_core_reset_rsp_packet(), if the reset failed or if handling NCI 1.x (where no notification is expected), clear NCI_RESET_PENDING and complete the request immediately. - In __nci_request(), ensure NCI_RESET_PENDING is cleared upon request completion, cancellation, or timeout. - In nci_core_reset_ntf_packet(), check and clear NCI_RESET_PENDING before updating device fields and completing the request. If the flag is not set, log a warning and drop the packet. Fixes: bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") Cc: stable@vger.kernel.org Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com> --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ net/nfc/nci/ntf.c | 5 +++++ net/nfc/nci/rsp.c | 15 ++++++++++----- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 664d5058e66e..504ea91ddf27 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -31,6 +31,7 @@ enum nci_flag { NCI_DATA_EXCHANGE, NCI_DATA_EXCHANGE_TO, NCI_UNREG, + NCI_RESET_PENDING, }; /* NCI device states */ diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5f46c4b5720f..5a90591cd9af 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -134,6 +134,7 @@ static int __nci_request(struct nci_dev *ndev, } ndev->req_status = ndev->req_result = 0; + clear_bit(NCI_RESET_PENDING, &ndev->flags); return rc; } @@ -163,6 +164,8 @@ static void nci_reset_req(struct nci_dev *ndev, const void *opt) { struct nci_core_reset_cmd cmd; + set_bit(NCI_RESET_PENDING, &ndev->flags); + cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); } diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index f5c9a8ab7ec1..69bc9047f057 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -36,6 +36,11 @@ static int nci_core_reset_ntf_packet(struct nci_dev *ndev, if (skb->len < sizeof(struct nci_core_reset_ntf)) return -EINVAL; + if (!test_and_clear_bit(NCI_RESET_PENDING, &ndev->flags)) { + pr_warn_ratelimited("unexpected CORE_RESET_NTF\n"); + return -EINVAL; + } + ntf = (struct nci_core_reset_ntf *)skb->data; ndev->nci_ver = ntf->nci_ver; diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index b0ab4f5acbce..a4bfe14372a8 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -32,14 +32,19 @@ static void nci_core_reset_rsp_packet(struct nci_dev *ndev, pr_debug("status 0x%x\n", rsp->status); + if (rsp->status != NCI_STATUS_OK) { + clear_bit(NCI_RESET_PENDING, &ndev->flags); + nci_req_complete(ndev, rsp->status); + return; + } + /* Handle NCI 1.x ver */ if (skb->len != 1) { - if (rsp->status == NCI_STATUS_OK) { - ndev->nci_ver = rsp->nci_ver; - pr_debug("nci_ver 0x%x, config_status 0x%x\n", - rsp->nci_ver, rsp->config_status); - } + ndev->nci_ver = rsp->nci_ver; + pr_debug("nci_ver 0x%x, config_status 0x%x\n", + rsp->nci_ver, rsp->config_status); + clear_bit(NCI_RESET_PENDING, &ndev->flags); nci_req_complete(ndev, rsp->status); } } -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/1] nfc: nci: ignore unexpected CORE_RESET_NTF 2026-09-18 1:33 ` [PATCH 1/1] " zjamg @ 2026-09-21 12:58 ` Simon Horman 2026-09-22 8:07 ` Yuchao Zhang 2026-09-22 8:07 ` [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP Yuchao Zhang 0 siblings, 2 replies; 7+ messages in thread From: Simon Horman @ 2026-09-21 12:58 UTC (permalink / raw) To: ndaugoing Cc: Simon Horman, david, davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev, linux-kernel, stable 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://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260918013337.82214-2-ndaugoing@gmail.com?part=1 --- nfc: nci: ignore unexpected CORE_RESET_NTF This adds an NCI_RESET_PENDING flag that is set when CORE_RESET_CMD is sent and cleared on request completion, so that CORE_RESET_NTF is only handled while a reset request is outstanding. Unexpected notifications are dropped with a ratelimited warning. A few questions about the new gate below. > diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c > index 5f46c4b5720f6..5a90591cd9af7 100644 > --- a/net/nfc/nci/core.c > +++ b/net/nfc/nci/core.c > @@ -134,6 +134,7 @@ static int __nci_request(struct nci_dev *ndev, > } > > ndev->req_status = ndev->req_result = 0; > + clear_bit(NCI_RESET_PENDING, &ndev->flags); > > return rc; > } [Severity: Medium] Since the flag carries no generation or sequence identity, can a CORE_RESET_NTF belonging to an abandoned reset be matched against a later reset attempt? If reset attempt #1 times out here, the flag is cleared, but the notification may already be sitting in nci_rx_work. The failure path in nci_open_device() purges the queue but does not drain the worker: /* Init failed, cleanup */ skb_queue_purge(&ndev->cmd_q); skb_queue_purge(&ndev->rx_q); skb_queue_purge(&ndev->tx_q); ndev->ops->close(ndev); Only nci_close_device() flushes ndev->rx_wq, and it does that after dropping req_lock; the exported nci_core_reset() path purges nothing. If userspace then retries NFC_CMD_DEV_UP (or a driver calls nci_core_reset()), nci_reset_req() sets the same bit again and the stale frame is accepted by test_and_clear_bit() in nci_core_reset_ntf_packet(), overwriting ndev->nci_ver / manufact_* from the old frame and completing reset #2 with NCI_STATUS_OK before its own CORE_RESET_RSP has been seen. The later real response can then complete the following CORE_INIT request. > @@ -163,6 +164,8 @@ static void nci_reset_req(struct nci_dev *ndev, const void *opt) > { > struct nci_core_reset_cmd cmd; > > + set_bit(NCI_RESET_PENDING, &ndev->flags); > + > cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; > nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); > } [Severity: Medium] Is a single bit enough synchronisation between the two contexts here? nci_reset_req()/__nci_request() run in process context under ndev->req_lock, while nci_core_reset_ntf_packet() runs from nci_rx_work on ndev->rx_wq, and the two share no lock. The check and the use are not atomic. After test_and_clear_bit() succeeds in nci_core_reset_ntf_packet(), the worker can be preempted before it reaches: ndev->nci_ver = ntf->nci_ver; ... nci_req_complete(ndev, NCI_STATUS_OK); If the reset it belonged to times out in that window, __nci_request() zeroes req_status and returns -ETIMEDOUT, and a new request can be armed (req_status = NCI_REQ_PEND). nci_req_complete() only looks at the global status: if (ndev->req_status == NCI_REQ_PEND) { ndev->req_result = result; ndev->req_status = NCI_REQ_DONE; complete(&ndev->req_completion); } so the resumed worker completes the new request as successful, and writes ndev->nci_ver while nci_open_device() may be reading it: if (ndev->nci_ver & NCI_VER_2_MASK) opt = &nci_init_v2_cmd; Would tying the notification to a request generation, or serialising the flag test with request start/teardown, close this? > diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c > index f5c9a8ab7ec10..b7fa8980804da 100644 > --- a/net/nfc/nci/ntf.c > +++ b/net/nfc/nci/ntf.c > @@ -36,6 +36,11 @@ static int nci_core_reset_ntf_packet(struct nci_dev *ndev, > if (skb->len < sizeof(struct nci_core_reset_ntf)) > return -EINVAL; > > + if (!test_and_clear_bit(NCI_RESET_PENDING, &ndev->flags)) { > + pr_warn_ratelimited("unexpected CORE_RESET_NTF\n"); > + return -EINVAL; > + } > + [Severity: High] Does returning -EINVAL here break the fdp firmware update handshake? In nci_ntf_packet() a non-zero return skips the driver dispatch: case NCI_OP_CORE_RESET_NTF: if (nci_core_reset_ntf_packet(ndev, skb)) goto end; break; ... nci_core_ntf_packet(ndev, ntf_opcode, skb); end: kfree_skb(skb); nci_core_ntf_packet() is the only dispatcher for driver core_ops handlers, and drivers/nfc/fdp registers one for exactly this notification: static const struct nci_driver_ops fdp_core_ops[] = { ... { .opcode = NCI_OP_CORE_RESET_NTF, .ntf = fdp_nci_core_reset_ntf_packet, }, }; That hook is what wakes the patch path: static int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { struct fdp_nci_info *info = nci_get_drvdata(ndev); info->setup_reset_ntf = 1; wake_up(&info->setup_wq); The notification it waits for is unsolicited, as the comment in fdp_nci_send_patch() describes: * command with NCI_PATCH_TYPE_EOT parameter. The device will send a * NFCC_PATCH_NTF packet and a NCI_OP_CORE_RESET_NTF packet. At that point the last request was nci_prop_cmd(NCI_OP_PROP_PATCH_OID), whose completion also clears NCI_RESET_PENDING in __nci_request(), so the flag is clear and the notification is dropped. Would fdp_nci_patch_otp()/fdp_nci_patch_ram() then block forever in wait_event_interruptible(info->setup_wq, info->setup_reset_ntf); with no timeout? That wait runs inside ndev->ops->setup(), which nci_open_device() calls while holding ndev->req_lock, so a stalled task would also block nci_close_device()/nci_unregister_device() and keep the request_firmware() buffers pinned because fdp_nci_release_firmware() is never reached. Would it be enough to skip only the core-level bookkeeping (drop the state update and break) instead of aborting the whole notification pipeline? [Severity: Medium] Should the decision use ntf->reset_trigger rather than a host-side flag? The notification carries the discriminator itself: struct nci_core_reset_ntf { u8 reset_trigger; u8 config_status; ... but the accept/reject decision is taken before the packet is even parsed, and reset_trigger is never read in the handler. That cuts both ways. A genuine NFCC-initiated reset arriving outside a reset request is discarded with only a warning, even though the controller has returned to its initial state (RF discovery stopped, logical connections and credits destroyed, routing table cleared), and the driver hook does not run either. Conversely, a spontaneous notification (reset_trigger not indicating CORE_RESET_CMD) that happens to land inside the reset window is still accepted, still overwrites ndev->nci_ver / manufact_id / manufact_specific_info, and still calls: nci_req_complete(ndev, NCI_STATUS_OK); Is the intended bug class actually eliminated, or only narrowed by timing? > diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c > index b0ab4f5acbce8..a4bfe14372a81 100644 > --- a/net/nfc/nci/rsp.c > +++ b/net/nfc/nci/rsp.c > @@ -32,14 +32,19 @@ static void nci_core_reset_rsp_packet(struct nci_dev *ndev, > > pr_debug("status 0x%x\n", rsp->status); > > + if (rsp->status != NCI_STATUS_OK) { > + clear_bit(NCI_RESET_PENDING, &ndev->flags); > + nci_req_complete(ndev, rsp->status); > + return; > + } > + [Severity: Medium] Should the response handler test NCI_RESET_PENDING too, rather than only clearing it? nci_rsp_packet() dispatches purely by opcode with no correlation to the outstanding command: switch (rsp_opcode) { case NCI_OP_CORE_RESET_RSP: nci_core_reset_rsp_packet(ndev, skb); break; and nci_req_complete() only checks the global status, so a stray or delayed CORE_RESET_RSP still completes whatever unrelated request is in flight (CORE_INIT, RF_DISCOVER, CONN_CREATE) - the desynchronisation the commit message sets out to remove. This new block also widens that surface. Before the patch a 1-byte (NCI 2.x) response completed nothing at all; now the status test runs before any length discrimination, so a single 3-byte control frame (header plus a non-OK status, accepted by nci_valid_size() since the declared payload is non-zero) completes an unrelated request with a controller-chosen status. There is also a mirror case: because the flag is cleared here without being validated, a stray CORE_RESET_RSP during a genuine NCI 2.x reset disarms the new guard, and the real CORE_RESET_NTF is then rejected as unexpected, so the reset stalls until NCI_RESET_TIMEOUT. > /* Handle NCI 1.x ver */ > if (skb->len != 1) { > - if (rsp->status == NCI_STATUS_OK) { > - ndev->nci_ver = rsp->nci_ver; > - pr_debug("nci_ver 0x%x, config_status 0x%x\n", > - rsp->nci_ver, rsp->config_status); > - } > + ndev->nci_ver = rsp->nci_ver; > + pr_debug("nci_ver 0x%x, config_status 0x%x\n", > + rsp->nci_ver, rsp->config_status); > > + clear_bit(NCI_RESET_PENDING, &ndev->flags); > nci_req_complete(ndev, rsp->status); > } > } [Severity: Low] This is a pre-existing issue and not introduced by this patch, but does "skb->len != 1" read rsp->config_status past the received payload? config_status is the third byte: struct nci_core_reset_rsp { __u8 status; __u8 nci_ver; __u8 config_status; } __packed; and nci_valid_size() only requires the declared payload to be present and non-zero: if (skb->len < hdr_size || skb->len < hdr_size + nci_plen(skb->data)) { return false; } if (!nci_plen(skb->data)) { so a CORE_RESET_RSP with plen == 2 and status == NCI_STATUS_OK reaches this block and the pr_debug() reads one byte beyond the payload. The patch keeps the same condition (status == OK and len != 1), so this is unchanged behaviour; would an explicit length check for the 3-byte form be worth adding while this function is being touched? ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/1] nfc: nci: ignore unexpected CORE_RESET_NTF 2026-09-21 12:58 ` Simon Horman @ 2026-09-22 8:07 ` Yuchao Zhang 2026-09-22 8:07 ` [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP Yuchao Zhang 1 sibling, 0 replies; 7+ messages in thread From: Yuchao Zhang @ 2026-09-22 8:07 UTC (permalink / raw) To: Simon Horman Cc: david, davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev, linux-kernel, stable, Yuchao Zhang Hi Simon, Thank you (and the sashiko reviewer) for the thorough review. The points raised are completely valid and insightful. 1. Regarding FDP firmware update breakage (Severity: High): You are absolutely right. In nci_ntf_packet(), returning -EINVAL from nci_core_reset_ntf_packet() aborts the dispatch pipeline (goto end), which prevents nci_core_ntf_packet() from running driver-specific handlers. In drivers/nfc/fdp/fdp.c, the firmware patching path relies on this spontaneous CORE_RESET_NTF to wake up info->setup_wq; dropping the packet causes it to block indefinitely. As suggested, in v2 we drop only the core-level bookkeeping (skipping the ndev->nci_ver update and nci_req_complete) and return 0 so that driver-registered ops continue to receive the notification. 2. Regarding ntf->reset_trigger (Severity: Medium): The reset_trigger field is revision-dependent: in NCI 1.0, CORE_RESET_CMD is reported as 0x00 (0x01 is powered-on), while NCI 2.0 defines 0x00 as an unrecoverable error and reports CORE_RESET_CMD as 0x02. Hardcoding either value would break the other generation of devices, so v2 gates the core bookkeeping on NCI_RESET_PENDING alone and only skips power-on notifications (trigger 0x01, which means the same in both revisions). Power-on notifications never overlap a pending reset in the protocol flow, and both revision's command-triggered values reach the flag gate. 3. Regarding nci_core_reset_rsp_packet() (Severity: Medium): Agreed. nci_core_reset_rsp_packet() now checks whether NCI_RESET_PENDING is set before calling nci_req_complete() on non-OK or NCI 1.x responses to avoid prematurely completing an unrelated request. 4. Regarding NCI 1.x response length check (Severity: Low): In v2, we explicitly check skb->len >= sizeof(*rsp) to ensure config_status is not read out-of-bounds in pr_debug(). 5. Regarding request serialization / stale notifications (Severity: Medium): All requests are serialized under ndev->req_lock, and __nci_request() unconditionally clears NCI_RESET_PENDING before dropping req_lock. Filtering out power-on notifications (trigger 0x01) ensures spontaneous controller power-ups are ignored, while checking and clearing the pending flag ensures each command matches at most one notification. 6. Regarding request generation (Severity: Medium on both questions): The NCI protocol gives notifications no request correlation, so a stale CORE_RESET_NTF/RSP that is already inside nci_rx_work when a reset times out and userspace retries cannot be distinguished from the new attempt's own frame: the flag gate and the trigger check narrow the window, but a generation identity would require protocol support that does not exist. The failure path purges queued frames; only an in-flight worker frame can straddle, and the data it carries originates from the same controller. If you would prefer, I can add a per-request generation check for the RSP path where an identity exists, as a follow-up. v2 patch has been sent as reply to this thread. Thanks, Yuchao ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP 2026-09-21 12:58 ` Simon Horman 2026-09-22 8:07 ` Yuchao Zhang @ 2026-09-22 8:07 ` Yuchao Zhang 2026-09-22 10:05 ` David Heidelberg 1 sibling, 1 reply; 7+ messages in thread From: Yuchao Zhang @ 2026-09-22 8:07 UTC (permalink / raw) To: Simon Horman Cc: david, davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev, linux-kernel, stable, Yuchao Zhang Commit bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") added handling of CORE_RESET_NTF in nci_core_reset_ntf_packet(). When received, it updates ndev->nci_ver, ndev->manufact_id, and ndev->manufact_specific_info, and calls nci_req_complete(ndev, NCI_STATUS_OK) to finish the pending reset request. However, unlike other notification handlers in ntf.c (which validate ndev->state before completing requests), nci_core_reset_ntf_packet() does not check whether a core reset request is actually pending. If an unsolicited or delayed CORE_RESET_NTF arrives (e.g. after a reset command times out or from a misbehaving NFCC), it unconditionally: 1. Completes whatever request is currently in-flight (such as CORE_INIT, RF_DISCOVER, or CONN_CREATE) with NCI_STATUS_OK, leading to kernel state desynchronization. 2. Overwrites ndev->nci_ver and manufacturer info. Because ndev->nci_ver is used as a selector for subsequent packet formats and parsers (e.g., in nci_open_device() and nci_core_init_rsp_packet()), unexpectedly modifying it can cause protocol format confusion. A similar issue exists in nci_core_reset_rsp_packet(): an unexpected or delayed response packet can prematurely complete an unrelated in-flight request. Fix this by ensuring CORE_RESET_NTF and CORE_RESET_RSP are only processed by the core layer when a reset command is actively awaiting them: - Set NCI_RESET_PENDING in nci_reset_req() when sending CORE_RESET_CMD. - In nci_core_reset_rsp_packet(), ignore the response if NCI_RESET_PENDING is not set. If set, clear the flag and complete the request on failure or for NCI 1.x (checking skb->len >= sizeof(*rsp)). - In __nci_request(), ensure NCI_RESET_PENDING is cleared upon request completion, cancellation, or timeout. - In nci_core_reset_ntf_packet(), skip power-on notifications (trigger 0x01, identical in NCI 1.0 and 2.0), then check and clear NCI_RESET_PENDING before updating device fields and completing the request. The CORE_RESET_CMD trigger value is revision-dependent (0x00 in NCI 1.0, 0x02 in NCI 2.0), so both are accepted and gated on NCI_RESET_PENDING alone. If unexpected, log a warning and return 0 so driver-specific handlers (such as fdp firmware patch handling) still receive the notification. Fixes: bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") Cc: stable@vger.kernel.org Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com> --- v2: - Do not abort the notification pipeline on unexpected CORE_RESET_NTF (return 0 instead of -EINVAL), preserving driver-level hooks (e.g. fdp firmware patching) per Simon Horman. - Skip power-on notifications (trigger 0x01, identical in NCI 1.0 and 2.0) while gating both NCI 1.0 (0x00) and NCI 2.0 (0x02) command-triggered resets on NCI_RESET_PENDING alone. - Check NCI_RESET_PENDING in nci_core_reset_rsp_packet() to avoid completing unrelated requests on unexpected responses. - Explicitly check skb->len >= sizeof(*rsp) in nci_core_reset_rsp_packet() for NCI 1.x handling. include/net/nfc/nci.h | 5 +++++ include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ net/nfc/nci/ntf.c | 17 ++++++++++++++++- net/nfc/nci/rsp.c | 24 ++++++++++++++++++------ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 09efcaed7c3f..2ac45adba088 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -140,6 +140,11 @@ #define NCI_RESET_TYPE_KEEP_CONFIG 0x00 #define NCI_RESET_TYPE_RESET_CONFIG 0x01 +/* NCI Reset Triggers */ +#define NCI_RESET_TRIGGER_ERROR 0x00 +#define NCI_RESET_TRIGGER_POWER_ON 0x01 +#define NCI_RESET_TRIGGER_RESET_CMD 0x02 + /* NCI Static RF connection ID */ #define NCI_STATIC_RF_CONN_ID 0x00 diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 664d5058e66e..504ea91ddf27 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -31,6 +31,7 @@ enum nci_flag { NCI_DATA_EXCHANGE, NCI_DATA_EXCHANGE_TO, NCI_UNREG, + NCI_RESET_PENDING, }; /* NCI device states */ diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5f46c4b5720f..5a90591cd9af 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -134,6 +134,7 @@ static int __nci_request(struct nci_dev *ndev, } ndev->req_status = ndev->req_result = 0; + clear_bit(NCI_RESET_PENDING, &ndev->flags); return rc; } @@ -163,6 +164,8 @@ static void nci_reset_req(struct nci_dev *ndev, const void *opt) { struct nci_core_reset_cmd cmd; + set_bit(NCI_RESET_PENDING, &ndev->flags); + cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); } diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index f5c9a8ab7ec1..57f99bf46a22 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -36,7 +36,22 @@ static int nci_core_reset_ntf_packet(struct nci_dev *ndev, if (skb->len < sizeof(struct nci_core_reset_ntf)) return -EINVAL; - ntf = (struct nci_core_reset_ntf *)skb->data; + ntf = (const struct nci_core_reset_ntf *)skb->data; + + /* + * A power-on notification must not update core protocol state nor + * complete a pending request. The CORE_RESET_CMD trigger value is + * revision-dependent (0x00 in NCI 1.0, 0x02 in NCI 2.0), so both + * are let through to the reset-pending gate below instead. + * Return 0 so driver-specific notification hooks can still run. + */ + if (ntf->reset_trigger == NCI_RESET_TRIGGER_POWER_ON) + return 0; + + if (!test_and_clear_bit(NCI_RESET_PENDING, &ndev->flags)) { + pr_warn_ratelimited("unexpected CORE_RESET_NTF\n"); + return 0; + } ndev->nci_ver = ntf->nci_ver; pr_debug("nci_ver 0x%x, config_status 0x%x\n", diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index b0ab4f5acbce..3a2491497e4b 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -32,14 +32,26 @@ static void nci_core_reset_rsp_packet(struct nci_dev *ndev, pr_debug("status 0x%x\n", rsp->status); + /* + * If no reset request is pending, ignore unexpected responses to avoid + * prematurely completing an unrelated request. + */ + if (!test_bit(NCI_RESET_PENDING, &ndev->flags)) + return; + + if (rsp->status != NCI_STATUS_OK) { + clear_bit(NCI_RESET_PENDING, &ndev->flags); + nci_req_complete(ndev, rsp->status); + return; + } + /* Handle NCI 1.x ver */ - if (skb->len != 1) { - if (rsp->status == NCI_STATUS_OK) { - ndev->nci_ver = rsp->nci_ver; - pr_debug("nci_ver 0x%x, config_status 0x%x\n", - rsp->nci_ver, rsp->config_status); - } + if (skb->len >= sizeof(*rsp)) { + ndev->nci_ver = rsp->nci_ver; + pr_debug("nci_ver 0x%x, config_status 0x%x\n", + rsp->nci_ver, rsp->config_status); + clear_bit(NCI_RESET_PENDING, &ndev->flags); nci_req_complete(ndev, rsp->status); } } -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP 2026-09-22 8:07 ` [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP Yuchao Zhang @ 2026-09-22 10:05 ` David Heidelberg 2026-09-22 10:18 ` Yuchao Zhang 0 siblings, 1 reply; 7+ messages in thread From: David Heidelberg @ 2026-09-22 10:05 UTC (permalink / raw) To: Yuchao Zhang, Simon Horman Cc: davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev, linux-kernel, stable On 22/09/2026 10:07, Yuchao Zhang wrote: > Commit bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") > added handling of CORE_RESET_NTF in nci_core_reset_ntf_packet(). When > received, it updates ndev->nci_ver, ndev->manufact_id, and > ndev->manufact_specific_info, and calls nci_req_complete(ndev, > NCI_STATUS_OK) to finish the pending reset request. > > However, unlike other notification handlers in ntf.c (which validate > ndev->state before completing requests), nci_core_reset_ntf_packet() > does not check whether a core reset request is actually pending. > If an unsolicited or delayed CORE_RESET_NTF arrives (e.g. after a reset > command times out or from a misbehaving NFCC), it unconditionally: > 1. Completes whatever request is currently in-flight (such as CORE_INIT, > RF_DISCOVER, or CONN_CREATE) with NCI_STATUS_OK, leading to kernel > state desynchronization. > 2. Overwrites ndev->nci_ver and manufacturer info. Because > ndev->nci_ver is used as a selector for subsequent packet formats > and parsers (e.g., in nci_open_device() and nci_core_init_rsp_packet()), > unexpectedly modifying it can cause protocol format confusion. > > A similar issue exists in nci_core_reset_rsp_packet(): an unexpected or > delayed response packet can prematurely complete an unrelated in-flight > request. > > Fix this by ensuring CORE_RESET_NTF and CORE_RESET_RSP are only processed > by the core layer when a reset command is actively awaiting them: > - Set NCI_RESET_PENDING in nci_reset_req() when sending CORE_RESET_CMD. > - In nci_core_reset_rsp_packet(), ignore the response if NCI_RESET_PENDING > is not set. If set, clear the flag and complete the request on failure > or for NCI 1.x (checking skb->len >= sizeof(*rsp)). > - In __nci_request(), ensure NCI_RESET_PENDING is cleared upon request > completion, cancellation, or timeout. > - In nci_core_reset_ntf_packet(), skip power-on notifications (trigger > 0x01, identical in NCI 1.0 and 2.0), then check and clear > NCI_RESET_PENDING before updating device fields and completing the > request. The CORE_RESET_CMD trigger value is revision-dependent > (0x00 in NCI 1.0, 0x02 in NCI 2.0), so both are accepted and gated > on NCI_RESET_PENDING alone. If unexpected, log a warning and return > 0 so driver-specific handlers (such as fdp firmware patch handling) > still receive the notification. > > Fixes: bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") > Cc: stable@vger.kernel.org > Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com> > --- > v2: > - Do not abort the notification pipeline on unexpected CORE_RESET_NTF (return 0 > instead of -EINVAL), preserving driver-level hooks (e.g. fdp firmware > patching) per Simon Horman. > - Skip power-on notifications (trigger 0x01, identical in NCI 1.0 and 2.0) > while gating both NCI 1.0 (0x00) and NCI 2.0 (0x02) command-triggered resets > on NCI_RESET_PENDING alone. > - Check NCI_RESET_PENDING in nci_core_reset_rsp_packet() to avoid completing > unrelated requests on unexpected responses. > - Explicitly check skb->len >= sizeof(*rsp) in nci_core_reset_rsp_packet() > for NCI 1.x handling. Hello Yuchao, please never send follow-ups as part of the thread. Always send them as a separate message (otherwise they may get lost and we already have patchwork wich can track the submissions. Even better, try to use tools such as b4 to submit patches (easier for you, sent in expected format for us). Thanks David ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP 2026-09-22 10:05 ` David Heidelberg @ 2026-09-22 10:18 ` Yuchao Zhang 0 siblings, 0 replies; 7+ messages in thread From: Yuchao Zhang @ 2026-09-22 10:18 UTC (permalink / raw) To: David Heidelberg Cc: Simon Horman, davem, edumazet, kuba, pabeni, oe-linux-nfc, netdev, linux-kernel, stable Hi David, Thanks a lot for the pointer and explanation! I apologize for the threading confusion — I mistakenly chained the v2 patch under the previous review message using --in-reply-to. I'll re-send the v2 patch as a clean, standalone thread right away so that patchwork can track it properly, and I'll definitely adopt b4 for future submissions. Thanks again for keeping me on the right track! Best regards, Yuchao David Heidelberg <david@ixit.cz> 于2026年9月22日周二 18:05写道: > > On 22/09/2026 10:07, Yuchao Zhang wrote: > > Commit bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") > > added handling of CORE_RESET_NTF in nci_core_reset_ntf_packet(). When > > received, it updates ndev->nci_ver, ndev->manufact_id, and > > ndev->manufact_specific_info, and calls nci_req_complete(ndev, > > NCI_STATUS_OK) to finish the pending reset request. > > > > However, unlike other notification handlers in ntf.c (which validate > > ndev->state before completing requests), nci_core_reset_ntf_packet() > > does not check whether a core reset request is actually pending. > > If an unsolicited or delayed CORE_RESET_NTF arrives (e.g. after a reset > > command times out or from a misbehaving NFCC), it unconditionally: > > 1. Completes whatever request is currently in-flight (such as CORE_INIT, > > RF_DISCOVER, or CONN_CREATE) with NCI_STATUS_OK, leading to kernel > > state desynchronization. > > 2. Overwrites ndev->nci_ver and manufacturer info. Because > > ndev->nci_ver is used as a selector for subsequent packet formats > > and parsers (e.g., in nci_open_device() and nci_core_init_rsp_packet()), > > unexpectedly modifying it can cause protocol format confusion. > > > > A similar issue exists in nci_core_reset_rsp_packet(): an unexpected or > > delayed response packet can prematurely complete an unrelated in-flight > > request. > > > > Fix this by ensuring CORE_RESET_NTF and CORE_RESET_RSP are only processed > > by the core layer when a reset command is actively awaiting them: > > - Set NCI_RESET_PENDING in nci_reset_req() when sending CORE_RESET_CMD. > > - In nci_core_reset_rsp_packet(), ignore the response if NCI_RESET_PENDING > > is not set. If set, clear the flag and complete the request on failure > > or for NCI 1.x (checking skb->len >= sizeof(*rsp)). > > - In __nci_request(), ensure NCI_RESET_PENDING is cleared upon request > > completion, cancellation, or timeout. > > - In nci_core_reset_ntf_packet(), skip power-on notifications (trigger > > 0x01, identical in NCI 1.0 and 2.0), then check and clear > > NCI_RESET_PENDING before updating device fields and completing the > > request. The CORE_RESET_CMD trigger value is revision-dependent > > (0x00 in NCI 1.0, 0x02 in NCI 2.0), so both are accepted and gated > > on NCI_RESET_PENDING alone. If unexpected, log a warning and return > > 0 so driver-specific handlers (such as fdp firmware patch handling) > > still receive the notification. > > > > Fixes: bcd684aace34 ("net/nfc/nci: Support NCI 2.x initial sequence") > > Cc: stable@vger.kernel.org > > Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com> > > --- > > v2: > > - Do not abort the notification pipeline on unexpected CORE_RESET_NTF (return 0 > > instead of -EINVAL), preserving driver-level hooks (e.g. fdp firmware > > patching) per Simon Horman. > > - Skip power-on notifications (trigger 0x01, identical in NCI 1.0 and 2.0) > > while gating both NCI 1.0 (0x00) and NCI 2.0 (0x02) command-triggered resets > > on NCI_RESET_PENDING alone. > > - Check NCI_RESET_PENDING in nci_core_reset_rsp_packet() to avoid completing > > unrelated requests on unexpected responses. > > - Explicitly check skb->len >= sizeof(*rsp) in nci_core_reset_rsp_packet() > > for NCI 1.x handling. > Hello Yuchao, > > please never send follow-ups as part of the thread. Always send them as a > separate message (otherwise they may get lost and we already have patchwork wich > can track the submissions. > > Even better, try to use tools such as b4 to submit patches (easier for you, sent > in expected format for us). > > Thanks > David ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-22 10:18 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-18 1:33 [PATCH 0/1] nfc: nci: ignore unexpected CORE_RESET_NTF zjamg 2026-09-18 1:33 ` [PATCH 1/1] " zjamg 2026-09-21 12:58 ` Simon Horman 2026-09-22 8:07 ` Yuchao Zhang 2026-09-22 8:07 ` [PATCH v2] nfc: nci: ignore unexpected CORE_RESET_NTF and CORE_RESET_RSP Yuchao Zhang 2026-09-22 10:05 ` David Heidelberg 2026-09-22 10:18 ` Yuchao Zhang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®