VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46047

CVE-2026-46047

Description

In the Linux kernel, the following vulnerability has been resolved:

net: qrtr: ns: Fix use-after-free in driver remove()

In the remove callback, if a packet arrives after destroy_workqueue() is called, but before sock_release(), the qrtr_ns_data_ready() callback will try to queue the work, causing use-after-free issue.

Fix this issue by saving the default 'sk_data_ready' callback during qrtr_ns_init() and use it to replace the qrtr_ns_data_ready() callback at the start of remove(). This ensures that even if a packet arrives after destroy_workqueue(), the work struct will not be dereferenced.

Note that it is also required to ensure that the RX threads are completed before destroying the workqueue, because the threads could be using the qrtr_ns_data_ready() callback.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Use-after-free in Linux kernel's QRTR namespace service (qrtr_ns) during driver removal due to late packet arrival after workqueue destruction.

Vulnerability

A use-after-free vulnerability exists in the Linux kernel's QRTR namespace service (qrtr_ns). During the remove callback of the driver, a race window allows a packet to arrive after destroy_workqueue() is called but before sock_release(). The qrtr_ns_data_ready() callback then attempts to queue work on the destroyed workqueue, leading to a use-after-free condition. The affected code is in net/qrtr/ns.c. The issue is present in kernel versions prior to the fix commit [1].

Exploitation

An attacker would need to be able to send crafted packets to the QRTR socket, and have the driver removal occur while packets are in flight. The attack requires precise timing to trigger the race condition where a packet arrives after destroy_workqueue() but before sock_release(). No special privileges beyond network access to QRTR are required if the socket is exposed. The race window is dependent on system load and scheduling.

Impact

Successful exploitation results in a use-after-free, which can lead to arbitrary code execution or system crash. The attacker gains control over freed kernel memory, potentially escalating privileges or causing denial of service. As this is a use-after-free in kernel space, the impact is a compromise of system integrity, confidentiality, and availability.

Mitigation

The fix was applied in the Linux kernel stable tree via commit db3c60ec772de30acae92d560dfcc5258e58dbe8 [1]. The patch saves the default sk_data_ready callback during qrtr_ns_init() and restores it at the start of qrtr_ns_remove() before destroying the workqueue. It also ensures RX threads are completed before workqueue destruction. Users should update to a kernel version containing this fix. No workaround is provided in the reference.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
7809fea20c94

