From 3b7e363b1d5f3ca329a7056b509a54d081923cd1 Mon Sep 17 00:00:00 2001 From: Yannick Cote Date: Thu, 13 Oct 2022 10:36:30 -0400 Subject: [KPATCH 9.0] kpatch fixes for CVE-2022-2585 Kernels: 5.14.0-70.13.1.el9_0 5.14.0-70.17.1.el9_0 5.14.0-70.22.1.el9_0 5.14.0-70.26.1.el9_0 Changes since last build: arches: x86_64 ppc64le exec.o: changed function: begin_new_exec exit.o: changed function: do_exit posix-timers.o: new function: kpatch_cve_2022_2585_exit_itimers --------------------------- Modifications: - removed changes to .h files, for kpatch locality - redefine exit_itimers instead of changing it (kpatch_cve_2022_2585_exit_itimers()) - use kpatch defined exit_itimers() everywhere it's called in the kernel commit 84013659b0e8a6965e79f1e7d6108aa2be2230ab Author: Wander Lairson Costa Date: Thu Aug 25 09:27:13 2022 -0300 fix race between exit_itimers() and /proc/pid/timers Bugzilla: https://bugzilla.redhat.com/2116967 CVE: CVE-2022-2585 Y-Commit: a532f4903a5d24d1e5e692628458d3a10184f615 O-Bugzilla: https://bugzilla.redhat.com/2116968 commit d5b36a4dbd06c5e8e36ca8ccc552f679069e2946 Author: Oleg Nesterov Date: Mon Jul 11 18:16:25 2022 +0200 fix race between exit_itimers() and /proc/pid/timers As Chris explains, the comment above exit_itimers() is not correct, we can race with proc_timers_seq_ops. Change exit_itimers() to clear signal->posix_timers with ->siglock held. Cc: Reported-by: chris@accessvector.net Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds Signed-off-by: Wander Lairson Costa Signed-off-by: Herton R. Krzesinski commit d91003cca875f4b0ca290db6e686ee960125da3a Author: Wander Lairson Costa Date: Thu Aug 25 09:27:22 2022 -0300 posix-cpu-timers: Cleanup CPU timers before freeing them during exec Bugzilla: https://bugzilla.redhat.com/2116967 CVE: CVE-2022-2585 Y-Commit: b89dd8173ef086055a00bd606813e370efe7f0d7 O-Bugzilla: https://bugzilla.redhat.com/2116968 commit e362359ace6f87c201531872486ff295df306d13 Author: Thadeu Lima de Souza Cascardo Date: Tue Aug 9 14:07:51 2022 -0300 posix-cpu-timers: Cleanup CPU timers before freeing them during exec Commit 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") started looking up tasks by PID when deleting a CPU timer. When a non-leader thread calls execve, it will switch PIDs with the leader process. Then, as it calls exit_itimers, posix_cpu_timer_del cannot find the task because the timer still points out to the old PID. That means that armed timers won't be disarmed, that is, they won't be removed from the timerqueue_list. exit_itimers will still release their memory, and when that list is later processed, it leads to a use-after-free. Clean up the timers from the de-threaded task before freeing them. This prevents a reported use-after-free. Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Thomas Gleixner Reviewed-by: Thomas Gleixner Cc: Link: https://lore.kernel.org/r/20220809170751.164716-1-cascardo@canonical.com Signed-off-by: Wander Lairson Costa Signed-off-by: Herton R. Krzesinski Signed-off-by: Yannick Cote --- fs/exec.c | 6 +++++- kernel/exit.c | 4 +++- kernel/time/posix-timers.c | 26 +++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 2bb8dd6a4e2a..decf9ccef49d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1229,6 +1229,7 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec) perf_event_comm(tsk, exec); } +void kpatch_cve_2022_2585_exit_itimers(struct task_struct *tsk); /* * Calling this is the point of no return. None of the failures will be * seen by userspace since either the process is already taking a fatal @@ -1292,7 +1293,10 @@ int begin_new_exec(struct linux_binprm * bprm) bprm->mm = NULL; #ifdef CONFIG_POSIX_TIMERS - exit_itimers(me->signal); + spin_lock_irq(&me->sighand->siglock); + posix_cpu_timers_exit(me); + spin_unlock_irq(&me->sighand->siglock); + kpatch_cve_2022_2585_exit_itimers(me); flush_itimer_signals(); #endif diff --git a/kernel/exit.c b/kernel/exit.c index 1731f60de259..d1e618505b7d 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -725,6 +725,7 @@ static void check_stack_usage(void) static inline void check_stack_usage(void) {} #endif +void kpatch_cve_2022_2585_exit_itimers(struct task_struct *tsk); void __noreturn do_exit(long code) { struct task_struct *tsk = current; @@ -797,7 +798,8 @@ void __noreturn do_exit(long code) #ifdef CONFIG_POSIX_TIMERS hrtimer_cancel(&tsk->signal->real_timer); - exit_itimers(tsk->signal); + kpatch_cve_2022_2585_exit_itimers(tsk); + #endif if (tsk->mm) setmax_mm_hiwater_rss(&tsk->signal->maxrss, tsk->mm); diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 1cd10b102c51..8a1f61232282 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -1035,7 +1035,7 @@ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id) /* * return timer owned by the process, used by exit_itimers */ -static void itimer_delete(struct k_itimer *timer) +static __always_inline void itimer_delete(struct k_itimer *timer) { retry_delete: spin_lock_irq(&timer->it_lock); @@ -1050,6 +1050,30 @@ static void itimer_delete(struct k_itimer *timer) release_posix_timer(timer, IT_ID_SET); } +/* + * CVE-2022-2585 - kpatch exit_itimers() redefinition + * This is called by do_exit or de_thread, only when nobody else can + * modify the signal->posix_timers list. Yet we need sighand->siglock + * to prevent the race with /proc/pid/timers. + */ +void kpatch_cve_2022_2585_exit_itimers(struct task_struct *tsk) +{ + struct list_head timers; + struct k_itimer *tmr; + + if (list_empty(&tsk->signal->posix_timers)) + return; + + spin_lock_irq(&tsk->sighand->siglock); + list_replace_init(&tsk->signal->posix_timers, &timers); + spin_unlock_irq(&tsk->sighand->siglock); + + while (!list_empty(&timers)) { + tmr = list_first_entry(&timers, struct k_itimer, list); + itimer_delete(tmr); + } +} + /* * This is called by do_exit or de_thread, only when there are no more * references to the shared signal_struct. -- 2.37.3