diff options
author | P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> | 2022-12-02 20:53:31 +0300 |
---|---|---|
committer | P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com> | 2022-12-02 21:09:30 +0300 |
commit | 7dd3ed26ca09df0e582be8cc2780bba588bdd11e (patch) | |
tree | ee5b64acbe5374240089bc65c9443dd29df482f8 /meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch | |
parent | e0c224c79550bf49928bfb75f629233b1ef07c7a (diff) | |
download | openbmc-7dd3ed26ca09df0e582be8cc2780bba588bdd11e.tar.xz |
Update to internal 1-0.92
Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch new file mode 100644 index 000000000..5f231cfe4 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2022-2938.patch @@ -0,0 +1,249 @@ +From d3e4c61e143e69671803ef3f52140cf7a7258ee7 Mon Sep 17 00:00:00 2001 +From: Suren Baghdasaryan <surenb@google.com> +Date: Tue, 11 Jan 2022 15:23:09 -0800 +Subject: [PATCH] psi: Fix uaf issue when psi trigger is destroyed while being + polled + +commit a06247c6804f1a7c86a2e5398a4c1f1db1471848 upstream. + +With write operation on psi files replacing old trigger with a new one, +the lifetime of its waitqueue is totally arbitrary. Overwriting an +existing trigger causes its waitqueue to be freed and pending poll() +will stumble on trigger->event_wait which was destroyed. +Fix this by disallowing to redefine an existing psi trigger. If a write +operation is used on a file descriptor with an already existing psi +trigger, the operation will fail with EBUSY error. +Also bypass a check for psi_disabled in the psi_trigger_destroy as the +flag can be flipped after the trigger is created, leading to a memory +leak. + +Fixes: 0e94682b73bf ("psi: introduce psi monitor") +Reported-by: syzbot+cdb5dd11c97cc532efad@syzkaller.appspotmail.com +Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> +Analyzed-by: Eric Biggers <ebiggers@kernel.org> +Signed-off-by: Suren Baghdasaryan <surenb@google.com> +Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> +Reviewed-by: Eric Biggers <ebiggers@google.com> +Acked-by: Johannes Weiner <hannes@cmpxchg.org> +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/20220111232309.1786347-1-surenb@google.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + Documentation/accounting/psi.rst | 3 +- + include/linux/psi.h | 2 +- + include/linux/psi_types.h | 3 -- + kernel/cgroup/cgroup.c | 11 ++++-- + kernel/sched/psi.c | 66 ++++++++++++++------------------ + 5 files changed, 40 insertions(+), 45 deletions(-) + +diff --git a/Documentation/accounting/psi.rst b/Documentation/accounting/psi.rst +index f2b3439edcc2cc..860fe651d6453e 100644 +--- a/Documentation/accounting/psi.rst ++++ b/Documentation/accounting/psi.rst +@@ -92,7 +92,8 @@ Triggers can be set on more than one psi metric and more than one trigger + for the same psi metric can be specified. However for each trigger a separate + file descriptor is required to be able to poll it separately from others, + therefore for each trigger a separate open() syscall should be made even +-when opening the same psi interface file. ++when opening the same psi interface file. Write operations to a file descriptor ++with an already existing psi trigger will fail with EBUSY. + + Monitors activate only when system enters stall state for the monitored + psi metric and deactivates upon exit from the stall state. While system is +diff --git a/include/linux/psi.h b/include/linux/psi.h +index 65eb1476ac7056..74f7148dfb9f4c 100644 +--- a/include/linux/psi.h ++++ b/include/linux/psi.h +@@ -32,7 +32,7 @@ void cgroup_move_task(struct task_struct *p, struct css_set *to); + + struct psi_trigger *psi_trigger_create(struct psi_group *group, + char *buf, size_t nbytes, enum psi_res res); +-void psi_trigger_replace(void **trigger_ptr, struct psi_trigger *t); ++void psi_trigger_destroy(struct psi_trigger *t); + + __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file, + poll_table *wait); +diff --git a/include/linux/psi_types.h b/include/linux/psi_types.h +index 0819c82dba920a..6f190002a20224 100644 +--- a/include/linux/psi_types.h ++++ b/include/linux/psi_types.h +@@ -140,9 +140,6 @@ struct psi_trigger { + * events to one per window + */ + u64 last_event_time; +- +- /* Refcounting to prevent premature destruction */ +- struct kref refcount; + }; + + struct psi_group { +diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c +index bb1a78ff14374e..de8b4fa1e1fd68 100644 +--- a/kernel/cgroup/cgroup.c ++++ b/kernel/cgroup/cgroup.c +@@ -3642,6 +3642,12 @@ static ssize_t cgroup_pressure_write(struct kernfs_open_file *of, char *buf, + cgroup_get(cgrp); + cgroup_kn_unlock(of->kn); + ++ /* Allow only one trigger per file descriptor */ ++ if (ctx->psi.trigger) { ++ cgroup_put(cgrp); ++ return -EBUSY; ++ } ++ + psi = cgroup_ino(cgrp) == 1 ? &psi_system : &cgrp->psi; + new = psi_trigger_create(psi, buf, nbytes, res); + if (IS_ERR(new)) { +@@ -3649,8 +3655,7 @@ static ssize_t cgroup_pressure_write(struct kernfs_open_file *of, char *buf, + return PTR_ERR(new); + } + +- psi_trigger_replace(&ctx->psi.trigger, new); +- ++ smp_store_release(&ctx->psi.trigger, new); + cgroup_put(cgrp); + + return nbytes; +@@ -3689,7 +3694,7 @@ static void cgroup_pressure_release(struct kernfs_open_file *of) + { + struct cgroup_file_ctx *ctx = of->priv; + +- psi_trigger_replace(&ctx->psi.trigger, NULL); ++ psi_trigger_destroy(ctx->psi.trigger); + } + + bool cgroup_psi_enabled(void) +diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c +index 69b19d3af690ff..ab460f8b77d13a 100644 +--- a/kernel/sched/psi.c ++++ b/kernel/sched/psi.c +@@ -1162,7 +1162,6 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, + t->event = 0; + t->last_event_time = 0; + init_waitqueue_head(&t->event_wait); +- kref_init(&t->refcount); + + mutex_lock(&group->trigger_lock); + +@@ -1191,15 +1190,19 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, + return t; + } + +-static void psi_trigger_destroy(struct kref *ref) ++void psi_trigger_destroy(struct psi_trigger *t) + { +- struct psi_trigger *t = container_of(ref, struct psi_trigger, refcount); +- struct psi_group *group = t->group; ++ struct psi_group *group; + struct task_struct *task_to_destroy = NULL; + +- if (static_branch_likely(&psi_disabled)) ++ /* ++ * We do not check psi_disabled since it might have been disabled after ++ * the trigger got created. ++ */ ++ if (!t) + return; + ++ group = t->group; + /* + * Wakeup waiters to stop polling. Can happen if cgroup is deleted + * from under a polling process. +@@ -1235,9 +1238,9 @@ static void psi_trigger_destroy(struct kref *ref) + mutex_unlock(&group->trigger_lock); + + /* +- * Wait for both *trigger_ptr from psi_trigger_replace and +- * poll_task RCUs to complete their read-side critical sections +- * before destroying the trigger and optionally the poll_task ++ * Wait for psi_schedule_poll_work RCU to complete its read-side ++ * critical section before destroying the trigger and optionally the ++ * poll_task. + */ + synchronize_rcu(); + /* +@@ -1254,18 +1257,6 @@ static void psi_trigger_destroy(struct kref *ref) + kfree(t); + } + +-void psi_trigger_replace(void **trigger_ptr, struct psi_trigger *new) +-{ +- struct psi_trigger *old = *trigger_ptr; +- +- if (static_branch_likely(&psi_disabled)) +- return; +- +- rcu_assign_pointer(*trigger_ptr, new); +- if (old) +- kref_put(&old->refcount, psi_trigger_destroy); +-} +- + __poll_t psi_trigger_poll(void **trigger_ptr, + struct file *file, poll_table *wait) + { +@@ -1275,24 +1266,15 @@ __poll_t psi_trigger_poll(void **trigger_ptr, + if (static_branch_likely(&psi_disabled)) + return DEFAULT_POLLMASK | EPOLLERR | EPOLLPRI; + +- rcu_read_lock(); +- +- t = rcu_dereference(*(void __rcu __force **)trigger_ptr); +- if (!t) { +- rcu_read_unlock(); ++ t = smp_load_acquire(trigger_ptr); ++ if (!t) + return DEFAULT_POLLMASK | EPOLLERR | EPOLLPRI; +- } +- kref_get(&t->refcount); +- +- rcu_read_unlock(); + + poll_wait(file, &t->event_wait, wait); + + if (cmpxchg(&t->event, 1, 0) == 1) + ret |= EPOLLPRI; + +- kref_put(&t->refcount, psi_trigger_destroy); +- + return ret; + } + +@@ -1316,14 +1298,24 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, + + buf[buf_size - 1] = '\0'; + +- new = psi_trigger_create(&psi_system, buf, nbytes, res); +- if (IS_ERR(new)) +- return PTR_ERR(new); +- + seq = file->private_data; ++ + /* Take seq->lock to protect seq->private from concurrent writes */ + mutex_lock(&seq->lock); +- psi_trigger_replace(&seq->private, new); ++ ++ /* Allow only one trigger per file descriptor */ ++ if (seq->private) { ++ mutex_unlock(&seq->lock); ++ return -EBUSY; ++ } ++ ++ new = psi_trigger_create(&psi_system, buf, nbytes, res); ++ if (IS_ERR(new)) { ++ mutex_unlock(&seq->lock); ++ return PTR_ERR(new); ++ } ++ ++ smp_store_release(&seq->private, new); + mutex_unlock(&seq->lock); + + return nbytes; +@@ -1358,7 +1350,7 @@ static int psi_fop_release(struct inode *inode, struct file *file) + { + struct seq_file *seq = file->private_data; + +- psi_trigger_replace(&seq->private, NULL); ++ psi_trigger_destroy(seq->private); + return single_release(inode, file); + } + |