mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kyle Zeng <kylebot@openai.com>
To: linux-perf-users@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, peterz@infradead.org,
	mingo@redhat.com, acme@kernel.org, namhyung@kernel.org,
	outbounddisclosures@openai.com, Kyle Zeng <kylebot@openai.com>
Subject: [PATCH] perf/hw_breakpoint: Avoid leaking private x86 breakpoint ranges
Date: Tue,  6 Oct 2026 15:25:19 -0700	[thread overview]
Message-ID: <20261006222519.43193-1-kylebot@openai.com> (raw)

An unprivileged caller can open a user breakpoint and submit a rejected
PERF_EVENT_IOC_MODIFY_ATTRIBUTES request that changes exclude_kernel to
zero. The architecture parser runs before the immutable-attribute check,
so an x86 blacklist hit returns EINVAL while an ordinary kernel address
reaches the CAP_SYS_ADMIN check and returns EPERM.

Moving the attribute check alone is insufficient. On CPUs without BPEXT,
an aligned power-of-two data range larger than eight bytes normally
returns EOPNOTSUPP, but overlapping the CPU-entry blacklist returns
EINVAL first. Such requests can keep exclude_kernel set and can also be
made through perf_event_open(). In particular, the __per_cpu_offset
check exposes the relocated kernel image.

Validate immutable attributes before parsing a modify request. Also check
x86 kernel-breakpoint access before consulting either private blacklist,
using the whole requested data range even when its length is unsupported.
Check instruction lengths first so that the sizeof(long) ABI does not
turn a single-address user instruction breakpoint into a kernel range.
Keep the overflow check, the generic post-parse permission check, and all
blacklist restrictions on authorized kernel breakpoints.

Fixes: e5779e8e1229 ("perf/x86/hw_breakpoints: Disallow kernel breakpoints unless kprobe-safe")
Fixes: 26c6ccdf5c06 ("perf/hw_breakpoint: Clean up and consolidate modify_user_hw_breakpoint_check()")
Assisted-by: Codex:gpt-6-astra
Signed-off-by: Kyle Zeng <kylebot@openai.com>
---
 arch/x86/kernel/hw_breakpoint.c | 44 ++++++++++++++++++++++-----------
 kernel/events/hw_breakpoint.c   |  8 +++---
 2 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index f846c15f21ca..cf0d09cdf497 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -15,6 +15,7 @@
  * using the CPU's debug registers.
  */
 
+#include <linux/capability.h>
 #include <linux/perf_event.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/irqflags.h>
@@ -332,14 +333,35 @@ static int arch_build_bp_info(struct perf_event *bp,
 		return -EINVAL;
 
 	/*
-	 * Prevent any breakpoint of any type that overlaps the CPU
-	 * entry area and data.  This protects the IST stacks and also
-	 * reduces the chance that we ever find out what happens if
-	 * there's a data breakpoint on the GDT, IDT, or TSS.
+	 * Instruction breakpoints match only the address, but their ABI
+	 * requires a length of sizeof(long). Reject other lengths before
+	 * checking the address range.
 	 */
-	if (within_cpu_entry(attr->bp_addr, bp_end))
+	if (attr->bp_type == HW_BREAKPOINT_X && attr->bp_len != sizeof(long))
 		return -EINVAL;
 
+	/*
+	 * Check permissions before consulting the private kernel address
+	 * ranges below. Otherwise their errors disclose the kernel layout,
+	 * including for unsupported range-breakpoint lengths.
+	 */
+	if (attr->bp_addr >= TASK_SIZE_MAX ||
+	    (attr->bp_type != HW_BREAKPOINT_X && bp_end >= TASK_SIZE_MAX)) {
+		if (attr->exclude_kernel)
+			return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		/*
+		 * Prevent any breakpoint of any type that overlaps the CPU
+		 * entry area and data. This protects the IST stacks and also
+		 * reduces the chance that we ever find out what happens if
+		 * there's a data breakpoint on the GDT, IDT, or TSS.
+		 */
+		if (within_cpu_entry(attr->bp_addr, bp_end))
+			return -EINVAL;
+	}
+
 	hw->address = attr->bp_addr;
 	hw->mask = 0;
 
@@ -363,16 +385,8 @@ static int arch_build_bp_info(struct perf_event *bp,
 		}
 
 		hw->type = X86_BREAKPOINT_EXECUTE;
-		/*
-		 * x86 inst breakpoints need to have a specific undefined len.
-		 * But we still need to check userspace is not trying to setup
-		 * an unsupported length, to get a range breakpoint for example.
-		 */
-		if (attr->bp_len == sizeof(long)) {
-			hw->len = X86_BREAKPOINT_LEN_X;
-			return 0;
-		}
-		fallthrough;
+		hw->len = X86_BREAKPOINT_LEN_X;
+		return 0;
 	default:
 		return -EINVAL;
 	}
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index 789add0c185a..1f0e7f63ba82 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -765,10 +765,6 @@ modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *a
 	struct arch_hw_breakpoint hw = { };
 	int err;
 
-	err = hw_breakpoint_parse(bp, attr, &hw);
-	if (err)
-		return err;
-
 	if (check) {
 		struct perf_event_attr old_attr;
 
@@ -778,6 +774,10 @@ modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *a
 			return -EINVAL;
 	}
 
+	err = hw_breakpoint_parse(bp, attr, &hw);
+	if (err)
+		return err;
+
 	if (bp->attr.bp_type != attr->bp_type) {
 		err = modify_bp_slot(bp, bp->attr.bp_type, attr->bp_type);
 		if (err)

base-commit: fd179f8a05be3ccae366b9b96e176b51fbe54aab
-- 
2.53.0


                 reply	other threads:[~2026-10-06 22:25 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261006222519.43193-1-kylebot@openai.com \
    --to=kylebot@openai.com \
    --cc=acme@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=outbounddisclosures@openai.com \
    --cc=peterz@infradead.org \
    /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

all inboxes | Powered by JetHome®