diff options
-rw-r--r-- | include/linux/perf_event.h | 8 | ||||
-rw-r--r-- | kernel/events/core.c | 38 |
2 files changed, 39 insertions, 7 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 2166a69e3bf2e7..780839dd8570ba 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1576,13 +1576,7 @@ static inline int perf_is_paranoid(void) return sysctl_perf_event_paranoid > -1; } -static inline int perf_allow_kernel(struct perf_event_attr *attr) -{ - if (sysctl_perf_event_paranoid > 1 && !perfmon_capable()) - return -EACCES; - - return security_perf_event_open(attr, PERF_SECURITY_KERNEL); -} +extern int perf_allow_kernel(struct perf_event_attr *attr); static inline int perf_allow_cpu(struct perf_event_attr *attr) { diff --git a/kernel/events/core.c b/kernel/events/core.c index 78ae7b6f90fdbf..0a58bcd981a4bd 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3174,6 +3174,12 @@ static int perf_event_modify_attr(struct perf_event *event, return -EOPNOTSUPP; } + if (!event->attr.exclude_kernel) { + err = perf_allow_kernel(attr); + if (err) + return err; + } + WARN_ON_ONCE(event->ctx->parent_ctx); mutex_lock(&event->child_mutex); @@ -12324,6 +12330,38 @@ perf_check_permission(struct perf_event_attr *attr, struct task_struct *task) return is_capable || ptrace_may_access(task, ptrace_mode); } +/* + * Check if unprivileged users are allowed to set up breakpoints on user + * addresses that also count when the kernel accesses them. + */ +static bool perf_allow_kernel_breakpoint(struct perf_event_attr *attr) +{ + if (attr->type != PERF_TYPE_BREAKPOINT) + return false; + + /* + * The sample may contain IPs, registers, or other information that may + * disclose kernel addresses or timing information. Disallow any kind of + * additional sample information. + */ + if (attr->sample_type) + return false; + + /* + * Only allow kernel breakpoints on user addresses. + */ + return access_ok((void __user *)(unsigned long)attr->bp_addr, attr->bp_len); +} + +int perf_allow_kernel(struct perf_event_attr *attr) +{ + if (sysctl_perf_event_paranoid > 1 && !perfmon_capable() && + !perf_allow_kernel_breakpoint(attr)) + return -EACCES; + + return security_perf_event_open(attr, PERF_SECURITY_KERNEL); +} + /** * sys_perf_event_open - open a performance event, associate it to a task/cpu * |