• R/O
  • HTTP
  • SSH
  • HTTPS

BrynKernel-AOSP: 提交

https://bryn-lab.my.id/kernel.html


Commit MetaInfo

修订版5a097d643717160d859f5bd4a29e2088f48a5fd3 (tree)
时间2020-11-19 02:26:31
作者Jiri Olsa <jolsa@redh...>
CommiterGreg Kroah-Hartman

Log Message

perf/core: Fix race in the perf_mmap_close() function

commit f91072ed1b7283b13ca57fcfbece5a3b92726143 upstream.

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
[sudip: backport to v4.9.y by using ring_buffer]
Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

更改概述

差异

--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5069,11 +5069,11 @@ static void perf_pmu_output_stop(struct perf_event *event);
50695069 static void perf_mmap_close(struct vm_area_struct *vma)
50705070 {
50715071 struct perf_event *event = vma->vm_file->private_data;
5072-
50735072 struct ring_buffer *rb = ring_buffer_get(event);
50745073 struct user_struct *mmap_user = rb->mmap_user;
50755074 int mmap_locked = rb->mmap_locked;
50765075 unsigned long size = perf_data_size(rb);
5076+ bool detach_rest = false;
50775077
50785078 if (event->pmu->event_unmapped)
50795079 event->pmu->event_unmapped(event);
@@ -5104,7 +5104,8 @@ static void perf_mmap_close(struct vm_area_struct *vma)
51045104 mutex_unlock(&event->mmap_mutex);
51055105 }
51065106
5107- atomic_dec(&rb->mmap_count);
5107+ if (atomic_dec_and_test(&rb->mmap_count))
5108+ detach_rest = true;
51085109
51095110 if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex))
51105111 goto out_put;
@@ -5113,7 +5114,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
51135114 mutex_unlock(&event->mmap_mutex);
51145115
51155116 /* If there's still other mmap()s of this buffer, we're done. */
5116- if (atomic_read(&rb->mmap_count))
5117+ if (!detach_rest)
51175118 goto out_put;
51185119
51195120 /*
Show on old repository browser