[PATCH] fork: Don't let copy_exec_state() fail before rt_mutex_init_task()

From: Christian Aurich Zanettini Martins

Date: Mon Nov 02 2026 - 19:28:41 EST


Since commit 6b1c66c9cca9 ("exec_state: relocate dumpable information"),
copy_exec_state() can fail before rt_mutex_init_task() has reset the PI
state that dup_task_struct() copied from the parent. If the allocation
fails while the parent owns an rt_mutex with waiters, such as a
contended PI futex, free_task() finds the parent's waiters in the child
and warns with CONFIG_DEBUG_RT_MUTEXES=y:

DEBUG_LOCKS_WARN_ON(!RB_EMPTY_ROOT(&task->pi_waiters.rb_root))
WARNING: kernel/locking/rtmutex_api.c:538 at rt_mutex_debug_task_free+0x7c/0xa0, CPU#0: repro/75
Call Trace:
free_task+0x34/0x80
copy_process+0x5e6/0x2550
kernel_clone+0xd7/0x420

This also turns lock debugging off until reboot, or panics with
panic_on_warn.

Commit 08cacffeef8f ("ftrace: fork: Initialize function graph state
before copy_exec_state()") fixed the same problem for the function graph
stack. Move copy_exec_state() down to just before copy_creds(). That
restores the order from before 6b1c66c9cca9, where copy_creds() was the
first step that could fail.

Fixes: 6b1c66c9cca9 ("exec_state: relocate dumpable information")
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@xxxxxxxxx>
---

Notes:
Reproduced in QEMU with failslab on the task_exec_state cache while the
forking thread holds a PI futex another thread is waiting on. With this
patch the fork just fails with -ENOMEM.

Based on v7.4-rc1.

kernel/fork.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/kernel/fork.c b/kernel/fork.c
index 8b108ed37152..fc12100e24d6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2147,9 +2147,6 @@ __latent_entropy struct task_struct *copy_process(
* free the parent's ret_stack.
*/
ftrace_graph_init_task(p);
- retval = copy_exec_state(clone_flags, p);
- if (retval)
- goto bad_fork_free;
p->flags &= ~PF_KTHREAD;
if (args->kthread)
p->flags |= PF_KTHREAD;
@@ -2177,6 +2174,13 @@ __latent_entropy struct task_struct *copy_process(
#ifdef CONFIG_PROVE_LOCKING
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
+ /*
+ * First fallible op. Must run after rt_mutex_init_task(), so error
+ * paths never see the parent's PI waiters.
+ */
+ retval = copy_exec_state(clone_flags, p);
+ if (retval)
+ goto bad_fork_free;
retval = copy_creds(p, clone_flags);
if (retval < 0)
goto bad_fork_free;
--
2.43.0