VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46173

CVE-2026-46173

Description

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

exit: prevent preemption of oopsing TASK_DEAD task

When an already-exiting task oopses, make_task_dead() currently calls do_task_dead() with preemption enabled. That is forbidden: do_task_dead() calls __schedule(), which has a comment saying "WARNING: must be called with preemption disabled!".

If an oopsing task is preempted in do_task_dead(), between becoming TASK_DEAD and entering the scheduler explicitly, bad things happen: finish_task_switch() assumes that once the scheduler has switched away from a TASK_DEAD task, the task can never run again and its stack is no longer needed; but that assumption apparently doesn't hold if the dead task was preempted (the SM_PREEMPT case).

This means that the scheduler ends up repeatedly dropping references on the dead task's stack, which can lead to use-after-free or double-free of the entire task stack; in other words, two tasks can end up running on the same stack, resulting in various kinds of memory corruption.

(This does not just affect "recursively oopsing" tasks; it is enough to oops once during task exit, for example in a file_operations::release handler)

AI Insight

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

In Linux kernel, a preemption bug in do_task_dead() during an oopsing TASK_DEAD task can lead to use-after-free or double-free of the task stack.

Vulnerability

In the Linux kernel, when an already-exiting task triggers an oops, the function make_task_dead() calls do_task_dead() with preemption enabled, violating the requirement that do_task_dead() must be called with preemption disabled. This bug affects kernel versions prior to the inclusion of the fix commit 6f49f94f3b11 [1]. The vulnerable code path is reachable when a task oopses during exit, for example in a file_operations::release handler [1].

Exploitation

An attacker needs to trigger an oops in a task that is already exiting. No special privileges or network position are required beyond the ability to cause the oops. The vulnerability relies on a race condition: after the oopsing task becomes TASK_DEAD but before it explicitly enters the scheduler via __schedule(), preemption can occur. This allows a different task to be scheduled on the same CPU while the dead task's stack is still considered in use [1].

Impact

On successful exploitation, the scheduler can assume the dead task's stack is no longer needed and drop references repeatedly, leading to use-after-free or double-free of the entire task stack. This can result in two tasks running on the same stack, causing memory corruption and potentially enabling privilege escalation or system instability [1].

Mitigation

The issue is fixed in commit 6f49f94f3b11 in the Linux kernel stable tree [1]. Users should apply the patch or update to a kernel version containing this commit. No workaround is available; the vulnerability requires a kernel fix.

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

Affected products

2

Patches

10
c1fa0bb633e4

exit: prevent preemption of oopsing TASK_DEAD task

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJann HornMay 11, 2026Fixed in 7.1-rc4via kernel-cna
2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 25e9cb6de7e793..9a909993ab1d8b 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1073,6 +1073,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 25e9cb6de7e793..9a909993ab1d8b 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1073,6 +1073,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
640b4c00fb0e

exit: prevent preemption of oopsing TASK_DEAD task

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJann HornMay 11, 2026Fixed in 6.6.140via kernel-cna
2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 686bbe72bb41ad..910ffc7450408e 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -981,6 +981,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 686bbe72bb41ad..910ffc7450408e 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -981,6 +981,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
7b2800ba5f5f

exit: prevent preemption of oopsing TASK_DEAD task

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJann HornMay 11, 2026Fixed in 6.12.88via kernel-cna
2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index d465b36bcc8696..021403fc756a53 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1045,6 +1045,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index d465b36bcc8696..021403fc756a53 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1045,6 +1045,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
6f49f94f3b11

exit: prevent preemption of oopsing TASK_DEAD task

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJann HornMay 11, 2026Fixed in 6.18.30via kernel-cna
2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index c8c3ff935a84b8..c97db291c5d1cb 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1069,6 +1069,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index c8c3ff935a84b8..c97db291c5d1cb 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1069,6 +1069,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
9756b3db5db6

exit: prevent preemption of oopsing TASK_DEAD task

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJann HornMay 11, 2026Fixed in 7.0.7via kernel-cna
2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index ede3117fa7d413..9852444627a0bf 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1074,6 +1074,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index ede3117fa7d413..9852444627a0bf 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1074,6 +1074,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
640b4c00fb0e

exit: prevent preemption of oopsing TASK_DEAD task

2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 686bbe72bb41ad..910ffc7450408e 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -981,6 +981,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 686bbe72bb41ad..910ffc7450408e 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -981,6 +981,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
6f49f94f3b11

exit: prevent preemption of oopsing TASK_DEAD task

2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index c8c3ff935a84b8..c97db291c5d1cb 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1069,6 +1069,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index c8c3ff935a84b8..c97db291c5d1cb 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1069,6 +1069,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
7b2800ba5f5f

exit: prevent preemption of oopsing TASK_DEAD task

2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index d465b36bcc8696..021403fc756a53 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1045,6 +1045,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index d465b36bcc8696..021403fc756a53 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1045,6 +1045,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
9756b3db5db6

exit: prevent preemption of oopsing TASK_DEAD task

2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index ede3117fa7d413..9852444627a0bf 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1074,6 +1074,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index ede3117fa7d413..9852444627a0bf 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1074,6 +1074,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
c1fa0bb633e4

exit: prevent preemption of oopsing TASK_DEAD task

2 files changed · +2 2
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 25e9cb6de7e793..9a909993ab1d8b 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1073,6 +1073,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
  • kernel/exit.c+1 1 modified
    diff --git a/kernel/exit.c b/kernel/exit.c
    index 25e9cb6de7e793..9a909993ab1d8b 100644
    --- a/kernel/exit.c
    +++ b/kernel/exit.c
    @@ -1073,6 +1073,7 @@ void __noreturn make_task_dead(int signr)
     		futex_exit_recursive(tsk);
     		tsk->exit_state = EXIT_DEAD;
     		refcount_inc(&tsk->rcu_users);
    +		preempt_disable();
     		do_task_dead();
     	}
     
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing preempt_disable() before do_task_dead() in make_task_dead() allows preemption of a TASK_DEAD task, violating scheduler assumptions and causing use-after-free of the task stack."

Attack vector

An attacker who can trigger a kernel oops (e.g., via a bug in a `file_operations::release` handler) in a task that is already exiting can cause the kernel to call `make_task_dead()` and then `do_task_dead()` with preemption still enabled. If the task is preempted between becoming `TASK_DEAD` and entering the scheduler, the scheduler's `finish_task_switch()` logic for the `SM_PREEMPT` case mishandles the dead task's stack reference, leading to use-after-free or double-free of the task stack. This can result in two tasks running on the same stack and arbitrary memory corruption [patch_id=2898064].

Affected code

The vulnerability is in the `make_task_dead()` function in `kernel/exit.c`. The function calls `do_task_dead()` without first disabling preemption, violating the requirement that `__schedule()` (called by `do_task_dead()`) must be invoked with preemption disabled [patch_id=2898064].

What the fix does

The fix adds a single line — `preempt_disable();` — immediately before the call to `do_task_dead()` inside `make_task_dead()` [patch_id=2898064]. This ensures that preemption is disabled before the task transitions to `TASK_DEAD` and calls into the scheduler, preventing the race condition where a preempted dead task could have its stack mishandled by `finish_task_switch()`. The patch is identical across all stable backports [patch_id=2898067][patch_id=2898062].

Preconditions

  • inputA task must already be in the process of exiting (e.g., in a file_operations::release handler) when it triggers a kernel oops.
  • configThe kernel must be running a preemptible configuration (CONFIG_PREEMPT).

Generated on May 28, 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.