Commit aaccdf9c authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'Remove libcap dependency from bpf selftests'



Martin KaFai Lau says:

====================

After upgrading to the newer libcap (>= 2.60),
the libcap commit aca076443591 ("Make cap_t operations thread safe.")
added a "__u8 mutex;" to the "struct _cap_struct".  It caused a few byte
shift that breaks the assumption made in the "struct libcap" definition
in test_verifier.c.

This set is to remove the libcap dependency from the bpf selftests.

v2:
- Define CAP_PERFMON and CAP_BPF when the older <linux/capability.h>
  does not have them. (Andrii)
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 6585abea 82cb2b30
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ CFLAGS += -g -O0 -rdynamic -Wall -Werror $(GENFLAGS) $(SAN_CFLAGS) \
	  -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)		\
	  -I$(TOOLSINCDIR) -I$(APIDIR) -I$(OUTPUT)
LDFLAGS += $(SAN_CFLAGS)
LDLIBS += -lcap -lelf -lz -lrt -lpthread
LDLIBS += -lelf -lz -lrt -lpthread

# Silence some warnings when compiled with clang
ifneq ($(LLVM),)
@@ -195,6 +195,7 @@ $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ)
CGROUP_HELPERS	:= $(OUTPUT)/cgroup_helpers.o
TESTING_HELPERS	:= $(OUTPUT)/testing_helpers.o
TRACE_HELPERS	:= $(OUTPUT)/trace_helpers.o
CAP_HELPERS	:= $(OUTPUT)/cap_helpers.o

$(OUTPUT)/test_dev_cgroup: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_skb_cgroup_id_user: $(CGROUP_HELPERS) $(TESTING_HELPERS)
@@ -211,7 +212,7 @@ $(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)
$(OUTPUT)/xdping: $(TESTING_HELPERS)
$(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)
$(OUTPUT)/test_maps: $(TESTING_HELPERS)
$(OUTPUT)/test_verifier: $(TESTING_HELPERS)
$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS)

BPFTOOL ?= $(DEFAULT_BPFTOOL)
$(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)    \
@@ -479,7 +480,8 @@ TRUNNER_TESTS_DIR := prog_tests
TRUNNER_BPF_PROGS_DIR := progs
TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c	\
			 network_helpers.c testing_helpers.c		\
			 btf_helpers.c flow_dissector_load.h
			 btf_helpers.c flow_dissector_load.h		\
			 cap_helpers.c
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko	\
		       ima_setup.sh					\
		       $(wildcard progs/btf_dump_test_case_*.c)
+67 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include "cap_helpers.h"

/* Avoid including <sys/capability.h> from the libcap-devel package,
 * so directly declare them here and use them from glibc.
 */
int capget(cap_user_header_t header, cap_user_data_t data);
int capset(cap_user_header_t header, const cap_user_data_t data);

int cap_enable_effective(__u64 caps, __u64 *old_caps)
{
	struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
	struct __user_cap_header_struct hdr = {
		.version = _LINUX_CAPABILITY_VERSION_3,
	};
	__u32 cap0 = caps;
	__u32 cap1 = caps >> 32;
	int err;

	err = capget(&hdr, data);
	if (err)
		return err;

	if (old_caps)
		*old_caps = (__u64)(data[1].effective) << 32 | data[0].effective;

	if ((data[0].effective & cap0) == cap0 &&
	    (data[1].effective & cap1) == cap1)
		return 0;

	data[0].effective |= cap0;
	data[1].effective |= cap1;
	err = capset(&hdr, data);
	if (err)
		return err;

	return 0;
}

