* [PATCH 0/2] perf: complete perf_allow_* trio and use in drm/xe
@ 2026-05-21 2:49 John Hubbard
2026-05-21 2:49 ` [PATCH 1/2] perf/core: out-of-line and export perf_allow_cpu/tracepoint() John Hubbard
2026-05-21 2:49 ` [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu() John Hubbard
0 siblings, 2 replies; 5+ messages in thread
From: John Hubbard @ 2026-05-21 2:49 UTC (permalink / raw)
To: Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, intel-xe, dri-devel, linux-perf-users,
LKML, John Hubbard
perf_allow_cpu() and perf_allow_tracepoint() are static inline and
reach into a non-exported sysctl, so modular drivers that want the
same permission model as system-wide perf end up writing partial
copies of this code. Let's instead export these properly so that modules
can call them.
Commit 5e9629d0ae97 ("drivers/perf: arm_spe: Use perf_allow_kernel()
for permissions") already moved perf_allow_kernel() out of line and
exported it. Patch 1 does the same for the other two.
Patch 2 converts drm/xe's OA and EU stall paths to call
perf_allow_cpu(), so xe observation now respects the system
perf_event_paranoid policy and consults the LSM hook. Sites that have
already configured an LSM perf policy or tuned the paranoid sysctl will
now see those settings honored on xe as well.
John Hubbard (2):
perf/core: out-of-line and export perf_allow_cpu/tracepoint()
drm/xe: gate observation streams with perf_allow_cpu()
drivers/gpu/drm/xe/xe_eu_stall.c | 5 +++--
drivers/gpu/drm/xe/xe_oa.c | 25 +++++++++++++---------
drivers/gpu/drm/xe/xe_observation.c | 32 ++++++++++++++++++++++++-----
drivers/gpu/drm/xe/xe_observation.h | 3 +--
include/linux/perf_event.h | 18 ++--------------
kernel/events/core.c | 18 ++++++++++++++++
6 files changed, 66 insertions(+), 35 deletions(-)
base-commit: b1f7e321a65b3defd5982ead219029ca6d5c8d0f
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] perf/core: out-of-line and export perf_allow_cpu/tracepoint()
2026-05-21 2:49 [PATCH 0/2] perf: complete perf_allow_* trio and use in drm/xe John Hubbard
@ 2026-05-21 2:49 ` John Hubbard
2026-05-21 2:49 ` [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu() John Hubbard
1 sibling, 0 replies; 5+ messages in thread
From: John Hubbard @ 2026-05-21 2:49 UTC (permalink / raw)
To: Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, intel-xe, dri-devel, linux-perf-users,
LKML, John Hubbard
These helpers are static inline in <linux/perf_event.h> and reach
into sysctl_perf_event_paranoid and security_perf_event_open(),
neither of which is itself exported. The perf_allow_* trio is
therefore asymmetric: built-in callers can use any of the three, but
modular code can only call perf_allow_kernel().
Move both bodies into kernel/events/core.c next to perf_allow_kernel()
and export them with EXPORT_SYMBOL_GPL, following the shape of
commit 5e9629d0ae97 ("drivers/perf: arm_spe: Use perf_allow_kernel()
for permissions"). Existing in-tree callers live in built-in arch and
tracing code, so the change is invisible to them.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
include/linux/perf_event.h | 18 ++----------------
kernel/events/core.c | 18 ++++++++++++++++++
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 48d851fbd8ea..00c4ffe1434d 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1791,22 +1791,8 @@ static inline int perf_is_paranoid(void)
}
extern int perf_allow_kernel(void);
-
-static inline int perf_allow_cpu(void)
-{
- if (sysctl_perf_event_paranoid > 0 && !perfmon_capable())
- return -EACCES;
-
- return security_perf_event_open(PERF_SECURITY_CPU);
-}
-
-static inline int perf_allow_tracepoint(void)
-{
- if (sysctl_perf_event_paranoid > -1 && !perfmon_capable())
- return -EPERM;
-
- return security_perf_event_open(PERF_SECURITY_TRACEPOINT);
-}
+extern int perf_allow_cpu(void);
+extern int perf_allow_tracepoint(void);
extern int perf_exclude_event(struct perf_event *event, struct pt_regs *regs);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7935d5663944..cb13f3ad11a3 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -14731,6 +14731,24 @@ int perf_allow_kernel(void)
}
EXPORT_SYMBOL_GPL(perf_allow_kernel);
+int perf_allow_cpu(void)
+{
+ if (sysctl_perf_event_paranoid > 0 && !perfmon_capable())
+ return -EACCES;
+
+ return security_perf_event_open(PERF_SECURITY_CPU);
+}
+EXPORT_SYMBOL_GPL(perf_allow_cpu);
+
+int perf_allow_tracepoint(void)
+{
+ if (sysctl_perf_event_paranoid > -1 && !perfmon_capable())
+ return -EPERM;
+
+ return security_perf_event_open(PERF_SECURITY_TRACEPOINT);
+}
+EXPORT_SYMBOL_GPL(perf_allow_tracepoint);
+
/*
* Inherit an event from parent task to child task.
*
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu()
2026-05-21 2:49 [PATCH 0/2] perf: complete perf_allow_* trio and use in drm/xe John Hubbard
2026-05-21 2:49 ` [PATCH 1/2] perf/core: out-of-line and export perf_allow_cpu/tracepoint() John Hubbard
@ 2026-05-21 2:49 ` John Hubbard
2026-05-21 3:30 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: John Hubbard @ 2026-05-21 2:49 UTC (permalink / raw)
To: Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, intel-xe, dri-devel, linux-perf-users,
LKML, John Hubbard
xe OA and EU-stall paths open-code a partial copy of the system-wide
perf CPU-event permission check:
if (xe_observation_paranoid && !perfmon_capable())
return -EACCES;
This open-coded check skips two things perf_allow_cpu() handles: the
graduated kernel.perf_event_paranoid policy that an administrator
may have tuned, and the security_perf_event_open() LSM hook.
Introduce xe_observation_paranoid_check() to wrap perf_allow_cpu(),
and convert the open-coded sites in xe_oa.c and xe_eu_stall.c. The
dev.xe.observation_paranoid sysctl still acts as an escape hatch
when cleared.
xe observation now consults kernel.perf_event_paranoid and the LSM
perf hook on every open. Sites that have already configured an LSM
perf policy or tuned the paranoid sysctl will see those settings
extend to xe.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/drm/xe/xe_eu_stall.c | 5 +++--
drivers/gpu/drm/xe/xe_oa.c | 25 +++++++++++++---------
drivers/gpu/drm/xe/xe_observation.c | 32 ++++++++++++++++++++++++-----
drivers/gpu/drm/xe/xe_observation.h | 3 +--
4 files changed, 46 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c
index dddcdd0bb7a3..ede8e3c98b2b 100644
--- a/drivers/gpu/drm/xe/xe_eu_stall.c
+++ b/drivers/gpu/drm/xe/xe_eu_stall.c
@@ -963,9 +963,10 @@ int xe_eu_stall_stream_open(struct drm_device *dev, u64 data, struct drm_file *f
return -ENODEV;
}
- if (xe_observation_paranoid && !perfmon_capable()) {
+ ret = xe_observation_paranoid_check();
+ if (ret) {
drm_dbg(&xe->drm, "Insufficient privileges for EU stall monitoring\n");
- return -EACCES;
+ return ret;
}
/* Initialize and set default values */
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index 6337e671c97a..f15847be01bc 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -1676,9 +1676,10 @@ static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
unsigned long start = vma->vm_start;
int i, ret;
- if (xe_observation_paranoid && !perfmon_capable()) {
+ ret = xe_observation_paranoid_check();
+ if (ret) {
drm_dbg(&stream->oa->xe->drm, "Insufficient privilege to map OA buffer\n");
- return -EACCES;
+ return ret;
}
/* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
@@ -2052,10 +2053,12 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
privileged_op = true;
}
- if (privileged_op && xe_observation_paranoid && !perfmon_capable()) {
- drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
- ret = -EACCES;
- goto err_exec_q;
+ if (privileged_op) {
+ ret = xe_observation_paranoid_check();
+ if (ret) {
+ drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
+ goto err_exec_q;
+ }
}
if (!param.exec_q && !param.sample) {
@@ -2334,9 +2337,10 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
return -ENODEV;
}
- if (xe_observation_paranoid && !perfmon_capable()) {
+ err = xe_observation_paranoid_check();
+ if (err) {
drm_dbg(&oa->xe->drm, "Insufficient privileges to add xe OA config\n");
- return -EACCES;
+ return err;
}
err = copy_from_user(¶m, u64_to_user_ptr(data), sizeof(param));
@@ -2436,9 +2440,10 @@ int xe_oa_remove_config_ioctl(struct drm_device *dev, u64 data, struct drm_file
return -ENODEV;
}
- if (xe_observation_paranoid && !perfmon_capable()) {
+ ret = xe_observation_paranoid_check();
+ if (ret) {
drm_dbg(&oa->xe->drm, "Insufficient privileges to remove xe OA config\n");
- return -EACCES;
+ return ret;
}
ret = get_user(arg, ptr);
diff --git a/drivers/gpu/drm/xe/xe_observation.c b/drivers/gpu/drm/xe/xe_observation.c
index e3f9b546207e..39e05b9131a7 100644
--- a/drivers/gpu/drm/xe/xe_observation.c
+++ b/drivers/gpu/drm/xe/xe_observation.c
@@ -4,6 +4,7 @@
*/
#include <linux/errno.h>
+#include <linux/perf_event.h>
#include <linux/sysctl.h>
#include <uapi/drm/xe_drm.h>
@@ -12,9 +13,28 @@
#include "xe_oa.h"
#include "xe_observation.h"
-u32 xe_observation_paranoid = true;
+static u32 xe_observation_paranoid = true;
static struct ctl_table_header *sysctl_header;
+/**
+ * xe_observation_paranoid_check - Gate access to xe observation streams.
+ *
+ * When the xe-specific observation_paranoid sysctl is enabled (the
+ * default), defer to perf_allow_cpu() so that access is governed by the
+ * same policy as system-wide perf CPU events: kernel.perf_event_paranoid
+ * plus the security_perf_event_open() LSM hook. When the sysctl has been
+ * cleared by a privileged user, observation is open to all callers.
+ *
+ * Return: 0 if access is permitted, a negative errno otherwise.
+ */
+int xe_observation_paranoid_check(void)
+{
+ if (!xe_observation_paranoid)
+ return 0;
+
+ return perf_allow_cpu();
+}
+
static int xe_oa_ioctl(struct drm_device *dev, struct drm_xe_observation_param *arg,
struct drm_file *file)
{
@@ -83,11 +103,13 @@ static const struct ctl_table observation_ctl_table[] = {
};
/**
- * xe_observation_sysctl_register - Register xe_observation_paranoid sysctl
+ * xe_observation_sysctl_register - Register the observation_paranoid sysctl
*
- * Normally only superuser/root can access observation stream
- * data. However, superuser can set xe_observation_paranoid sysctl to 0 to
- * allow non-privileged users to also access observation data.
+ * When dev.xe.observation_paranoid is set (the default), access to
+ * observation streams follows the system-wide perf_allow_cpu() policy:
+ * kernel.perf_event_paranoid plus the security_perf_event_open() LSM
+ * hook. A privileged user can clear the sysctl to bypass that gate and
+ * allow unprivileged access to observation data.
*
* Return: always returns 0
*/
diff --git a/drivers/gpu/drm/xe/xe_observation.h b/drivers/gpu/drm/xe/xe_observation.h
index 17816998e966..73a03e03c96a 100644
--- a/drivers/gpu/drm/xe/xe_observation.h
+++ b/drivers/gpu/drm/xe/xe_observation.h
@@ -11,8 +11,7 @@
struct drm_device;
struct drm_file;
-extern u32 xe_observation_paranoid;
-
+int xe_observation_paranoid_check(void);
int xe_observation_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int xe_observation_sysctl_register(void);
void xe_observation_sysctl_unregister(void);
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu()
2026-05-21 2:49 ` [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu() John Hubbard
@ 2026-05-21 3:30 ` sashiko-bot
2026-05-21 3:48 ` John Hubbard
0 siblings, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-05-21 3:30 UTC (permalink / raw)
To: John Hubbard; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Low] Unconditional call to `perf_allow_cpu()` causes a build failure when `CONFIG_PERF_EVENTS` is disabled.
Pre-existing issues:
- [High] Reference count leak of `xe_exec_queue` on the error path in `xe_oa_stream_open_ioctl()`.
- [High] Memory leak of the `stream->syncs` array when an OA stream is disabled or fails to emit config.
--
commit c928d2e2efb93b79ed4af6b5c4bcf6b62b0a9acb
Author: John Hubbard <jhubbard@nvidia.com>
Subject: drm/xe: gate observation streams with perf_allow_cpu()
This patch introduces xe_observation_paranoid_check() to replace open-coded
privilege checks in the xe driver. It wraps perf_allow_cpu(), meaning xe
observation now respects the system-wide kernel.perf_event_paranoid policy
and the security_perf_event_open() LSM hook.
> diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
> index 6337e671c97ae..f15847be01bc2 100644
> --- a/drivers/gpu/drm/xe/xe_oa.c
> +++ b/drivers/gpu/drm/xe/xe_oa.c
[ ... ]
> @@ -2052,10 +2053,12 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
> privileged_op = true;
> }
>
> - if (privileged_op && xe_observation_paranoid && !perfmon_capable()) {
> - drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
> - ret = -EACCES;
> - goto err_exec_q;
> + if (privileged_op) {
> + ret = xe_observation_paranoid_check();
> + if (ret) {
> + drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
> + goto err_exec_q;
> + }
> }
[Severity: High]
This is a pre-existing issue, but does this code leak the xe_exec_queue
reference on the error path?
Looking earlier in xe_oa_stream_open_ioctl():
param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id);
if (XE_IOCTL_DBG(oa->xe, !param.exec_q))
return -ENOENT;
if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1))
return -EOPNOTSUPP;
When the execution queue width is greater than 1, it directly returns
-EOPNOTSUPP instead of jumping to the err_exec_q label. This bypasses
xe_exec_queue_put() and leaves the reference incremented, which could allow
a malicious user space program to repeatedly leak execution queue references.
[Severity: High]
This isn't a bug introduced by this patch, but does the stream opening
process leak the stream->syncs array if the stream is disabled?
When xe_oa_stream_open_ioctl() calls xe_oa_stream_open_ioctl_locked(), the
stream->syncs array is allocated via xe_oa_parse_syncs(). This array is
usually freed inside xe_oa_emit_oa_config() when the stream is enabled.
If a stream is opened with the disabled parameter set to true,
xe_oa_enable_locked() is skipped, and xe_oa_emit_oa_config() is never
called. When the stream is subsequently destroyed upon file release,
xe_oa_stream_destroy() does not appear to free stream->syncs, which might
result in a memory leak.
[ ... ]
> diff --git a/drivers/gpu/drm/xe/xe_observation.c b/drivers/gpu/drm/xe/xe_observation.c
> index e3f9b546207e4..39e05b9131a74 100644
> --- a/drivers/gpu/drm/xe/xe_observation.c
> +++ b/drivers/gpu/drm/xe/xe_observation.c
[ ... ]
> @@ -12,9 +13,28 @@
> #include "xe_oa.h"
> #include "xe_observation.h"
>
> -u32 xe_observation_paranoid = true;
> +static u32 xe_observation_paranoid = true;
> static struct ctl_table_header *sysctl_header;
>
> +/**
> + * xe_observation_paranoid_check - Gate access to xe observation streams.
> + *
> + * When the xe-specific observation_paranoid sysctl is enabled (the
> + * default), defer to perf_allow_cpu() so that access is governed by the
> + * same policy as system-wide perf CPU events: kernel.perf_event_paranoid
> + * plus the security_perf_event_open() LSM hook. When the sysctl has been
> + * cleared by a privileged user, observation is open to all callers.
> + *
> + * Return: 0 if access is permitted, a negative errno otherwise.
> + */
> +int xe_observation_paranoid_check(void)
> +{
> + if (!xe_observation_paranoid)
> + return 0;
> +
> + return perf_allow_cpu();
> +}
[Severity: Low]
Will this cause a build failure when CONFIG_PERF_EVENTS is disabled?
The function perf_allow_cpu() is only defined if CONFIG_PERF_EVENTS is
enabled, and there is no fallback stub provided in
include/linux/perf_event.h for the disabled case. Since the DRM_XE
subsystem does not strictly depend on CONFIG_PERF_EVENTS, building the
driver without perf events support might result in an undefined reference
linker error.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260521024904.331912-1-jhubbard@nvidia.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu()
2026-05-21 3:30 ` sashiko-bot
@ 2026-05-21 3:48 ` John Hubbard
0 siblings, 0 replies; 5+ messages in thread
From: John Hubbard @ 2026-05-21 3:48 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
On 5/20/26 8:30 PM, sashiko-bot@kernel.org wrote:
...
>> - if (privileged_op && xe_observation_paranoid && !perfmon_capable()) {
>> - drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
>> - ret = -EACCES;
>> - goto err_exec_q;
>> + if (privileged_op) {
>> + ret = xe_observation_paranoid_check();
>> + if (ret) {
>> + drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
>> + goto err_exec_q;
>> + }
>> }
>
> [Severity: High]
> This is a pre-existing issue, but does this code leak the xe_exec_queue
> reference on the error path?
Hi Sashiko-bot-san,
Good to know, but like you said, pre-existing, and therefore out of
scope for this patchset.
>
> Looking earlier in xe_oa_stream_open_ioctl():
>
> param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id);
> if (XE_IOCTL_DBG(oa->xe, !param.exec_q))
> return -ENOENT;
>
> if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1))
> return -EOPNOTSUPP;
>
> When the execution queue width is greater than 1, it directly returns
> -EOPNOTSUPP instead of jumping to the err_exec_q label. This bypasses
> xe_exec_queue_put() and leaves the reference incremented, which could allow
> a malicious user space program to repeatedly leak execution queue references.
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does the stream opening
> process leak the stream->syncs array if the stream is disabled?
Another pre-existing issue, and also out of scope for this patchset.
...
> [Severity: Low]
> Will this cause a build failure when CONFIG_PERF_EVENTS is disabled?
>
> The function perf_allow_cpu() is only defined if CONFIG_PERF_EVENTS is
> enabled, and there is no fallback stub provided in
> include/linux/perf_event.h for the disabled case. Since the DRM_XE
> subsystem does not strictly depend on CONFIG_PERF_EVENTS, building the
> driver without perf events support might result in an undefined reference
> linker error.
>
That seems like a real problem, let me fix that in a v2, after waiting
a bit for any other feedback.
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-21 3:48 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-21 2:49 [PATCH 0/2] perf: complete perf_allow_* trio and use in drm/xe John Hubbard
2026-05-21 2:49 ` [PATCH 1/2] perf/core: out-of-line and export perf_allow_cpu/tracepoint() John Hubbard
2026-05-21 2:49 ` [PATCH 2/2] drm/xe: gate observation streams with perf_allow_cpu() John Hubbard
2026-05-21 3:30 ` sashiko-bot
2026-05-21 3:48 ` John Hubbard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox