VYPR
Unrated severityNVD Advisory· Published Jun 8, 2026

CVE-2026-46274

CVE-2026-46274

Description

Linux kernel io_wq_remove_pending() mishandles hashed vs non-hashed work, leading to a use-after-free vulnerability.

AI Insight

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

Linux kernel io_wq_remove_pending() mishandles hashed vs non-hashed work, leading to a use-after-free vulnerability.

Vulnerability

The Linux kernel's io_wq_remove_pending() function incorrectly handles the cleanup of hashed work queues. Specifically, it fails to verify that a predecessor in a work list is actually hashed before updating the hash_tail pointer. This can occur when hashed bucket-0 work is cancelled and its predecessor is non-hashed. The function io_get_work_hash() returns 0 for non-hashed work, causing the check to pass spuriously. This results in wq->hash_tail[0] being updated with a pointer to non-hashed io_kiocb work.

Exploitation

An attacker can trigger this vulnerability by cancelling hashed bucket-0 work when a non-hashed work item precedes it in the list. The io_wq is task-specific and persists across ring operations. After the non-hashed io_kiocb completes and is freed, wq->hash_tail[0] becomes a dangling pointer. The next time hashed bucket-0 work is enqueued, io_wq_insert_work() and wq_list_add_after() will dereference this stale pointer.

Impact

Dereferencing the dangling pointer leads to a use-after-free vulnerability. This can result in a crash or potentially arbitrary code execution within the context of the affected task, depending on the state of the freed memory and subsequent operations.

Mitigation

This vulnerability has been resolved in the Linux kernel. The fix involves adding a check for io_wq_is_hashed() to ensure that a non-hashed predecessor does not incorrectly inherit a hash_tail[] slot. The specific commit addressing this issue is d6bda9df0c0a3080804181464d5c0f4d78a4e769 [1].

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

Affected products

1

Patches

10
d6a2d7b04b5a

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitNicholas CarliniMay 11, 2026Fixed in 7.1-rc4via kernel-cna
1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 7a9f94a0ce6f2..8cc7b47d30894 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1124,7 +1124,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
5a20ebf0c81b

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitNicholas CarliniMay 11, 2026Fixed in 6.12.91via kernel-cna
1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index faa00f163e236..0aa32ec6de630 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1044,7 +1044,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
d6bda9df0c0a

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitNicholas CarliniMay 11, 2026Fixed in 6.6.141via kernel-cna
1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index c848a5018d126..d8123dc5d742c 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1046,7 +1046,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
252c5051dba9

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitNicholas CarliniMay 11, 2026Fixed in 6.18.33via kernel-cna
1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 49a9c914b4e97..bd47d32f71c25 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1125,7 +1125,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
d376c131af7c

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitNicholas CarliniMay 11, 2026Fixed in 7.0.10via kernel-cna
1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 7a9f94a0ce6f2..8cc7b47d30894 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1124,7 +1124,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
5a20ebf0c81b

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index faa00f163e236..0aa32ec6de630 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1044,7 +1044,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
252c5051dba9

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 49a9c914b4e97..bd47d32f71c25 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1125,7 +1125,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
d376c131af7c

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 7a9f94a0ce6f2..8cc7b47d30894 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1124,7 +1124,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
d6bda9df0c0a

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index c848a5018d126..d8123dc5d742c 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1046,7 +1046,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    
d6a2d7b04b5a

io-wq: check that the predecessor is hashed in io_wq_remove_pending()

1 file changed · +2 2
  • io_uring/io-wq.c+2 2 modified
    diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
    index 7a9f94a0ce6f2..8cc7b47d30894 100644
    --- a/io_uring/io-wq.c
    +++ b/io_uring/io-wq.c
    @@ -1124,7 +1124,8 @@ static inline void io_wq_remove_pending(struct io_wq *wq,
     	if (io_wq_is_hashed(work) && work == wq->hash_tail[hash]) {
     		if (prev)
     			prev_work = container_of(prev, struct io_wq_work, list);
    -		if (prev_work && io_get_work_hash(prev_work) == hash)
    +		if (prev_work && io_wq_is_hashed(prev_work) &&
    +		    io_get_work_hash(prev_work) == hash)
     			wq->hash_tail[hash] = prev_work;
     		else
     			wq->hash_tail[hash] = NULL;
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

5

News mentions

0

No linked articles in our index yet.