int cap_disable_effective(__u64 caps, __u64 *old_caps)
{
	struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
	struct __user_cap_header_struct hdr = {
		.version = _LINUX_CAPABILITY_VERSION_3,
	};
	__u32 cap0 = caps;
	__u32 cap1 = caps >> 32;
	int err;

	err = capget(&hdr, data);
	if (err)
		return err;

	if (old_caps)
		*old_caps = (__u64)(data[1].effective) << 32 | data[0].effective;

	if (!(data[0].effective & cap0) && !(data[1].effective & cap1))
		return 0;

	data[0].effective &= ~cap0;
	data[1].effective &= ~cap1;
	err = capset(&hdr, data);
	if (err)
		return err;

	return 0;
}
+19 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CAP_HELPERS_H
#define __CAP_HELPERS_H

#include <linux/types.h>
#include <linux/capability.h>

#ifndef CAP_PERFMON
#define CAP_PERFMON		38
#endif

#ifndef CAP_BPF
#define CAP_BPF			39
#endif

int cap_enable_effective(__u64 caps, __u64 *old_caps);
int cap_disable_effective(__u64 caps, __u64 *old_caps);

#endif
+8 −36
Original line number Diff line number Diff line
@@ -4,9 +4,9 @@
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/capability.h>

#include "test_progs.h"
#include "cap_helpers.h"
#include "bind_perm.skel.h"

static int duration;
@@ -49,41 +49,11 @@ void try_bind(int family, int port, int expected_errno)
		close(fd);
}

bool cap_net_bind_service(cap_flag_value_t flag)
{
	const cap_value_t cap_net_bind_service = CAP_NET_BIND_SERVICE;
	cap_flag_value_t original_value;
	bool was_effective = false;
	cap_t caps;

	caps = cap_get_proc();
	if (CHECK(!caps, "cap_get_proc", "errno %d", errno))
		goto free_caps;

	if (CHECK(cap_get_flag(caps, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE,
			       &original_value),
		  "cap_get_flag", "errno %d", errno))
		goto free_caps;

	was_effective = (original_value == CAP_SET);

	if (CHECK(cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_bind_service,
			       flag),
		  "cap_set_flag", "errno %d", errno))
		goto free_caps;

	if (CHECK(cap_set_proc(caps), "cap_set_proc", "errno %d", errno))
		goto free_caps;

free_caps:
	CHECK(cap_free(caps), "cap_free", "errno %d", errno);
	return was_effective;
}

void test_bind_perm(void)
{
	bool cap_was_effective;
	const __u64 net_bind_svc_cap = 1ULL << CAP_NET_BIND_SERVICE;
	struct bind_perm *skel;
	__u64 old_caps = 0;
	int cgroup_fd;

	if (create_netns())
@@ -105,7 +75,8 @@ void test_bind_perm(void)
	if (!ASSERT_OK_PTR(skel, "bind_v6_prog"))
		goto close_skeleton;

	cap_was_effective = cap_net_bind_service(CAP_CLEAR);
	ASSERT_OK(cap_disable_effective(net_bind_svc_cap, &old_caps),
		  "cap_disable_effective");

	try_bind(AF_INET, 110, EACCES);
	try_bind(AF_INET6, 110, EACCES);
@@ -113,8 +84,9 @@ void test_bind_perm(void)
	try_bind(AF_INET, 111, 0);
	try_bind(AF_INET6, 111, 0);

	if (cap_was_effective)
		cap_net_bind_service(CAP_SET);
	if (old_caps & net_bind_svc_cap)
		ASSERT_OK(cap_enable_effective(net_bind_svc_cap, NULL),
			  "cap_enable_effective");

close_skeleton:
	bind_perm__destroy(skel);
+25 −63
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@
#include <limits.h>
#include <assert.h>

#include <sys/capability.h>

#include <linux/unistd.h>
#include <linux/filter.h>
#include <linux/bpf_perf_event.h>
@@ -42,6 +40,7 @@
#  define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1
# endif
#endif
#include "cap_helpers.h"
#include "bpf_rand.h"
#include "bpf_util.h"
#include "test_btf.h"
@@ -62,6 +61,10 @@
#define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS	(1 << 0)
#define F_LOAD_WITH_STRICT_ALIGNMENT		(1 << 1)

/* need CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON to load progs */
#define ADMIN_CAPS (1ULL << CAP_NET_ADMIN |	\
		    1ULL << CAP_PERFMON |	\
		    1ULL << CAP_BPF)
