Commit e25ebda7 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

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: default avatarIan 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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 8b1042c4
Loading
Loading
Loading
Loading
+0 −11
Original line number Diff line number Diff line
@@ -1031,17 +1031,6 @@ ifndef NO_LIBZSTD
  endif
endif

ifndef NO_LIBCAP
  ifeq ($(feature-libcap), 1)
    CFLAGS += -DHAVE_LIBCAP_SUPPORT
    EXTLIBS += -lcap
    $(call detected,CONFIG_LIBCAP)
  else
    $(warning No libcap found, disables capability support, please install libcap-devel/libcap-dev)
    NO_LIBCAP := 1
  endif
endif

ifndef NO_BACKTRACE
  ifeq ($(feature-backtrace), 1)
    CFLAGS += -DHAVE_BACKTRACE_SUPPORT
+14 −14
Original line number Diff line number Diff line
@@ -63,20 +63,21 @@ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
	done = true;
}

static int check_ftrace_capable(void)
static bool check_ftrace_capable(void)
{
	if (!(perf_cap__capable(CAP_PERFMON) ||
	      perf_cap__capable(CAP_SYS_ADMIN))) {
	bool used_root;

	if (perf_cap__capable(CAP_PERFMON, &used_root))
		return true;

	if (!used_root && perf_cap__capable(CAP_SYS_ADMIN, &used_root))
		return true;

	pr_err("ftrace only works for %s!\n",
#ifdef HAVE_LIBCAP_SUPPORT
		"users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
#else
		"root"
#endif
		used_root ? "root"
			  : "users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
		);
		return -1;
	}
	return 0;
	return false;
}

static int __write_tracing_file(const char *name, const char *val, bool append)
@@ -1579,8 +1580,7 @@ int cmd_ftrace(int argc, const char **argv)
	signal(SIGCHLD, sig_handler);
	signal(SIGPIPE, sig_handler);

	ret = check_ftrace_capable();
	if (ret < 0)
	if (!check_ftrace_capable())
		return -1;

	ret = perf_config(perf_ftrace_config, &ftrace);
+1 −1
Original line number Diff line number Diff line
@@ -223,7 +223,7 @@ perf-util-$(CONFIG_ZLIB) += zlib.o
perf-util-$(CONFIG_LZMA) += lzma.o
perf-util-$(CONFIG_ZSTD) += zstd.o

perf-util-$(CONFIG_LIBCAP) += cap.o
perf-util-y += cap.o

perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o
perf-util-y += demangle-ocaml.o
+44 −19
Original line number Diff line number Diff line
@@ -3,27 +3,52 @@
 * Capability utilities
 */

#ifdef HAVE_LIBCAP_SUPPORT

#include "cap.h"
#include <stdbool.h>
#include <sys/capability.h>
#include "debug.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/syscall.h>

bool perf_cap__capable(cap_value_t cap)
{
	cap_flag_value_t val;
	cap_t caps = cap_get_proc();
#ifndef SYS_capget
#define SYS_capget 90
#endif

	if (!caps)
		return false;
#define MAX_LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3

	if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val) != 0)
		val = CAP_CLEAR;
bool perf_cap__capable(int cap, bool *used_root)
{
	struct __user_cap_header_struct header = {
		.version = _LINUX_CAPABILITY_VERSION_3,
		.pid = getpid(),
	};
	struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S];
	__u32 cap_val;

	*used_root = false;
	while (syscall(SYS_capget, &header, &data[0]) == -1) {
		/* Retry, first attempt has set the header.version correctly. */
		if (errno == EINVAL && header.version != _LINUX_CAPABILITY_VERSION_3 &&
		    header.version == _LINUX_CAPABILITY_VERSION_1)
			continue;

		pr_debug2("capget syscall failed (%s - %d) fall back on root check\n",
			  strerror(errno), errno);
		*used_root = true;
		return geteuid() == 0;
	}

	if (cap_free(caps) != 0)
	/* Extract the relevant capability bit. */
	if (cap >= 32) {
		if (header.version == _LINUX_CAPABILITY_VERSION_3) {
			cap_val = data[1].effective;
		} else {
			/* Capability beyond 32 is requested but only 32 are supported. */
			return false;

	return val == CAP_SET;
		}

#endif  /* HAVE_LIBCAP_SUPPORT */
	} else {
		cap_val = data[0].effective;
	}
	return (cap_val & (1 << (cap & 0x1f))) != 0;
}
+3 −20
Original line number Diff line number Diff line
@@ -3,26 +3,6 @@
#define __PERF_CAP_H

#include <stdbool.h>
#include <linux/capability.h>
#include <linux/compiler.h>

#ifdef HAVE_LIBCAP_SUPPORT

#include <sys/capability.h>

bool perf_cap__capable(cap_value_t cap);

#else

#include <unistd.h>
#include <sys/types.h>

static inline bool perf_cap__capable(int cap __maybe_unused)
{
	return geteuid() == 0;
}

#endif /* HAVE_LIBCAP_SUPPORT */

/* For older systems */
#ifndef CAP_SYSLOG
@@ -33,4 +13,7 @@ static inline bool perf_cap__capable(int cap __maybe_unused)
#define CAP_PERFMON	38
#endif

/* Query if a capability is supported, used_root is set if the fallback root check was used. */
bool perf_cap__capable(int cap, bool *used_root);

#endif /* __PERF_CAP_H */
Loading