Received: by 10.223.148.5 with SMTP id 5csp7883792wrq; Thu, 18 Jan 2018 10:41:06 -0800 (PST) X-Google-Smtp-Source: ACJfBotxcNcGNh4xru9lt8ocMQtxzQLPpXbdjnkkC0W01qQe6uAiOckjfEEpjB7rof3fVMJ5LTAT X-Received: by 10.98.252.82 with SMTP id e79mr24751232pfh.159.1516300866673; Thu, 18 Jan 2018 10:41:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516300866; cv=none; d=google.com; s=arc-20160816; b=OD+rc1kMxixoWxlITdEYZb9sNWpxwAs50q86EAgbRr2wtNqL3Xbd/FaVRErafxTdK3 edQOxoHoxlORY1+BCTvefKFDKBCWl6OlRi6m1cp6j7e9FQGEwuP1dvrwh4Sva9LUGg3F Vde9ZJ56do3j4KSrPkrEy18T46qrcmLOY28Au3IvLfCpSPkV3D3rTal8FmTskilC2IX7 Zq88qz4PcpSNXvB6y2X9dgQISWFBGSDvNL7J9GH5RktMmgWp7/7IKuBSp4aXHTaOAhaA jgffPT5+4DXAGiHvwDKs5n09+ON4g4GZnfkO4VczedUvTczC5bgnoVgETsHs8jczwEH9 T12A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=x3bU1zRPezXmfLlIoN7WfRfZhlG1HCWXoyfI1GfzALM=; b=fUW7ix6Z3DsdpC24XtTruWdDWSK7wcmFbH7nm1fcUDcqwqAk+o6G/NIUd/4dgR2H4c enFETR1PIHmkRzRa2QXYZlszvoab01QP0yeg+QkXdGHchvHkTr8Qgz43WxjMiri0EsYt ErpktCPydKHUzS+1vMTfbVXucPcZmiROxqO75JFrgULOwLoHwnDXoXQRaNgw2hbMrNXq 3Dwcjz081jwwaTUaUEcV/islPoGPy2T8rsjP82861CTYhtfx6ffOnpM2imiLpZbBsr4M 1mcehqpFui/qRrK8h5PSkahbq7df3c59x5eotXANA+Ig7/dPIP3F90muAZKilhBwRQQ5 yGeg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@ursulin-net.20150623.gappssmtp.com header.s=20150623 header.b=Vjfmhmmb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id g2si6737914pgu.30.2018.01.18.10.40.52; Thu, 18 Jan 2018 10:41:06 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@ursulin-net.20150623.gappssmtp.com header.s=20150623 header.b=Vjfmhmmb; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932622AbeARSkW (ORCPT + 99 others); Thu, 18 Jan 2018 13:40:22 -0500 Received: from mail-wm0-f66.google.com ([74.125.82.66]:34141 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932446AbeARSkT (ORCPT ); Thu, 18 Jan 2018 13:40:19 -0500 Received: by mail-wm0-f66.google.com with SMTP id 81so3982351wmb.1 for ; Thu, 18 Jan 2018 10:40:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ursulin-net.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=x3bU1zRPezXmfLlIoN7WfRfZhlG1HCWXoyfI1GfzALM=; b=Vjfmhmmbywox5eCdkl0bobIJWVdtrxNJMtJPwyPQdcGYsSA5e6aGuuj82Y2hRLreK0 P/n7Ag/hlZuxmSDznYS3w4ywxWcIuszDBa/SBDdVq1E6e8a5xfKqfW1blKjf+nH/6HLF 8UH/pJsc/c6jIjvMPCmCgbjrFn9e5EibGgymBUy7wZAruUGyGDA1wM3blRBjvygpNyJw Obh0wM6l1ZsDCuU5efJ8VDvQuWb5S8yz+tac2EzROoM2QRjx2PGDH4eg77UG8rDEW0IH 61W2L8M2rojgsQLe869drZO45LWY+ddoi9YD5DdAWiOmr9+kMhfLBFsNed25tMb/+KVo FfHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=x3bU1zRPezXmfLlIoN7WfRfZhlG1HCWXoyfI1GfzALM=; b=Xa/ny17/OVBdHfY5i7qukm0RA0CdSfaITag+/pVWSo71dH+fb5jrR/Bkn9hQXyhtku dsYQ2+13JA9cV0XgLFBEwuXRn6FDeDhrh1iAW3+r84vozyHR0dqFka5OEuR4ueFJ4kub N+I7R6XGZj7o0K3pxqB4ubJLJxSQTw2NzhgFdrJEouVWq1Jvs8OWzAtpemdxXwBeiKyO dOlyKLPHVlh8i4iNUXnwNorVEIyUwkXPGxbt8D4C0jE2IsQzNRPbMDIA83SpDmyzhzcT 2Fuy5+3Oxw/lAFSjjwlGK+dmMH5A4IAEXehNLLecGwJH6B7wm4iCuajqyhPLxQF5yr3q 4FHQ== X-Gm-Message-State: AKwxytcqz5sXJQOxqbpofK72XyLF0aVzU4FUr1FHAS6+NOtJdm+n/Mig f6+YZjEoeV8rvsAK89huCZYMcw== X-Received: by 10.28.202.6 with SMTP id a6mr6009505wmg.95.1516300818327; Thu, 18 Jan 2018 10:40:18 -0800 (PST) Received: from localhost.localdomain ([95.146.148.3]) by smtp.gmail.com with ESMTPSA id i1sm17416781wrh.96.2018.01.18.10.40.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jan 2018 10:40:17 -0800 (PST) From: Tvrtko Ursulin X-Google-Original-From: Tvrtko Ursulin To: Intel-gfx@lists.freedesktop.org Cc: tursulin@ursulin.net, Tvrtko Ursulin , Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Alexander Shishkin , Jiri Olsa , Namhyung Kim , linux-kernel@vger.kernel.org Subject: [RFC] perf: Allow fine-grained PMU access control Date: Thu, 18 Jan 2018 18:40:07 +0000 Message-Id: <20180118184007.1300-1-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.14.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Tvrtko Ursulin For situations where sysadmins might want to allow different level of of access control for different PMUs, we start creating per-PMU perf_event_paranoid controls in sysfs. These work in equivalent fashion as the existing perf_event_paranoid sysctl, which now becomes the parent control for each PMU. On PMU registration the global/parent value will be inherited by each PMU, as it will be propagated to all registered PMUs when the sysctl is updated. At any later point individual PMU access controls, located in /device//perf_event_paranoid, can be adjusted to achieve fine grained access control. Signed-off-by: Tvrtko Ursulin Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Namhyung Kim Cc: linux-kernel@vger.kernel.org --- arch/x86/events/intel/bts.c | 2 +- arch/x86/events/intel/core.c | 2 +- arch/x86/events/intel/p4.c | 2 +- include/linux/perf_event.h | 18 +++++--- kernel/events/core.c | 99 ++++++++++++++++++++++++++++++++++------- kernel/sysctl.c | 4 +- kernel/trace/trace_event_perf.c | 6 ++- 7 files changed, 105 insertions(+), 28 deletions(-) diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c index 24ffa1e88cf9..e416c9e2400a 100644 --- a/arch/x86/events/intel/bts.c +++ b/arch/x86/events/intel/bts.c @@ -555,7 +555,7 @@ static int bts_event_init(struct perf_event *event) * Note that the default paranoia setting permits unprivileged * users to profile the kernel. */ - if (event->attr.exclude_kernel && perf_paranoid_kernel() && + if (event->attr.exclude_kernel && perf_paranoid_kernel(event->pmu) && !capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 731153a4681e..d623db13f212 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -3009,7 +3009,7 @@ static int intel_pmu_hw_config(struct perf_event *event) if (x86_pmu.version < 3) return -EINVAL; - if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + if (perf_paranoid_cpu(event->pmu) && !capable(CAP_SYS_ADMIN)) return -EACCES; event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY; diff --git a/arch/x86/events/intel/p4.c b/arch/x86/events/intel/p4.c index d32c0eed38ca..878451ef1ace 100644 --- a/arch/x86/events/intel/p4.c +++ b/arch/x86/events/intel/p4.c @@ -776,7 +776,7 @@ static int p4_validate_raw_event(struct perf_event *event) * the user needs special permissions to be able to use it */ if (p4_ht_active() && p4_event_bind_map[v].shared) { - if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + if (perf_paranoid_cpu(event->pmu) && !capable(CAP_SYS_ADMIN)) return -EACCES; } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 7546822a1d74..1cb4e00d7f96 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -271,6 +271,9 @@ struct pmu { /* number of address filters this PMU can do */ unsigned int nr_addr_filters; + /* fine grained access control */ + int perf_event_paranoid; + /* * Fully disable/enable this PMU, can be used to protect from the PMI * as well as for lazy/batch writing of the MSRs. @@ -1141,6 +1144,9 @@ extern int sysctl_perf_cpu_time_max_percent; extern void perf_sample_event_took(u64 sample_len_ns); +extern int perf_proc_paranoid_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos); extern int perf_proc_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -1151,19 +1157,19 @@ extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, int perf_event_max_stack_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -static inline bool perf_paranoid_tracepoint_raw(void) +static inline bool perf_paranoid_tracepoint_raw(const struct pmu *pmu) { - return sysctl_perf_event_paranoid > -1; + return pmu->perf_event_paranoid > -1; } -static inline bool perf_paranoid_cpu(void) +static inline bool perf_paranoid_cpu(const struct pmu *pmu) { - return sysctl_perf_event_paranoid > 0; + return pmu->perf_event_paranoid > 0; } -static inline bool perf_paranoid_kernel(void) +static inline bool perf_paranoid_kernel(const struct pmu *pmu) { - return sysctl_perf_event_paranoid > 1; + return pmu->perf_event_paranoid > 1; } extern void perf_event_init(void); diff --git a/kernel/events/core.c b/kernel/events/core.c index b4152da656fa..21fd4430df66 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -432,6 +432,24 @@ static void update_perf_cpu_limits(void) static int perf_rotate_context(struct perf_cpu_context *cpuctx); +int perf_proc_paranoid_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + struct pmu *pmu; + + if (ret || !write) + return ret; + + mutex_lock(&pmus_lock); + list_for_each_entry(pmu, &pmus, entry) + pmu->perf_event_paranoid = sysctl_perf_event_paranoid; + mutex_unlock(&pmus_lock); + + return 0; +} + int perf_proc_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -3772,7 +3790,7 @@ find_get_context(struct pmu *pmu, struct task_struct *task, if (!task) { /* Must be root to operate on a CPU event: */ - if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + if (perf_paranoid_cpu(pmu) && !capable(CAP_SYS_ADMIN)) return ERR_PTR(-EACCES); cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); @@ -5313,7 +5331,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) lock_limit >>= PAGE_SHIFT; locked = vma->vm_mm->pinned_vm + extra; - if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && + if ((locked > lock_limit) && perf_paranoid_tracepoint_raw(event->pmu) && !capable(CAP_IPC_LOCK)) { ret = -EPERM; goto unlock; @@ -8880,6 +8898,41 @@ static void free_pmu_context(struct pmu *pmu) mutex_unlock(&pmus_lock); } +/* + * Fine-grained access control: + */ +static ssize_t +perf_event_paranoid_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct pmu *pmu = dev_get_drvdata(dev); + + return snprintf(page, PAGE_SIZE - 1, "%d\n", pmu->perf_event_paranoid); +} + +static ssize_t +perf_event_paranoid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pmu *pmu = dev_get_drvdata(dev); + int ret, val; + + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; + + if (val < -1 || val > 2) + return -EINVAL; + + pmu->perf_event_paranoid = val; + + return count; +} + +DEVICE_ATTR_RW(perf_event_paranoid); + /* * Let userspace know that this PMU supports address range filtering: */ @@ -8994,6 +9047,11 @@ static int pmu_dev_alloc(struct pmu *pmu) if (ret) goto free_dev; + /* Add fine-grained access control attribute. */ + ret = device_create_file(pmu->dev, &dev_attr_perf_event_paranoid); + if (ret) + goto del_dev; + /* For PMUs with address filters, throw in an extra attribute: */ if (pmu->nr_addr_filters) ret = device_create_file(pmu->dev, &dev_attr_nr_addr_filters); @@ -9025,6 +9083,7 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type) if (!pmu->pmu_disable_count) goto unlock; + pmu->perf_event_paranoid = sysctl_perf_event_paranoid; pmu->type = -1; if (!name) goto skip_type; @@ -9634,10 +9693,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, */ attr->branch_sample_type = mask; } - /* privileged levels capture (kernel, hv): check permissions */ - if ((mask & PERF_SAMPLE_BRANCH_PERM_PLM) - && perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) - return -EACCES; } if (attr->sample_type & PERF_SAMPLE_REGS_USER) { @@ -9851,11 +9906,6 @@ SYSCALL_DEFINE5(perf_event_open, if (err) return err; - if (!attr.exclude_kernel) { - if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) - return -EACCES; - } - if (attr.namespaces) { if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -9869,11 +9919,6 @@ SYSCALL_DEFINE5(perf_event_open, return -EINVAL; } - /* Only privileged users can get physical addresses */ - if ((attr.sample_type & PERF_SAMPLE_PHYS_ADDR) && - perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) - return -EACCES; - if (!attr.sample_max_stack) attr.sample_max_stack = sysctl_perf_event_max_stack; @@ -9946,6 +9991,28 @@ SYSCALL_DEFINE5(perf_event_open, goto err_cred; } + if (!attr.exclude_kernel) { + if (perf_paranoid_kernel(event->pmu) && + !capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto err_alloc; + } + } + + /* Only privileged users can get physical addresses */ + if ((attr.sample_type & PERF_SAMPLE_PHYS_ADDR) && + perf_paranoid_kernel(event->pmu) && !capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto err_alloc; + } + + /* privileged levels capture (kernel, hv): check permissions */ + if ((attr.branch_sample_type & PERF_SAMPLE_BRANCH_PERM_PLM) && + perf_paranoid_kernel(event->pmu) && !capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto err_alloc; + } + if (is_sampling_event(event)) { if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) { err = -EOPNOTSUPP; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 557d46728577..2f724b951e92 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1129,7 +1129,9 @@ static struct ctl_table kern_table[] = { .data = &sysctl_perf_event_paranoid, .maxlen = sizeof(sysctl_perf_event_paranoid), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = perf_proc_paranoid_handler, + .extra1 = &neg_one, + .extra2 = &two, }, { .procname = "perf_event_mlock_kb", diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 55d6dff37daf..f23e3560237e 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -44,7 +44,8 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event, /* The ftrace function trace is allowed only for root. */ if (ftrace_event_is_function(tp_event)) { - if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN)) + if (perf_paranoid_tracepoint_raw(p_event->pmu) && + !capable(CAP_SYS_ADMIN)) return -EPERM; if (!is_sampling_event(p_event)) @@ -80,7 +81,8 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event, * ...otherwise raw tracepoint data can be a severe data leak, * only allow root to have these. */ - if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN)) + if (perf_paranoid_tracepoint_raw(p_event->pmu) && + !capable(CAP_SYS_ADMIN)) return -EPERM; return 0; -- 2.14.1