#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
static bool unpriv_disabled = false;
static int skips;
@@ -973,47 +976,19 @@ struct libcap {

static int set_admin(bool admin)
{
	cap_t caps;
	/* need CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON to load progs */
	const cap_value_t cap_net_admin = CAP_NET_ADMIN;
	const cap_value_t cap_sys_admin = CAP_SYS_ADMIN;
	struct libcap *cap;
	int ret = -1;

	caps = cap_get_proc();
	if (!caps) {
		perror("cap_get_proc");
		return -1;
	}
	cap = (struct libcap *)caps;
	if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_sys_admin, CAP_CLEAR)) {
		perror("cap_set_flag clear admin");
		goto out;
	}
	if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_admin,
				admin ? CAP_SET : CAP_CLEAR)) {
		perror("cap_set_flag set_or_clear net");
		goto out;
	}
	/* libcap is likely old and simply ignores CAP_BPF and CAP_PERFMON,
	 * so update effective bits manually
	 */
	int err;

	if (admin) {
		cap->data[1].effective |= 1 << (38 /* CAP_PERFMON */ - 32);
		cap->data[1].effective |= 1 << (39 /* CAP_BPF */ - 32);
		err = cap_enable_effective(ADMIN_CAPS, NULL);
		if (err)
			perror("cap_enable_effective(ADMIN_CAPS)");
	} else {
		cap->data[1].effective &= ~(1 << (38 - 32));
		cap->data[1].effective &= ~(1 << (39 - 32));
		err = cap_disable_effective(ADMIN_CAPS, NULL);
		if (err)
			perror("cap_disable_effective(ADMIN_CAPS)");
	}
	if (cap_set_proc(caps)) {
		perror("cap_set_proc");
		goto out;
	}
	ret = 0;
out:
	if (cap_free(caps))
		perror("cap_free");
	return ret;

	return err;
}

static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
@@ -1291,31 +1266,18 @@ static void do_test_single(struct bpf_test *test, bool unpriv,

static bool is_admin(void)
{
	cap_flag_value_t net_priv = CAP_CLEAR;
	bool perfmon_priv = false;
	bool bpf_priv = false;
	struct libcap *cap;
	cap_t caps;

#ifdef CAP_IS_SUPPORTED
	if (!CAP_IS_SUPPORTED(CAP_SETFCAP)) {
		perror("cap_get_flag");
		return false;
	}
#endif
	caps = cap_get_proc();
	if (!caps) {
		perror("cap_get_proc");
	__u64 caps;

	/* The test checks for finer cap as CAP_NET_ADMIN,
	 * CAP_PERFMON, and CAP_BPF instead of CAP_SYS_ADMIN.
	 * Thus, disable CAP_SYS_ADMIN at the beginning.
	 */
	if (cap_disable_effective(1ULL << CAP_SYS_ADMIN, &caps)) {
		perror("cap_disable_effective(CAP_SYS_ADMIN)");
		return false;
	}
	cap = (struct libcap *)caps;
	bpf_priv = cap->data[1].effective & (1 << (39/* CAP_BPF */ - 32));
	perfmon_priv = cap->data[1].effective & (1 << (38/* CAP_PERFMON */ - 32));
	if (cap_get_flag(caps, CAP_NET_ADMIN, CAP_EFFECTIVE, &net_priv))
		perror("cap_get_flag NET");
	if (cap_free(caps))
		perror("cap_free");
	return bpf_priv && perfmon_priv && net_priv == CAP_SET;

	return (caps & ADMIN_CAPS) == ADMIN_CAPS;
}

static void get_unpriv_disabled()