net: qrtr: ns: Fix use-after-free in driver remove()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitManivannan SadhasivamFixed in 7.1-rc1via kernel-cna
1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index c0418764470bed..b3f9bbcf9ab9ba 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -25,6 +25,7 @@ static struct {
     	u32 lookup_count;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -757,6 +758,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -797,6 +799,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -806,7 +812,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
0f313eb6a8f6

net: qrtr: ns: Fix use-after-free in driver remove()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitManivannan SadhasivamFixed in 6.6.140via kernel-cna
1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 654a3cc0d3479e..e6788b45000ab7 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -706,6 +707,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -746,6 +748,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -755,7 +761,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
db3c60ec772d

net: qrtr: ns: Fix use-after-free in driver remove()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitManivannan SadhasivamFixed in 6.12.86via kernel-cna
1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3de9350cbf3075..e5b3dac7836b1b 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
f96779e91657

net: qrtr: ns: Fix use-after-free in driver remove()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitManivannan SadhasivamFixed in 7.0.4via kernel-cna
1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3203b222086034..354cea22d088f4 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
2e127ceb1c41

net: qrtr: ns: Fix use-after-free in driver remove()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitManivannan SadhasivamFixed in 6.18.27via kernel-cna
1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3de9350cbf3075..e5b3dac7836b1b 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
f96779e91657

net: qrtr: ns: Fix use-after-free in driver remove()

1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3203b222086034..354cea22d088f4 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
0f313eb6a8f6

net: qrtr: ns: Fix use-after-free in driver remove()

1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 654a3cc0d3479e..e6788b45000ab7 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -706,6 +707,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -746,6 +748,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -755,7 +761,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
2e127ceb1c41

net: qrtr: ns: Fix use-after-free in driver remove()

1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3de9350cbf3075..e5b3dac7836b1b 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
7809fea20c94

net: qrtr: ns: Fix use-after-free in driver remove()

1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index c0418764470bed..b3f9bbcf9ab9ba 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -25,6 +25,7 @@ static struct {
     	u32 lookup_count;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -757,6 +758,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -797,6 +799,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -806,7 +812,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    
db3c60ec772d

net: qrtr: ns: Fix use-after-free in driver remove()

1 file changed · +11 1
  • net/qrtr/ns.c+11 1 modified
    diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
    index 3de9350cbf3075..e5b3dac7836b1b 100644
    --- a/net/qrtr/ns.c
    +++ b/net/qrtr/ns.c
    @@ -24,6 +24,7 @@ static struct {
     	struct list_head lookups;
     	struct workqueue_struct *workqueue;
     	struct work_struct work;
    +	void (*saved_data_ready)(struct sock *sk);
     	int local_node;
     } qrtr_ns;
     
    @@ -709,6 +710,7 @@ int qrtr_ns_init(void)
     		goto err_sock;
     	}
     
    +	qrtr_ns.saved_data_ready = qrtr_ns.sock->sk->sk_data_ready;
     	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
     
     	sq.sq_port = QRTR_PORT_CTRL;
    @@ -749,6 +751,10 @@ int qrtr_ns_init(void)
     	return 0;
     
     err_wq:
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	destroy_workqueue(qrtr_ns.workqueue);
     err_sock:
     	sock_release(qrtr_ns.sock);
    @@ -758,7 +764,12 @@ EXPORT_SYMBOL_GPL(qrtr_ns_init);
     
     void qrtr_ns_remove(void)
     {
    +	write_lock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +	qrtr_ns.sock->sk->sk_data_ready = qrtr_ns.saved_data_ready;
    +	write_unlock_bh(&qrtr_ns.sock->sk->sk_callback_lock);
    +
     	cancel_work_sync(&qrtr_ns.work);
    +	synchronize_net();
     	destroy_workqueue(qrtr_ns.workqueue);
     
     	/* sock_release() expects the two references that were put during
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing teardown ordering in qrtr_ns_remove() allows the qrtr_ns_data_ready() callback to queue work on a destroyed workqueue, causing use-after-free."

Attack vector

During driver removal, qrtr_ns_remove() calls destroy_workqueue() before restoring the original sk_data_ready callback. If a network packet arrives in this window, the qrtr_ns_data_ready() callback (still installed on the socket) attempts to queue work_struct work onto the already-destroyed workqueue, causing a use-after-free. An attacker on the same QRTR bus can trigger this by sending a packet to the nameservice socket while the driver is being removed [patch_id=2660159].

Affected code

The vulnerability is in net/qrtr/ns.c, in the qrtr_ns_remove() function and the error path of qrtr_ns_init() [patch_id=2660159]. The static qrtr_ns structure holds the workqueue and work_struct that are freed before the sk_data_ready callback is restored.

What the fix does

The patch saves the original sk_data_ready pointer in qrtr_ns.saved_data_ready during qrtr_ns_init() [patch_id=2660159]. In qrtr_ns_remove(), the custom qrtr_ns_data_ready callback is replaced with the saved default before cancel_work_sync() and destroy_workqueue() are called. A synchronize_net() barrier is also added after cancel_work_sync() to ensure any in-flight RX threads using the old callback have completed before the workqueue is destroyed. The same restoration is applied in the error path of qrtr_ns_init() for consistency.

Preconditions

  • configThe QRTR nameservice driver must be in the process of being removed (e.g., module unload or device removal).
  • networkAn attacker must be able to send a QRTR packet to the nameservice control port (QRTR_PORT_CTRL) during the removal window.

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.