From e5ac623a254c0c92faf9b1ccfc5238dc168d249f Mon Sep 17 00:00:00 2001 From: Raghavendra Rao Ananta Date: Wed, 18 Mar 2020 11:11:34 -0700 Subject: [PATCH] perf: Satisfy the kernel's request to request PMU counters The SELinux policy on Android doesn't provide the kernel-space with CAP_SYS_ADMIN capability. The perf-core checks for this capability before giving access to the callers to read the PMU counters. Hence, when the kernel drivers try to gain access to the PMU (via perf_event_create_kernel_counter()), it fails with -EACCES. To get past this, add another condition to check if the request is coming from kernel-space or user-space. If kernel-space, don't check for the capability any further and move on. Change-Id: Ie2ddc6b619fa0d60d4752994ea0c9cedf5b96b5c Signed-off-by: Raghavendra Rao Ananta --- include/linux/perf_event.h | 20 +++++++++++++++++--- kernel/events/core.c | 9 +-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 5af7ae27b252..59556b1f3474 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1253,6 +1253,13 @@ int perf_event_max_stack_handler(struct ctl_table *table, int write, #define PERF_SECURITY_KERNEL 2 #define PERF_SECURITY_TRACEPOINT 3 +#define TASK_TOMBSTONE ((void *)-1L) + +static bool is_kernel_event(struct perf_event *event) +{ + return READ_ONCE(event->owner) == TASK_TOMBSTONE; +} + static inline bool perf_paranoid_any(void) { return sysctl_perf_event_paranoid > 2; @@ -1271,12 +1278,19 @@ static inline int perf_allow_kernel(struct perf_event_attr *attr) return security_perf_event_open(attr, PERF_SECURITY_KERNEL); } -static inline int perf_allow_cpu(struct perf_event_attr *attr) +static inline int perf_allow_cpu(struct perf_event *event) { - if (sysctl_perf_event_paranoid > 0 && !capable(CAP_SYS_ADMIN)) + if (sysctl_perf_event_paranoid > 0 && !is_kernel_event(event) && + !capable(CAP_SYS_ADMIN)) { return -EACCES; + } - return security_perf_event_open(attr, PERF_SECURITY_CPU); + /* + * Bypass the system call security check if the + * event creation is from the kernel + */ + return is_kernel_event(event) ? 0 : + security_perf_event_open(&event->attr, PERF_SECURITY_CPU); } static inline int perf_allow_tracepoint(struct perf_event_attr *attr) diff --git a/kernel/events/core.c b/kernel/events/core.c index bd80191171e6..6f0827d6f064 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -164,13 +164,6 @@ static void perf_ctx_unlock(struct perf_cpu_context *cpuctx, raw_spin_unlock(&cpuctx->ctx.lock); } -#define TASK_TOMBSTONE ((void *)-1L) - -static bool is_kernel_event(struct perf_event *event) -{ - return READ_ONCE(event->owner) == TASK_TOMBSTONE; -} - /* * On task ctx scheduling... * @@ -4234,7 +4227,7 @@ find_get_context(struct pmu *pmu, struct task_struct *task, if (!task) { /* Must be root to operate on a CPU event: */ - err = perf_allow_cpu(&event->attr); + err = perf_allow_cpu(event); if (err) return ERR_PTR(err);