diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2020-14351/0001-perf-core-Fix-race-in-the-perf_mmap_close-function.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2020-14351/0001-perf-core-Fix-race-in-the-perf_mmap_close-function.patch | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2020-14351/0001-perf-core-Fix-race-in-the-perf_mmap_close-function.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2020-14351/0001-perf-core-Fix-race-in-the-perf_mmap_close-function.patch new file mode 100644 index 000000000..c5114ac32 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2020-14351/0001-perf-core-Fix-race-in-the-perf_mmap_close-function.patch @@ -0,0 +1,98 @@ +From 4ef4d084d9e38fbf31d40ff0a91bd621720421d9 Mon Sep 17 00:00:00 2001 +From: Jiri Olsa <jolsa@redhat.com> +Date: Wed, 16 Sep 2020 13:53:11 +0200 +Subject: [PATCH] perf/core: Fix race in the perf_mmap_close() function + +There's a possible race in perf_mmap_close() when checking ring buffer's +mmap_count refcount value. The problem is that the mmap_count check is +not atomic because we call atomic_dec() and atomic_read() separately. + + perf_mmap_close: + ... + atomic_dec(&rb->mmap_count); + ... + if (atomic_read(&rb->mmap_count)) + goto out_put; + + <ring buffer detach> + free_uid + +out_put: + ring_buffer_put(rb); /* could be last */ + +The race can happen when we have two (or more) events sharing same ring +buffer and they go through atomic_dec() and then they both see 0 as refcount +value later in atomic_read(). Then both will go on and execute code which +is meant to be run just once. + +The code that detaches ring buffer is probably fine to be executed more +than once, but the problem is in calling free_uid(), which will later on +demonstrate in related crashes and refcount warnings, like: + + refcount_t: addition on 0; use-after-free. + ... + RIP: 0010:refcount_warn_saturate+0x6d/0xf + ... + Call Trace: + prepare_creds+0x190/0x1e0 + copy_creds+0x35/0x172 + copy_process+0x471/0x1a80 + _do_fork+0x83/0x3a0 + __do_sys_wait4+0x83/0x90 + __do_sys_clone+0x85/0xa0 + do_syscall_64+0x5b/0x1e0 + entry_SYSCALL_64_after_hwframe+0x44/0xa9 + +Using atomic decrease and check instead of separated calls. + +Tested-by: Michael Petlan <mpetlan@redhat.com> +Signed-off-by: Jiri Olsa <jolsa@kernel.org> +Signed-off-by: Ingo Molnar <mingo@kernel.org> +Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl> +Acked-by: Namhyung Kim <namhyung@kernel.org> +Acked-by: Wade Mealing <wmealing@redhat.com> +Fixes: 9bb5d40cd93c ("perf: Fix mmap() accounting hole"); +Link: https://lore.kernel.org/r/20200916115311.GE2301783@krava +--- + kernel/events/core.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/kernel/events/core.c b/kernel/events/core.c +index 956d5ab46db9..1d5f2806c65e 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -5587,11 +5587,11 @@ static void perf_pmu_output_stop(struct perf_event *event); + static void perf_mmap_close(struct vm_area_struct *vma) + { + struct perf_event *event = vma->vm_file->private_data; +- + struct ring_buffer *rb = ring_buffer_get(event); + struct user_struct *mmap_user = rb->mmap_user; + int mmap_locked = rb->mmap_locked; + unsigned long size = perf_data_size(rb); ++ bool detach_rest = false; + + if (event->pmu->event_unmapped) + event->pmu->event_unmapped(event, vma->vm_mm); +@@ -5622,7 +5622,8 @@ static void perf_mmap_close(struct vm_area_struct *vma) + mutex_unlock(&event->mmap_mutex); + } + +- atomic_dec(&rb->mmap_count); ++ if (atomic_dec_and_test(&rb->mmap_count)) ++ detach_rest = true; + + if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) + goto out_put; +@@ -5631,7 +5632,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) + mutex_unlock(&event->mmap_mutex); + + /* If there's still other mmap()s of this buffer, we're done. */ +- if (atomic_read(&rb->mmap_count)) ++ if (!detach_rest) + goto out_put; + + /* +-- +2.17.1 + |