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(-)
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
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
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
© 2016 - 2026 Red Hat, Inc.