public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Haocheng Yu <yuhaocheng035@gmail.com>,
	kernel test robot <lkp@intel.com>,
	"Peter Zijlstra (Intel)" <peterz@infradead.org>,
	Sasha Levin <sashal@kernel.org>,
	mingo@redhat.com, acme@kernel.org, namhyung@kernel.org,
	linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH AUTOSEL 6.19-6.18] perf/core: Fix refcount bug and potential UAF in perf_mmap
Date: Thu,  5 Mar 2026 10:36:58 -0500	[thread overview]
Message-ID: <20260305153704.106918-15-sashal@kernel.org> (raw)
In-Reply-To: <20260305153704.106918-1-sashal@kernel.org>

From: Haocheng Yu <yuhaocheng035@gmail.com>

[ Upstream commit 77de62ad3de3967818c3dbe656b7336ebee461d2 ]

Syzkaller reported a refcount_t: addition on 0; use-after-free warning
in perf_mmap.

The issue is caused by a race condition between a failing mmap() setup
and a concurrent mmap() on a dependent event (e.g., using output
redirection).

In perf_mmap(), the ring_buffer (rb) is allocated and assigned to
event->rb with the mmap_mutex held. The mutex is then released to
perform map_range().

If map_range() fails, perf_mmap_close() is called to clean up.
However, since the mutex was dropped, another thread attaching to
this event (via inherited events or output redirection) can acquire
the mutex, observe the valid event->rb pointer, and attempt to
increment its reference count. If the cleanup path has already
dropped the reference count to zero, this results in a
use-after-free or refcount saturation warning.

Fix this by extending the scope of mmap_mutex to cover the
map_range() call. This ensures that the ring buffer initialization
and mapping (or cleanup on failure) happens atomically effectively,
preventing other threads from accessing a half-initialized or
dying ring buffer.

Closes: https://lore.kernel.org/oe-kbuild-all/202602020208.m7KIjdzW-lkp@intel.com/
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Haocheng Yu <yuhaocheng035@gmail.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260202162057.7237-1-yuhaocheng035@gmail.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

This confirms the race scenario. `perf_event_set_output()` acquires
`output_event->mmap_mutex` and then calls `ring_buffer_get()` which
increments the refcount. If the mmap_mutex was released after
`perf_mmap_rb()` set `event->rb` but before cleanup on `map_range()`
failure, another thread can observe `event->rb` and attempt to get the
ring buffer.

## Analysis

### 1. COMMIT MESSAGE ANALYSIS
The commit clearly describes a **race condition** leading to a **use-
after-free** and **refcount saturation warning** in `perf_mmap()`. It
was:
- **Reported by**: kernel test robot (syzkaller-like automated testing)
- **Signed-off by**: Peter Zijlstra (Intel), the perf subsystem
  maintainer
- **Link to report**: Provided via `Closes:` tag

### 2. CODE CHANGE ANALYSIS
The fix is **purely a scope change** - it extends the existing
`scoped_guard(mutex, &event->mmap_mutex)` to cover the `map_range()`
call and associated cleanup. Specifically:

**Before**: The mutex was released at line 7191 (closing brace of
`scoped_guard`), then `vm_flags_set()`, `mapped()` callback, and
`map_range()` all ran without the mutex. If `map_range()` failed and
`perf_mmap_close()` was called, a concurrent thread could race in
between.

**After**: All of `vm_flags_set()`, `mapped()`, `map_range()`, and
`perf_mmap_close()` (on failure) run inside the `scoped_guard`, closing
the race window.

The race scenario:
1. Thread A: acquires `mmap_mutex`, allocates rb, assigns `event->rb`,
   releases mutex
2. Thread B: acquires `mmap_mutex`, sees valid `event->rb`, calls
   `ring_buffer_get()` (refcount increment)
3. Thread A: `map_range()` fails, calls `perf_mmap_close()` which drops
   refcount to 0
4. Thread B: now holds a reference to a freed ring buffer → UAF

### 3. CLASSIFICATION
- **Bug type**: Race condition → use-after-free / refcount corruption
- **Severity**: HIGH - UAF is a security-class bug (exploitable from
  userspace via perf syscall)
- **Subsystem**: perf/core - widely used, security-sensitive subsystem

### 4. SCOPE AND RISK
- **Size**: 1 file, 38 lines changed (19 insertions, 19 deletions) -
  essentially indentation changes moving code into the existing
  scoped_guard block
- **Risk**: LOW - the fix simply holds a mutex for longer, covering
  operations that logically should have been protected. The only risk is
  holding the mutex across `map_range()`, but `map_range()` doesn't take
  any conflicting locks.
- **Regression potential**: Minimal - holding a mutex slightly longer
  might theoretically increase contention, but `perf_mmap()` is not a
  hot path (called during mmap setup, not during data collection)

### 5. USER IMPACT
- perf is used by virtually all Linux deployments for profiling
- The race can be triggered from userspace with concurrent mmap
  operations
- Found by automated fuzzing (kernel test robot), meaning it's reachable

### 6. DEPENDENCIES
- **Requires**: `scoped_guard` for mmap_mutex (commit d23a6dbc0a717, in
  v6.18+) - **present in 6.19.y**
- **Requires**: `map_range()` and `perf_mmap_close()` on failure path
  (commit f74b9f4ba63ff, in v6.17+) - **present in 6.19.y**
- For older stable trees (6.17.y, 6.12.y, etc.), this would need
  adaptation since the `scoped_guard` pattern and `map_range()` function
  may not exist. The underlying race exists but the fix would need to be
  written differently.

