Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf cap: Tidy up and improve capability testing

Remove dependence on libcap. libcap is only used to query whether a
capability is supported, which is just 1 capget system call.

If the capget system call fails, fall back on root permission
checking. Previously if libcap fails then the permission is assumed
not present which may be pessimistic/wrong.

Add a used_root out argument to perf_cap__capable to say whether the
fall back root check was used. This allows the correct error message,
"root" vs "users with the CAP_PERFMON or CAP_SYS_ADMIN capability", to
be selected.

Tidy uses of perf_cap__capable so that tests aren't repeated if capget
isn't supported.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Changbin Du <changbin.du@huawei.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240806220614.831914-1-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
e25ebda7 8b1042c4

+73 -70
-11
tools/perf/Makefile.config
··· 1031 1031 endif 1032 1032 endif 1033 1033 1034 - ifndef NO_LIBCAP 1035 - ifeq ($(feature-libcap), 1) 1036 - CFLAGS += -DHAVE_LIBCAP_SUPPORT 1037 - EXTLIBS += -lcap 1038 - $(call detected,CONFIG_LIBCAP) 1039 - else 1040 - $(warning No libcap found, disables capability support, please install libcap-devel/libcap-dev) 1041 - NO_LIBCAP := 1 1042 - endif 1043 - endif 1044 - 1045 1034 ifndef NO_BACKTRACE 1046 1035 ifeq ($(feature-backtrace), 1) 1047 1036 CFLAGS += -DHAVE_BACKTRACE_SUPPORT
+14 -14
tools/perf/builtin-ftrace.c
··· 63 63 done = true; 64 64 } 65 65 66 - static int check_ftrace_capable(void) 66 + static bool check_ftrace_capable(void) 67 67 { 68 - if (!(perf_cap__capable(CAP_PERFMON) || 69 - perf_cap__capable(CAP_SYS_ADMIN))) { 70 - pr_err("ftrace only works for %s!\n", 71 - #ifdef HAVE_LIBCAP_SUPPORT 72 - "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" 73 - #else 74 - "root" 75 - #endif 68 + bool used_root; 69 + 70 + if (perf_cap__capable(CAP_PERFMON, &used_root)) 71 + return true; 72 + 73 + if (!used_root && perf_cap__capable(CAP_SYS_ADMIN, &used_root)) 74 + return true; 75 + 76 + pr_err("ftrace only works for %s!\n", 77 + used_root ? "root" 78 + : "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" 76 79 ); 77 - return -1; 78 - } 79 - return 0; 80 + return false; 80 81 } 81 82 82 83 static int __write_tracing_file(const char *name, const char *val, bool append) ··· 1580 1579 signal(SIGCHLD, sig_handler); 1581 1580 signal(SIGPIPE, sig_handler); 1582 1581 1583 - ret = check_ftrace_capable(); 1584 - if (ret < 0) 1582 + if (!check_ftrace_capable()) 1585 1583 return -1; 1586 1584 1587 1585 ret = perf_config(perf_ftrace_config, &ftrace);
+1 -1
tools/perf/util/Build
··· 223 223 perf-util-$(CONFIG_LZMA) += lzma.o 224 224 perf-util-$(CONFIG_ZSTD) += zstd.o 225 225 226 - perf-util-$(CONFIG_LIBCAP) += cap.o 226 + perf-util-y += cap.o 227 227 228 228 perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o 229 229 perf-util-y += demangle-ocaml.o
+42 -17
tools/perf/util/cap.c
··· 3 3 * Capability utilities 4 4 */ 5 5 6 - #ifdef HAVE_LIBCAP_SUPPORT 7 - 8 6 #include "cap.h" 9 - #include <stdbool.h> 10 - #include <sys/capability.h> 7 + #include "debug.h" 8 + #include <errno.h> 9 + #include <string.h> 10 + #include <unistd.h> 11 + #include <linux/capability.h> 12 + #include <sys/syscall.h> 11 13 12 - bool perf_cap__capable(cap_value_t cap) 14 + #ifndef SYS_capget 15 + #define SYS_capget 90 16 + #endif 17 + 18 + #define MAX_LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3 19 + 20 + bool perf_cap__capable(int cap, bool *used_root) 13 21 { 14 - cap_flag_value_t val; 15 - cap_t caps = cap_get_proc(); 22 + struct __user_cap_header_struct header = { 23 + .version = _LINUX_CAPABILITY_VERSION_3, 24 + .pid = getpid(), 25 + }; 26 + struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S]; 27 + __u32 cap_val; 16 28 17 - if (!caps) 18 - return false; 29 + *used_root = false; 30 + while (syscall(SYS_capget, &header, &data[0]) == -1) { 31 + /* Retry, first attempt has set the header.version correctly. */ 32 + if (errno == EINVAL && header.version != _LINUX_CAPABILITY_VERSION_3 && 33 + header.version == _LINUX_CAPABILITY_VERSION_1) 34 + continue; 19 35 20 - if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val) != 0) 21 - val = CAP_CLEAR; 36 + pr_debug2("capget syscall failed (%s - %d) fall back on root check\n", 37 + strerror(errno), errno); 38 + *used_root = true; 39 + return geteuid() == 0; 40 + } 22 41 23 - if (cap_free(caps) != 0) 24 - return false; 25 - 26 - return val == CAP_SET; 42 + /* Extract the relevant capability bit. */ 43 + if (cap >= 32) { 44 + if (header.version == _LINUX_CAPABILITY_VERSION_3) { 45 + cap_val = data[1].effective; 46 + } else { 47 + /* Capability beyond 32 is requested but only 32 are supported. */ 48 + return false; 49 + } 50 + } else { 51 + cap_val = data[0].effective; 52 + } 53 + return (cap_val & (1 << (cap & 0x1f))) != 0; 27 54 } 28 - 29 - #endif /* HAVE_LIBCAP_SUPPORT */
+3 -20
tools/perf/util/cap.h
··· 3 3 #define __PERF_CAP_H 4 4 5 5 #include <stdbool.h> 6 - #include <linux/capability.h> 7 - #include <linux/compiler.h> 8 - 9 - #ifdef HAVE_LIBCAP_SUPPORT 10 - 11 - #include <sys/capability.h> 12 - 13 - bool perf_cap__capable(cap_value_t cap); 14 - 15 - #else 16 - 17 - #include <unistd.h> 18 - #include <sys/types.h> 19 - 20 - static inline bool perf_cap__capable(int cap __maybe_unused) 21 - { 22 - return geteuid() == 0; 23 - } 24 - 25 - #endif /* HAVE_LIBCAP_SUPPORT */ 26 6 27 7 /* For older systems */ 28 8 #ifndef CAP_SYSLOG ··· 12 32 #ifndef CAP_PERFMON 13 33 #define CAP_PERFMON 38 14 34 #endif 35 + 36 + /* Query if a capability is supported, used_root is set if the fallback root check was used. */ 37 + bool perf_cap__capable(int cap, bool *used_root); 15 38 16 39 #endif /* __PERF_CAP_H */
+4 -4
tools/perf/util/symbol.c
··· 2425 2425 { 2426 2426 bool value = false; 2427 2427 FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); 2428 + bool used_root; 2429 + bool cap_syslog = perf_cap__capable(CAP_SYSLOG, &used_root); 2428 2430 2429 2431 if (fp != NULL) { 2430 2432 char line[8]; 2431 2433 2432 2434 if (fgets(line, sizeof(line), fp) != NULL) 2433 - value = perf_cap__capable(CAP_SYSLOG) ? 2434 - (atoi(line) >= 2) : 2435 - (atoi(line) != 0); 2435 + value = cap_syslog ? (atoi(line) >= 2) : (atoi(line) != 0); 2436 2436 2437 2437 fclose(fp); 2438 2438 } ··· 2440 2440 /* Per kernel/kallsyms.c: 2441 2441 * we also restrict when perf_event_paranoid > 1 w/o CAP_SYSLOG 2442 2442 */ 2443 - if (perf_event_paranoid() > 1 && !perf_cap__capable(CAP_SYSLOG)) 2443 + if (perf_event_paranoid() > 1 && !cap_syslog) 2444 2444 value = true; 2445 2445 2446 2446 return value;
+9 -3
tools/perf/util/util.c
··· 325 325 326 326 bool perf_event_paranoid_check(int max_level) 327 327 { 328 - return perf_cap__capable(CAP_SYS_ADMIN) || 329 - perf_cap__capable(CAP_PERFMON) || 330 - perf_event_paranoid() <= max_level; 328 + bool used_root; 329 + 330 + if (perf_cap__capable(CAP_SYS_ADMIN, &used_root)) 331 + return true; 332 + 333 + if (!used_root && perf_cap__capable(CAP_PERFMON, &used_root)) 334 + return true; 335 + 336 + return perf_event_paranoid() <= max_level; 331 337 } 332 338 333 339 static int