summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch161
1 files changed, 161 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch
new file mode 100644
index 000000000..7bed5bc46
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2024-26686.patch
@@ -0,0 +1,161 @@
+From 27978243f165b44e342f28f449b91327944ea071 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Tue, 23 Jan 2024 16:33:57 +0100
+Subject: [PATCH] fs/proc: do_task_stat: use sig->stats_lock to gather the
+ threads/children stats
+
+commit 7601df8031fd67310af891897ef6cc0df4209305 upstream.
+
+lock_task_sighand() can trigger a hard lockup. If NR_CPUS threads call
+do_task_stat() at the same time and the process has NR_THREADS, it will
+spin with irqs disabled O(NR_CPUS * NR_THREADS) time.
+
+Change do_task_stat() to use sig->stats_lock to gather the statistics
+outside of ->siglock protected section, in the likely case this code will
+run lockless.
+
+Link: https://lkml.kernel.org/r/20240123153357.GA21857@redhat.com
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Signed-off-by: Dylan Hatch <dylanbhatch@google.com>
+Cc: Eric W. Biederman <ebiederm@xmission.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/array.c | 76 ++++++++++++++++++++++++++++---------------------
+ 1 file changed, 43 insertions(+), 33 deletions(-)
+
+diff --git a/fs/proc/array.c b/fs/proc/array.c
+index 49be8c8ef555..c6b840e25151 100644
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -462,12 +462,13 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ int permitted;
+ struct mm_struct *mm;
+ unsigned long long start_time;
+- unsigned long cmin_flt = 0, cmaj_flt = 0;
+- unsigned long min_flt = 0, maj_flt = 0;
+- u64 cutime, cstime, utime, stime;
+- u64 cgtime, gtime;
++ unsigned long cmin_flt, cmaj_flt, min_flt, maj_flt;
++ u64 cutime, cstime, cgtime, utime, stime, gtime;
+ unsigned long rsslim = 0;
+ unsigned long flags;
++ int exit_code = task->exit_code;
++ struct signal_struct *sig = task->signal;
++ unsigned int seq = 1;
+
+ state = *get_task_state(task);
+ vsize = eip = esp = 0;
+@@ -495,12 +496,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+
+ sigemptyset(&sigign);
+ sigemptyset(&sigcatch);
+- cutime = cstime = utime = stime = 0;
+- cgtime = gtime = 0;
+
+ if (lock_task_sighand(task, &flags)) {
+- struct signal_struct *sig = task->signal;
+-
+ if (sig->tty) {
+ struct pid *pgrp = tty_get_pgrp(sig->tty);
+ tty_pgrp = pid_nr_ns(pgrp, ns);
+@@ -511,26 +508,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ num_threads = get_nr_threads(task);
+ collect_sigign_sigcatch(task, &sigign, &sigcatch);
+
+- cmin_flt = sig->cmin_flt;
+- cmaj_flt = sig->cmaj_flt;
+- cutime = sig->cutime;
+- cstime = sig->cstime;
+- cgtime = sig->cgtime;
+ rsslim = READ_ONCE(sig->rlim[RLIMIT_RSS].rlim_cur);
+
+- /* add up live thread stats at the group level */
+ if (whole) {
+- struct task_struct *t = task;
+- do {
+- min_flt += t->min_flt;
+- maj_flt += t->maj_flt;
+- gtime += task_gtime(t);
+- } while_each_thread(task, t);
+-
+- min_flt += sig->min_flt;
+- maj_flt += sig->maj_flt;
+- thread_group_cputime_adjusted(task, &utime, &stime);
+- gtime += sig->gtime;
++ if (sig->flags & (SIGNAL_GROUP_EXIT | SIGNAL_STOP_STOPPED))
++ exit_code = sig->group_exit_code;
+ }
+
+ sid = task_session_nr_ns(task, ns);
+@@ -541,11 +523,42 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ }
+
+ if (permitted && (!whole || num_threads < 2))
+- wchan = get_wchan(task);
+- if (!whole) {
++ wchan = !task_is_running(task);
++
++ do {
++ seq++; /* 2 on the 1st/lockless path, otherwise odd */
++ flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq);
++
++ cmin_flt = sig->cmin_flt;
++ cmaj_flt = sig->cmaj_flt;
++ cutime = sig->cutime;
++ cstime = sig->cstime;
++ cgtime = sig->cgtime;
++
++ if (whole) {
++ struct task_struct *t;
++
++ min_flt = sig->min_flt;
++ maj_flt = sig->maj_flt;
++ gtime = sig->gtime;
++
++ rcu_read_lock();
++ __for_each_thread(sig, t) {
++ min_flt += t->min_flt;
++ maj_flt += t->maj_flt;
++ gtime += task_gtime(t);
++ }
++ rcu_read_unlock();
++ }
++ } while (need_seqretry(&sig->stats_lock, seq));
++ done_seqretry_irqrestore(&sig->stats_lock, seq, flags);
++
++ if (whole) {
++ thread_group_cputime_adjusted(task, &utime, &stime);
++ } else {
++ task_cputime_adjusted(task, &utime, &stime);
+ min_flt = task->min_flt;
+ maj_flt = task->maj_flt;
+- task_cputime_adjusted(task, &utime, &stime);
+ gtime = task_gtime(task);
+ }
+
+@@ -606,10 +619,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ *
+ * This works with older implementations of procps as well.
+ */
+- if (wchan)
+- seq_puts(m, " 1");
+- else
+- seq_puts(m, " 0");
++ seq_put_decimal_ull(m, " ", wchan);
+
+ seq_put_decimal_ull(m, " ", 0);
+ seq_put_decimal_ull(m, " ", 0);
+@@ -633,7 +643,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
+ seq_puts(m, " 0 0 0 0 0 0 0");
+
+ if (permitted)
+- seq_put_decimal_ll(m, " ", task->exit_code);
++ seq_put_decimal_ll(m, " ", exit_code);
+ else
+ seq_puts(m, " 0");
+
+--
+2.25.1
+