### 7. STABILITY
- Signed-off by Peter Zijlstra, the perf maintainer
- Already merged into mainline (v7.0 merge window)
- Clean, minimal change with clear logic

### Verification

- **git log showed** commit 77de62ad3de39 exists and is authored by
  Haocheng Yu, signed by Peter Zijlstra (Intel) - **verified**
- **git merge-base** confirmed the commit is NOT yet in the 6.19.y
  stable tree (HEAD = v6.19.6) - **verified**
- **git merge-base** confirmed prerequisite d23a6dbc0a717 (scoped_guard)
  IS in 6.19.y - **verified**
- **git merge-base** confirmed prerequisite f74b9f4ba63ff (map_range
  fail handling) IS in 6.19.y - **verified**
- **Read of current perf_mmap()** (lines 7145-7215) confirmed the race
  window: scoped_guard closes at line 7191, then map_range() at line
  7210 is unprotected - **verified**
- **Read of perf_event_set_output()** (lines 13320-13389) confirmed
  concurrent path: acquires mmap_mutex, calls ring_buffer_get() on
  output_event's rb - **verified as concurrent accessor**
- **git show --stat** confirmed 1 file changed, 19 insertions/19
  deletions - **verified minimal scope**
- **git tag --contains** confirmed scoped_guard was first in v6.18,
  map_range fix first in v6.17 - older stable trees would need different
  backport approach - **verified**

### Conclusion

This is a textbook stable backport candidate:
- Fixes a **use-after-free** race condition (security-class bug)
- Found by automated testing (reproducible)
- Signed off by the subsystem maintainer
- Minimal, surgical fix (scope extension of existing mutex)
- Low regression risk
- Applies cleanly to 6.19.y (prerequisites present)
- Core subsystem (perf) with broad user impact

**YES**

 kernel/events/core.c | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 69c56cad88a89..c0bb657e28e31 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7188,28 +7188,28 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
 			ret = perf_mmap_aux(vma, event, nr_pages);
 		if (ret)
 			return ret;
-	}
 
-	/*
-	 * Since pinned accounting is per vm we cannot allow fork() to copy our
-	 * vma.
-	 */
-	vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
-	vma->vm_ops = &perf_mmap_vmops;
+		/*
+		 * Since pinned accounting is per vm we cannot allow fork() to copy our
+		 * vma.
+		 */
+		vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
+		vma->vm_ops = &perf_mmap_vmops;
 
-	mapped = get_mapped(event, event_mapped);
-	if (mapped)
-		mapped(event, vma->vm_mm);
+		mapped = get_mapped(event, event_mapped);
+		if (mapped)
+			mapped(event, vma->vm_mm);
 
-	/*
-	 * Try to map it into the page table. On fail, invoke
-	 * perf_mmap_close() to undo the above, as the callsite expects
-	 * full cleanup in this case and therefore does not invoke
-	 * vmops::close().
-	 */
-	ret = map_range(event->rb, vma);
-	if (ret)
-		perf_mmap_close(vma);
+		/*
+		 * Try to map it into the page table. On fail, invoke
+		 * perf_mmap_close() to undo the above, as the callsite expects
+		 * full cleanup in this case and therefore does not invoke
+		 * vmops::close().
+		 */
+		ret = map_range(event->rb, vma);
+		if (ret)
+			perf_mmap_close(vma);
+	}
 
 	return ret;
 }
-- 
2.51.0


  parent reply	other threads:[~2026-03-05 15:37 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-05 15:36 [PATCH AUTOSEL 6.19-6.18] scsi: ufs: core: Reset urgent_bkops_lvl to allow runtime PM power mode Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.10] unshare: fix unshare_fs() handling Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.12] drm/amdgpu/vcn5: Add SMU dpm interface type Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.1] wifi: mac80211: set default WMM parameters on all links Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.15] ALSA: usb-audio: Check max frame size for implicit feedback mode, too Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.10] scsi: ses: Fix devices attaching to different hosts Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.6] ASoC: cs42l43: Report insert for exotic peripherals Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.15] ALSA: usb-audio: Avoid implicit feedback mode on DIYINHK USB Audio 2.0 Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.10] ACPI: PM: Save NVS memory on Lenovo G70-35 Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.10] scsi: storvsc: Fix scheduling while atomic on PREEMPT_RT Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.1] ASoC: amd: yc: Add ASUS EXPERTBOOK BM1503CDA to quirk table Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-5.10] ACPI: OSI: Add DMI quirk for Acer Aspire One D255 Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.18] fs: init flags_valid before calling vfs_fileattr_get Sasha Levin
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.6] scsi: ufs: core: Fix possible NULL pointer dereference in ufshcd_add_command_trace() Sasha Levin
2026-03-05 15:36 ` Sasha Levin [this message]
2026-03-05 15:36 ` [PATCH AUTOSEL 6.19-6.6] scsi: ufs: core: Fix shift out of bounds when MAXQ=32 Sasha Levin
2026-03-05 15:37 ` [PATCH AUTOSEL 6.19-5.15] scsi: mpi3mr: Add NULL checks when resetting request and reply queues Sasha Levin
2026-03-05 15:37 ` [PATCH AUTOSEL 6.19-6.12] ALSA: hda/realtek: Fix speaker pop on Star Labs StarFighter Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260305153704.106918-15-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=acme@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=lkp@intel.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=patches@lists.linux.dev \
    --cc=peterz@infradead.org \
    --cc=stable@vger.kernel.org \
    --cc=yuhaocheng035@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox