CVE-2026-46267
Description
Linux kernel NFC vulnerability allows UAF and shutdown races due to improper timer and work handling during deinitialization.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Linux kernel NFC vulnerability allows UAF and shutdown races due to improper timer and work handling during deinitialization.
Vulnerability
The Linux kernel's NFC subsystem has a use-after-free (UAF) and shutdown race condition vulnerability. The llc_shdlc_deinit() function frees the llc_shdlc context while its timers and state machine work might still be active. Timer callbacks can schedule sm_work, which accesses the SHDLC state and skb queues, leading to a UAF if teardown occurs concurrently with queued or running work items. This affects versions prior to the fix [1].
Exploitation
An attacker could exploit this vulnerability by triggering a deinitialization sequence in the NFC subsystem while timers or state machine work items are active. This race condition requires specific timing and conditions related to the NFC device's lifecycle and ongoing operations to manifest, potentially involving parallel execution paths within the kernel's NFC handling code [1].
Impact
Successful exploitation of this vulnerability can lead to a use-after-free condition and other shutdown race issues within the Linux kernel's NFC subsystem. This could result in system instability, crashes, or potentially allow an attacker to gain elevated privileges or execute arbitrary code with kernel-level permissions, depending on the exact nature of the UAF and race condition [1].
Mitigation
The vulnerability has been resolved by ensuring all SHDLC timers are stopped and sm_work is canceled synchronously before purging queues and freeing the context. The fix is available in the Linux kernel. Specific patched versions and release dates are not detailed in the provided references, but the fix is present in the kernel source [1].
AI Insight generated on Jun 3, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
14a24a676329d4nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
c9efde1e537bnfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
c60f41022eaanfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index aef750d7787c8..948cf4d210bde 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -779,6 +779,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
77eef9f2eef0nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
cf70cedce327nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
276820278e97nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
1cb97b122545nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
1cb97b122545nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
a24a676329d4nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
276820278e97nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
77eef9f2eef0nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
c9efde1e537bnfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index 4fc37894860c9..08c8aa1530d8a 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
c60f41022eaanfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index aef750d7787c8..948cf4d210bde 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -779,6 +779,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
cf70cedce327nfc: hci: shdlc: Stop timers and work before freeing context
1 file changed · +8 −1
net/nfc/hci/llc_shdlc.c+8 −1 modifieddiff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c index e90f70385813a..a106f4352356d 100644 --- a/net/nfc/hci/llc_shdlc.c +++ b/net/nfc/hci/llc_shdlc.c @@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) { struct llc_shdlc *shdlc = nfc_llc_get_data(llc); + timer_shutdown_sync(&shdlc->connect_timer); + timer_shutdown_sync(&shdlc->t1_timer); + timer_shutdown_sync(&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + + cancel_work_sync(&shdlc->sm_work); + skb_queue_purge(&shdlc->rcv_q); skb_queue_purge(&shdlc->send_q); skb_queue_purge(&shdlc->ack_pending_q); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"The llc_shdlc_deinit function frees the llc_shdlc context while timers and work items may still be active."
Attack vector
The vulnerability is triggered during the teardown of the SHDLC context. If the teardown process in `llc_shdlc_deinit()` occurs concurrently with an active timer callback or a queued/running work item, it can lead to race conditions. Specifically, timer callbacks can schedule `sm_work`, which then accesses the SHDLC state and skb queues after they may have been freed, resulting in a use-after-free vulnerability and other shutdown races.
Affected code
The vulnerability exists in the `llc_shdlc_deinit()` function within the `net/nfc/hci/llc_shdlc.c` file. The original implementation purged skb queues and freed the `llc_shdlc` structure while its timers and state machine work might still be active. The fix introduces synchronous cancellation of timers and work items before memory deallocation.
What the fix does
The patch modifies the `llc_shdlc_deinit()` function to ensure that all SHDLC timers are stopped synchronously using `timer_shutdown_sync()` and that the `sm_work` is cancelled synchronously using `cancel_work_sync()`. This is done before the function proceeds to purge the skb queues and free the `llc_shdlc` context. By synchronizing the shutdown of timers and work items, the patch prevents the `sm_work` from accessing freed memory, thus mitigating the use-after-free and shutdown race conditions.
Generated on Jun 3, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- git.kernel.org/stable/c/1cb97b1225450af3f7b728777929ba50c6a58cednvd
- git.kernel.org/stable/c/276820278e9717cc7d4bb32381892dd3ddf418d4nvd
- git.kernel.org/stable/c/77eef9f2eef045c3c37a3df82d3e661afb866b98nvd
- git.kernel.org/stable/c/a24a676329d40481b2331bfa1418a679577dfd3anvd
- git.kernel.org/stable/c/c60f41022eaad2a1dafecd3ae6f249a3bd6d4b6envd
- git.kernel.org/stable/c/c9efde1e537baed7648a94022b43836a348a074fnvd
- git.kernel.org/stable/c/cf70cedce327833296ebe6043364d1e44b76a2abnvd
News mentions
1- Linux Kernel: 25 Vulnerabilities Disclosed in Single Batch on June 3, 2026Vypr Intelligence · Jun 3, 2026