[Deepin-Kernel-SIG] [linux 6.6.y] [Upstream] Update kernel base to 6.6.147 - #2034
Conversation
commit 920f893 upstream. Wongi and Jungwoo decoded and reported a non-leader exec() related race which can result in an UAF: sys_timer_delete() exec() posix_cpu_timer_del() // Observes old leader p = pid_task(pid, pid_type); de_thread() switch_leader(); release_task(old_leader) __exit_signal(old_leader) sighand = lock(old_leader, sighand); posix_cpu_timers*_exit(); sighand = lock_task_sighand(p) unhash_task(old_leader); sh = lock(p, sighand) old_leader->sighand = NULL; unlock(sighand); (p->sighand == NULL) unlock(sh) return NULL; // Returns without action if(!sighand) return 0; free_posix_timer(); This is "harmless" unless the deleted timer was armed and enqueued in p->signal because on exec() a TGID targeted timer is inherited. As sys_timer_delete() freed the underlying posix timer object run_posix_cpu_timers() or any timerqueue related add/delete operations on other timers will access the freed object's timerqueue node, which results in an UAF. There is a similar problem vs. posix_cpu_timer_set(). For regular posix timers it just transiently returns -ESRCH to user space, but for the use case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is allocated on the stack. Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops to expire. While debating solutions Frederic pointed out another problem: posix_cpu_timer_del(tmr) __exit_signal(p) posix_cpu_timers*_exit(p); unhash_task(p); p->sighand = NULL; sh = lock_task_sighand(p) sighand = p->sighand; if (!sighand) return NULL; lock(sighand); if (!sh) WARN_ON_ONCE(timer_queued(tmr)); On weakly ordered architectures it is not guaranteed that posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit() when p->sighand is observed as NULL, which means the WARN() can be a false positive. Solve these issues by: 1) Changing the store in __exit_signal() to smp_store_release(). 2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path of lock_task_sighand(). 3) Creating a helper function for looking up the task and locking sighand which does not return when sighand == NULL. Instead it retries the task lookup and only if that fails it gives up. 4) Using that helper in the three affected functions. #1/#2 ensures that the reader side which observes sighand == NULL also observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit() and the ones in unhash_task(). #3 ensures that the above described non-leader exec() situation is handled gracefully. When the task lookup returns the old leader, but sighand == NULL then it retries. In the non-leader exec() case the subsequent task lookup will observe the new leader due to #1/#2. In normal exit() scenarios the subsequent lookup fails. When the task lookup fails, the function also checks whether the timer is still enqueued and issues a warning if that's the case. Unfortunately there is nothing which can be done about it, but as the task is already not longer visible the timer should not be accessed anymore. This check also requires memory ordering, which is not provided when the first lookup fails. To achieve that the check is preceeded by a smp_rmb() which pairs with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that the stores in posix_cpu_timers*_exit() are visible. The history of the non-leader exec() issue goes back to the early days of posix CPU timers, which stored a pointer to the group leader task in the timer. That obviously fails when a non-leader exec() switches the leader. commit e0a7021 ("posix-cpu-timers: workaround to suppress the problems with mt exec") added a temporary workaround for that in 2010 which survived about 10 years. The fix for the workaround changed the task pointer to a pid pointer, but failed to see the subtle race described above. So the Fixes tag picks that commit, which seems to be halfways accurate. Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for review, feedback and suggestions and to Wongi and Jungwoo for the excellent bug report and analysis! Fixes: 55e8c8e ("posix-cpu-timers: Store a reference to a pid not a task") Reported-by: Wongi Lee <qw3rtyp0@gmail.com> Reported-by: Jungwoo Lee <jwlee2217@gmail.com> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin <sashal@kernel.org> (cherry picked from commit 12a891c773aeb5823d63dbd0cb2ab931d6c21c9b) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> (cherry picked from commit a1153c0deb44f75190f07677c0c6d61efd887246) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
Reviewer's GuideUpdates the kernel base to 6.6.147 and pulls in upstream fixes for POSIX CPU timers, including a new helper for safely locking a task’s sighand under races with exit/exec, memory-ordering corrections around sighand teardown, and minor Makefile version bumps. Sequence diagram for safe POSIX CPU timer operations racing with task exitsequenceDiagram
participant CPU_timer_path
participant Exit_path
participant task
participant sighand
CPU_timer_path->>CPU_timer_path: posix_cpu_timer_set(timer, timer_flags, new, old)
CPU_timer_path->>task: timer_lock_sighand(timer, flags)
activate CPU_timer_path
par exit_cleanup
Exit_path->>task: __exit_signal(tsk)
Exit_path->>sighand: flush_sigqueue(pending)
Exit_path->>task: smp_store_release_sighand_NULL(&tsk->sighand)
Exit_path-->>sighand: __cleanup_sighand(sighand)
and timer_lock
loop PID retry
CPU_timer_path->>task: pid_task(timer.it.cpu.pid, clock_pid_type(timer.it_clock))
alt task found
CPU_timer_path->>task: lock_task_sighand(tsk, flags)
alt sighand_non_NULL
CPU_timer_path-->>CPU_timer_path: proceed with timer operation
CPU_timer_path->>task: unlock_task_sighand(tsk, flags)
else sighand_NULL
CPU_timer_path->>task: smp_acquire__after_ctrl_dep()
CPU_timer_path-->>CPU_timer_path: p treated as reaped, return NULL
end
else task_not_found
CPU_timer_path-->>CPU_timer_path: break loop, return NULL
end
end
end
CPU_timer_path-->>CPU_timer_path: if !p return -ESRCH
deactivate CPU_timer_path
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Pull request overview
Updates this tree to Linux 6.6.147 by bringing in upstream fixes around POSIX CPU timer task lookup/sighand locking (to avoid exit/exec races) and tightening memory ordering when clearing tsk->sighand, plus bumping the kernel sublevel.
Changes:
- Bump kernel sublevel to
6.6.147. - Refactor POSIX CPU timer delete/set/rearm to use a shared helper that safely looks up the target task and locks its
sighand. - Add release/acquire pairing between
__exit_signal()andlock_task_sighand()for correct state visibility whensighandbecomesNULL.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Makefile | Bumps SUBLEVEL from 146 to 147. |
| kernel/time/posix-cpu-timers.c | Introduces timer_lock_sighand() and updates delete/set/rearm paths to use it, addressing exit/exec races. |
| kernel/signal.c | Adds acquire barrier on the sighand == NULL path in __lock_task_sighand(). |
| kernel/exit.c | Switches tsk->sighand = NULL to smp_store_release() to pair with the new acquire path. |
Suppressed comments (2)
kernel/time/posix-cpu-timers.c:485
- Typo/grammar in comment: "themself" / "not longer" -> "themselves" / "no longer".
* all pending timers from the related timer queues. The POSIX timers (k_itimer)
* themself are still accessible, but not longer connected to the task.
kernel/time/posix-cpu-timers.c:521
- Typo in comment: "wont" should be "won't".
* timer wont expire anymore.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * In this case sighand is NULL, which means the task and the associated timer | ||
| * queue cannot be longer accessed safely. |
| /* | ||
| * Pairs with the smp_store_release() in | ||
| * __exit_signal(). It ensures that all state | ||
| * modifications to the task preceeding the store are | ||
| * visible to the callers of lock_task_sighand(). | ||
| */ |
| /* | ||
| * Ensure that all preceeding state is visible. Pairs with | ||
| * the smp_acquire__after_ctrl_dep() in the sighand == NULL | ||
| * path of lock_task_sighand(). | ||
| */ |
Update kernel base to 6.6.147.
Summary by Sourcery
Update POSIX CPU timer handling and signal-hand locking to fix race conditions with task exit/exec, and bump the kernel version to 6.6.147.
Bug Fixes:
Enhancements:
Build: