From: Greg Kroah-Hartman Date: Tue, 12 Aug 2025 10:20:55 +0000 (+0200) Subject: 6.1-stable patches X-Git-Tag: v6.1.148~51 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9a9379c7a8a5bc12ea0c87ad701d9f9fde7f1423;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches added patches: perf-core-don-t-leak-aux-buffer-refcount-on-allocation-failure.patch perf-core-exit-early-on-perf_mmap-fail.patch perf-core-prevent-vma-split-of-buffer-mappings.patch selftests-perf_events-add-a-mmap-correctness-test.patch --- diff --git a/queue-6.1/perf-core-don-t-leak-aux-buffer-refcount-on-allocation-failure.patch b/queue-6.1/perf-core-don-t-leak-aux-buffer-refcount-on-allocation-failure.patch new file mode 100644 index 0000000000..4f2087d6fa --- /dev/null +++ b/queue-6.1/perf-core-don-t-leak-aux-buffer-refcount-on-allocation-failure.patch @@ -0,0 +1,54 @@ +From a94b7abacb2029166b30bd98b43b1b106dae8e8a Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Sat, 2 Aug 2025 12:39:39 +0200 +Subject: perf/core: Don't leak AUX buffer refcount on allocation failure + +From: Thomas Gleixner + +commit 5468c0fbccbb9d156522c50832244a8b722374fb upstream. + +Failure of the AUX buffer allocation leaks the reference count. + +Set the reference count to 1 only when the allocation succeeds. + +Fixes: 45bfb2e50471 ("perf/core: Add AUX area to ring buffer for raw data streams") +Signed-off-by: Thomas Gleixner +Reviewed-by: Lorenzo Stoakes +Cc: stable@vger.kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + kernel/events/core.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -6381,9 +6381,7 @@ static int perf_mmap(struct file *file, + goto unlock; + } + +- atomic_set(&rb->aux_mmap_count, 1); + user_extra = nr_pages; +- + goto accounting; + } + +@@ -6485,8 +6483,10 @@ accounting: + } else { + ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages, + event->attr.aux_watermark, flags); +- if (!ret) ++ if (!ret) { ++ atomic_set(&rb->aux_mmap_count, 1); + rb->aux_mmap_locked = extra; ++ } + } + + unlock: +@@ -6496,6 +6496,7 @@ unlock: + + atomic_inc(&event->mmap_count); + } else if (rb) { ++ /* AUX allocation failed */ + atomic_dec(&rb->mmap_count); + } + aux_unlock: diff --git a/queue-6.1/perf-core-exit-early-on-perf_mmap-fail.patch b/queue-6.1/perf-core-exit-early-on-perf_mmap-fail.patch new file mode 100644 index 0000000000..831495ec5c --- /dev/null +++ b/queue-6.1/perf-core-exit-early-on-perf_mmap-fail.patch @@ -0,0 +1,37 @@ +From 74493399bd1f10c03e197254fec7e2af24731d49 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Sat, 2 Aug 2025 12:49:48 +0200 +Subject: perf/core: Exit early on perf_mmap() fail + +From: Thomas Gleixner + +commit 07091aade394f690e7b655578140ef84d0e8d7b0 upstream. + +When perf_mmap() fails to allocate a buffer, it still invokes the +event_mapped() callback of the related event. On X86 this might increase +the perf_rdpmc_allowed reference counter. But nothing undoes this as +perf_mmap_close() is never called in this case, which causes another +reference count leak. + +Return early on failure to prevent that. + +Fixes: 1e0fb9ec679c ("perf/core: Add pmu callbacks to track event mapping and unmapping") +Signed-off-by: Thomas Gleixner +Reviewed-by: Lorenzo Stoakes Cc: stable@vger.kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + kernel/events/core.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -6504,6 +6504,9 @@ aux_unlock: + mutex_unlock(aux_mutex); + mutex_unlock(&event->mmap_mutex); + ++ if (ret) ++ return ret; ++ + /* + * Since pinned accounting is per vm we cannot allow fork() to copy our + * vma. diff --git a/queue-6.1/perf-core-prevent-vma-split-of-buffer-mappings.patch b/queue-6.1/perf-core-prevent-vma-split-of-buffer-mappings.patch new file mode 100644 index 0000000000..46bec36b56 --- /dev/null +++ b/queue-6.1/perf-core-prevent-vma-split-of-buffer-mappings.patch @@ -0,0 +1,69 @@ +From 650d471664e56e4099c838d0f8978a533996bad1 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Wed, 30 Jul 2025 23:01:21 +0200 +Subject: perf/core: Prevent VMA split of buffer mappings + +From: Thomas Gleixner + +commit b024d7b56c77191cde544f838debb7f8451cd0d6 upstream. + +The perf mmap code is careful about mmap()'ing the user page with the +ringbuffer and additionally the auxiliary buffer, when the event supports +it. Once the first mapping is established, subsequent mapping have to use +the same offset and the same size in both cases. The reference counting for +the ringbuffer and the auxiliary buffer depends on this being correct. + +Though perf does not prevent that a related mapping is split via mmap(2), +munmap(2) or mremap(2). A split of a VMA results in perf_mmap_open() calls, +which take reference counts, but then the subsequent perf_mmap_close() +calls are not longer fulfilling the offset and size checks. This leads to +reference count leaks. + +As perf already has the requirement for subsequent mappings to match the +initial mapping, the obvious consequence is that VMA splits, caused by +resizing of a mapping or partial unmapping, have to be prevented. + +Implement the vm_operations_struct::may_split() callback and return +unconditionally -EINVAL. + +That ensures that the mapping offsets and sizes cannot be changed after the +fact. Remapping to a different fixed address with the same size is still +possible as it takes the references for the new mapping and drops those of +the old mapping. + +Fixes: 45bfb2e50471 ("perf/core: Add AUX area to ring buffer for raw data streams") +Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-27504 +Signed-off-by: Thomas Gleixner +Reviewed-by: Lorenzo Stoakes +Acked-by: Arnaldo Carvalho de Melo +Acked-by: Vlastimil Babka +Cc: stable@vger.kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + kernel/events/core.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -6285,11 +6285,21 @@ out_put: + ring_buffer_put(rb); /* could be last */ + } + ++static int perf_mmap_may_split(struct vm_area_struct *vma, unsigned long addr) ++{ ++ /* ++ * Forbid splitting perf mappings to prevent refcount leaks due to ++ * the resulting non-matching offsets and sizes. See open()/close(). ++ */ ++ return -EINVAL; ++} ++ + static const struct vm_operations_struct perf_mmap_vmops = { + .open = perf_mmap_open, + .close = perf_mmap_close, /* non mergeable */ + .fault = perf_mmap_fault, + .page_mkwrite = perf_mmap_fault, ++ .may_split = perf_mmap_may_split, + }; + + static int perf_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/queue-6.1/selftests-perf_events-add-a-mmap-correctness-test.patch b/queue-6.1/selftests-perf_events-add-a-mmap-correctness-test.patch new file mode 100644 index 0000000000..80c1b892a1 --- /dev/null +++ b/queue-6.1/selftests-perf_events-add-a-mmap-correctness-test.patch @@ -0,0 +1,292 @@ +From 9f90d60eb3247b74f31ff599fafc73b6456eedc1 Mon Sep 17 00:00:00 2001 +From: Lorenzo Stoakes +Date: Sat, 2 Aug 2025 22:55:35 +0200 +Subject: selftests/perf_events: Add a mmap() correctness test + +From: Lorenzo Stoakes + +commit 084d2ac4030c5919e85bba1f4af26e33491469cb upstream. + +Exercise various mmap(), munmap() and mremap() invocations, which might +cause a perf buffer mapping to be split or truncated. + +To avoid hard coding the perf event and having dependencies on +architectures and configuration options, scan through event types in sysfs +and try to open them. On success, try to mmap() and if that succeeds try to +mmap() the AUX buffer. + +In case that no AUX buffer supporting event is found, only test the base +buffer mapping. If no mappable event is found or permissions are not +sufficient, skip the tests. + +Reserve a PROT_NONE region for both rb and aux tests to allow testing the +case where mremap unmaps beyond the end of a mapped VMA to prevent it from +unmapping unrelated mappings. + +Signed-off-by: Lorenzo Stoakes +Co-developed-by: Thomas Gleixner +Signed-off-by: Thomas Gleixner +Reviewed-by: Lorenzo Stoakes +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/perf_events/.gitignore | 1 + tools/testing/selftests/perf_events/Makefile | 2 + tools/testing/selftests/perf_events/mmap.c | 236 +++++++++++++++++++++++++ + 3 files changed, 238 insertions(+), 1 deletion(-) + create mode 100644 tools/testing/selftests/perf_events/mmap.c + +--- a/tools/testing/selftests/perf_events/.gitignore ++++ b/tools/testing/selftests/perf_events/.gitignore +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + sigtrap_threads + remove_on_exec ++mmap +--- a/tools/testing/selftests/perf_events/Makefile ++++ b/tools/testing/selftests/perf_events/Makefile +@@ -2,5 +2,5 @@ + CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES) + LDFLAGS += -lpthread + +-TEST_GEN_PROGS := sigtrap_threads remove_on_exec ++TEST_GEN_PROGS := sigtrap_threads remove_on_exec mmap + include ../lib.mk +--- /dev/null ++++ b/tools/testing/selftests/perf_events/mmap.c +@@ -0,0 +1,236 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "../kselftest_harness.h" ++ ++#define RB_SIZE 0x3000 ++#define AUX_SIZE 0x10000 ++#define AUX_OFFS 0x4000 ++ ++#define HOLE_SIZE 0x1000 ++ ++/* Reserve space for rb, aux with space for shrink-beyond-vma testing. */ ++#define REGION_SIZE (2 * RB_SIZE + 2 * AUX_SIZE) ++#define REGION_AUX_OFFS (2 * RB_SIZE) ++ ++#define MAP_BASE 1 ++#define MAP_AUX 2 ++ ++#define EVENT_SRC_DIR "/sys/bus/event_source/devices" ++ ++FIXTURE(perf_mmap) ++{ ++ int fd; ++ void *ptr; ++ void *region; ++}; ++ ++FIXTURE_VARIANT(perf_mmap) ++{ ++ bool aux; ++ unsigned long ptr_size; ++}; ++ ++FIXTURE_VARIANT_ADD(perf_mmap, rb) ++{ ++ .aux = false, ++ .ptr_size = RB_SIZE, ++}; ++ ++FIXTURE_VARIANT_ADD(perf_mmap, aux) ++{ ++ .aux = true, ++ .ptr_size = AUX_SIZE, ++}; ++ ++static bool read_event_type(struct dirent *dent, __u32 *type) ++{ ++ char typefn[512]; ++ FILE *fp; ++ int res; ++ ++ snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name); ++ fp = fopen(typefn, "r"); ++ if (!fp) ++ return false; ++ ++ res = fscanf(fp, "%u", type); ++ fclose(fp); ++ return res > 0; ++} ++ ++FIXTURE_SETUP(perf_mmap) ++{ ++ struct perf_event_attr attr = { ++ .size = sizeof(attr), ++ .disabled = 1, ++ .exclude_kernel = 1, ++ .exclude_hv = 1, ++ }; ++ struct perf_event_attr attr_ok = {}; ++ unsigned int eacces = 0, map = 0; ++ struct perf_event_mmap_page *rb; ++ struct dirent *dent; ++ void *aux, *region; ++ DIR *dir; ++ ++ self->ptr = NULL; ++ ++ dir = opendir(EVENT_SRC_DIR); ++ if (!dir) ++ SKIP(return, "perf not available."); ++ ++ region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); ++ ASSERT_NE(region, MAP_FAILED); ++ self->region = region; ++ ++ // Try to find a suitable event on this system ++ while ((dent = readdir(dir))) { ++ int fd; ++ ++ if (!read_event_type(dent, &attr.type)) ++ continue; ++ ++ fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0); ++ if (fd < 0) { ++ if (errno == EACCES) ++ eacces++; ++ continue; ++ } ++ ++ // Check whether the event supports mmap() ++ rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); ++ if (rb == MAP_FAILED) { ++ close(fd); ++ continue; ++ } ++ ++ if (!map) { ++ // Save the event in case that no AUX capable event is found ++ attr_ok = attr; ++ map = MAP_BASE; ++ } ++ ++ if (!variant->aux) ++ continue; ++ ++ rb->aux_offset = AUX_OFFS; ++ rb->aux_size = AUX_SIZE; ++ ++ // Check whether it supports a AUX buffer ++ aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE, ++ MAP_SHARED | MAP_FIXED, fd, AUX_OFFS); ++ if (aux == MAP_FAILED) { ++ munmap(rb, RB_SIZE); ++ close(fd); ++ continue; ++ } ++ ++ attr_ok = attr; ++ map = MAP_AUX; ++ munmap(aux, AUX_SIZE); ++ munmap(rb, RB_SIZE); ++ close(fd); ++ break; ++ } ++ closedir(dir); ++ ++ if (!map) { ++ if (!eacces) ++ SKIP(return, "No mappable perf event found."); ++ else ++ SKIP(return, "No permissions for perf_event_open()"); ++ } ++ ++ self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0); ++ ASSERT_NE(self->fd, -1); ++ ++ rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0); ++ ASSERT_NE(rb, MAP_FAILED); ++ ++ if (!variant->aux) { ++ self->ptr = rb; ++ return; ++ } ++ ++ if (map != MAP_AUX) ++ SKIP(return, "No AUX event found."); ++ ++ rb->aux_offset = AUX_OFFS; ++ rb->aux_size = AUX_SIZE; ++ aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE, ++ MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS); ++ ASSERT_NE(aux, MAP_FAILED); ++ self->ptr = aux; ++} ++ ++FIXTURE_TEARDOWN(perf_mmap) ++{ ++ ASSERT_EQ(munmap(self->region, REGION_SIZE), 0); ++ if (self->fd != -1) ++ ASSERT_EQ(close(self->fd), 0); ++} ++ ++TEST_F(perf_mmap, remap) ++{ ++ void *tmp, *ptr = self->ptr; ++ unsigned long size = variant->ptr_size; ++ ++ // Test the invalid remaps ++ ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); ++ ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); ++ ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED); ++ // Shrink the end of the mapping such that we only unmap past end of the VMA, ++ // which should succeed and poke a hole into the PROT_NONE region ++ ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED); ++ ++ // Remap the whole buffer to a new address ++ tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); ++ ASSERT_NE(tmp, MAP_FAILED); ++ ++ // Try splitting offset 1 hole size into VMA, this should fail ++ ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE, ++ MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED); ++ // Remapping the whole thing should succeed fine ++ ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp); ++ ASSERT_EQ(ptr, tmp); ++ ASSERT_EQ(munmap(tmp, size), 0); ++} ++ ++TEST_F(perf_mmap, unmap) ++{ ++ unsigned long size = variant->ptr_size; ++ ++ // Try to poke holes into the mappings ++ ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0); ++ ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0); ++ ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0); ++} ++ ++TEST_F(perf_mmap, map) ++{ ++ unsigned long size = variant->ptr_size; ++ ++ // Try to poke holes into the mappings by mapping anonymous memory over it ++ ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); ++ ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); ++ ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED); ++} ++ ++TEST_HARNESS_MAIN diff --git a/queue-6.1/series b/queue-6.1/series index 3ce9d550f3..0eb27172b1 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -230,3 +230,7 @@ smb-server-let-recv_done-consistently-call-put_recvm.patch smb-server-let-recv_done-avoid-touching-data_transfe.patch smb-client-let-recv_done-cleanup-before-notifying-th.patch pptp-fix-pptp_xmit-error-path.patch +perf-core-don-t-leak-aux-buffer-refcount-on-allocation-failure.patch +perf-core-exit-early-on-perf_mmap-fail.patch +perf-core-prevent-vma-split-of-buffer-mappings.patch +selftests-perf_events-add-a-mmap-correctness-test.patch