kernel/events/core.c | 9 +++++++++ 1 file changed, 9 insertions(+)
When calling refcount_inc(&event->mmap_count) inside perf_mmap_rb(), the
following warning is triggered:
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25
PoC:
struct perf_event_attr attr = {0};
int fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int victim = syscall(__NR_perf_event_open, &attr, 0, -1, fd,
PERF_FLAG_FD_OUTPUT);
mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, victim, 0);
This occurs when creating a group member event with the flag
PERF_FLAG_FD_OUTPUT. The group leader should be mmap-ed and then mmap-ing
the event triggers the warning.
Since the event has copied the output_event in perf_event_set_output(),
event->rb is set. As a result, perf_mmap_rb() calls
refcount_inc(&event->mmap_count) when event->mmap_count = 0.
Disallow the case when event->mmap_count = 0. This also prevents two
events from updating the same user_page.
Fixes: 448f97fba901 ("perf: Convert mmap() refcounts to refcount_t")
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Will Rosenberg <whrosenb@asu.edu>
---
Notes:
v2 -> v3: Update patch to error out instead of incrementing.
Thank you, this is a much better solution. I was not thinking
that the mmap itself was unintended.
I believe you are missing a "!" in your patch. After adding
that, I tested the patch, and it fixed the bug.
I also wanted to check my understanding of the race with
perf_mmap_close() to double check this patch will not cause
an issue. perf_mmap_rb() should always hold the
event->mmap_mutex, so there should be no race on
event->mmap_count with perf_mmap_close()'s
refcount_dec_and_mutex_lock(). If there was a race, we would
risk returning -EBUSY when we should "continue as if !event->rb."
Thank you for your help.
kernel/events/core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 3c2a491200c6..ac7f12560172 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7273,6 +7273,15 @@ static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event,
if (data_page_nr(event->rb) != nr_pages)
return -EINVAL;
+ /*
+ * If this event doesn't have mmap_count, we're attempting to
+ * create an alias of another event's mmap(); this would mean
+ * both events will end up scribbling the same user_page;
+ * which makes no sense.
+ */
+ if (!refcount_read(&event->mmap_count))
+ return -EBUSY;
+
if (refcount_inc_not_zero(&event->rb->mmap_count)) {
/*
* Success -- managed to mmap() the same buffer
base-commit: 5d3b0106245d467fd5ba0bd9a373a13356684f6e
--
2.34.1
On Mon, Jan 19, 2026 at 11:49:56AM -0700, Will Rosenberg wrote: > > Notes: > v2 -> v3: Update patch to error out instead of incrementing. > > Thank you, this is a much better solution. I was not thinking > that the mmap itself was unintended. > > I believe you are missing a "!" in your patch. After adding > that, I tested the patch, and it fixed the bug. D'0h indeed. Sometimes typing is so very hard ;-) > I also wanted to check my understanding of the race with > perf_mmap_close() to double check this patch will not cause > an issue. perf_mmap_rb() should always hold the > event->mmap_mutex, so there should be no race on > event->mmap_count with perf_mmap_close()'s > refcount_dec_and_mutex_lock(). If there was a race, we would > risk returning -EBUSY when we should "continue as if !event->rb." Since we're failing perf_mmap_rb(), it won't call ->close(), right? Also, we already have an error path on data_page_nr() mismatch. The caller of perf_mmap_rb() has if (ret) return ret; nothing is modified before calling perf_mmap_rb() and perf_mmap_rb() itself hasn't modified anytyhing yet at the point of failure. So afaict we're good. Anyway, thanks for the patch, I'll get it applied!
The following commit has been merged into the perf/urgent branch of tip:
Commit-ID: d06bf78e55d5159c1b00072e606ab924ffbbad35
Gitweb: https://git.kernel.org/tip/d06bf78e55d5159c1b00072e606ab924ffbbad35
Author: Will Rosenberg <whrosenb@asu.edu>
AuthorDate: Mon, 19 Jan 2026 11:49:56 -07:00
Committer: Peter Zijlstra <peterz@infradead.org>
CommitterDate: Wed, 21 Jan 2026 16:28:58 +01:00
perf: Fix refcount warning on event->mmap_count increment
When calling refcount_inc(&event->mmap_count) inside perf_mmap_rb(), the
following warning is triggered:
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25
PoC:
struct perf_event_attr attr = {0};
int fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int victim = syscall(__NR_perf_event_open, &attr, 0, -1, fd,
PERF_FLAG_FD_OUTPUT);
mmap(NULL, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED, victim, 0);
This occurs when creating a group member event with the flag
PERF_FLAG_FD_OUTPUT. The group leader should be mmap-ed and then mmap-ing
the event triggers the warning.
Since the event has copied the output_event in perf_event_set_output(),
event->rb is set. As a result, perf_mmap_rb() calls
refcount_inc(&event->mmap_count) when event->mmap_count = 0.
Disallow the case when event->mmap_count = 0. This also prevents two
events from updating the same user_page.
Fixes: 448f97fba901 ("perf: Convert mmap() refcounts to refcount_t")
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Will Rosenberg <whrosenb@asu.edu>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260119184956.801238-1-whrosenb@asu.edu
---
kernel/events/core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 5b5cb62..a0fa488 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6997,6 +6997,15 @@ static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event,
if (data_page_nr(event->rb) != nr_pages)
return -EINVAL;
+ /*
+ * If this event doesn't have mmap_count, we're attempting to
+ * create an alias of another event's mmap(); this would mean
+ * both events will end up scribbling the same user_page;
+ * which makes no sense.
+ */
+ if (!refcount_read(&event->mmap_count))
+ return -EBUSY;
+
if (refcount_inc_not_zero(&event->rb->mmap_count)) {
/*
* Success -- managed to mmap() the same buffer
© 2016 - 2026 Red Hat, Inc.