[PATCH] posix-cpu-timers: Dequeue per-thread timers before exchange_tids()

Hyunwoo Kim <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.file-systems,gmane.linux.kernel.mm
Message-ID <anfgrsPlUdwBhdrp@v4bel>
A per-thread CPU timer holds a reference to the PID of the thread it is
attached to and, while it is armed, its node is queued in that thread's
posix_cputimers. The task is looked up by that PID.

When a non-leader thread exec()s, de_thread() changes which task owns
that PID:

  de_thread(tsk)
    exchange_tids(tsk, leader);		// tsk's PID now belongs to leader
    ...
    release_task(leader)
      __exit_signal(leader)
        posix_cpu_timers_exit(leader);	// cleans leader's queue, not tsk's
        __unhash_process(leader)	// that PID has no task anymore

pid_task(timer->it.cpu.pid, PIDTYPE_PID) then returns NULL, but the node
is still queued on tsk, which is alive. timer_lock_sighand() takes a
failed lookup to mean that the node is already dequeued, so it has
nothing to undo.

begin_new_exec() calls posix_cpu_timers_exit(me) right after
exec_task_namespaces() and that removes the leftover node, so the state
normally stays invisible. But bprm->point_of_no_return is set before
de_thread(), so if unshare_files(), set_mm_exe_file(), exec_mmap() or
exec_task_namespaces() fails, the task dies before it gets there.
exit_itimers() then frees the k_itimer while its node is still queued,
and reaping tsk later erases that freed node from the rbtree.

Dequeue the per-thread CPU timers of tsk before the PID changes hands, so
that a failed lookup again implies a dequeued node. Process-wide timers
are looked up with PIDTYPE_TGID and transfer_pid() moves that link to
tsk, so they are left alone.

Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
Cc: [email protected]
Signed-off-by: Hyunwoo Kim <[email protected]>
---
 fs/exec.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index c7b8f2d6366c44..f80f70e1c26de4 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1000,6 +1000,18 @@ static int de_thread(struct task_struct *tsk)
 		 * the former thread group leader:
 		 */
 
+#ifdef CONFIG_POSIX_TIMERS
+		/*
+		 * exchange_tids() hands this thread's PID to the old leader,
+		 * which is reaped right after. The PID lookup in
+		 * timer_lock_sighand() then fails while the per thread CPU
+		 * timers are still queued here, so dequeue them first.
+		 */
+		spin_lock(lock);
+		posix_cpu_timers_exit(tsk);
+		spin_unlock(lock);
+#endif
+
 		/* Become a process group leader with the old leader's pid.
 		 * The old leader becomes a thread of the this thread group.
 		 */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.