[openwrt/openwrt] kernel: backport libcap workaround for BPF selftests

LEDE Commits lede-commits at lists.infradead.org
Fri Jun 9 04:23:42 PDT 2023


hauke pushed a commit to openwrt/openwrt.git, branch openwrt-23.05:
https://git.openwrt.org/11677aa44cc0ab423c520201ce067be2bdef730f

commit 11677aa44cc0ab423c520201ce067be2bdef730f
Author: Tony Ambardar <itugrok at yahoo.com>
AuthorDate: Mon Nov 28 20:43:19 2022 -0800

    kernel: backport libcap workaround for BPF selftests
    
    Recent libcap versions (>= 2.60) cause problems with BPF kselftests, so
    backport an upstream patch that replaces libcap and drops the dependency.
    
    Signed-off-by: Tony Ambardar <itugrok at yahoo.com>
    (cherry picked from commit 04981c716acab6b7a81f672f217e5c47ee42a0b6)
---
 ...ts-Add-helpers-to-directly-use-the-capget.patch | 123 ++++++++++++++
 ...ts-Remove-libcap-usage-from-test_verifier.patch | 188 +++++++++++++++++++++
 ...tests-Remove-libcap-usage-from-test_progs.patch | 122 +++++++++++++
 3 files changed, 433 insertions(+)

diff --git a/target/linux/generic/backport-5.15/060-v5.18-01-bpf-selftests-Add-helpers-to-directly-use-the-capget.patch b/target/linux/generic/backport-5.15/060-v5.18-01-bpf-selftests-Add-helpers-to-directly-use-the-capget.patch
new file mode 100644
index 0000000000..4d544a30f5
--- /dev/null
+++ b/target/linux/generic/backport-5.15/060-v5.18-01-bpf-selftests-Add-helpers-to-directly-use-the-capget.patch
@@ -0,0 +1,123 @@
+From 5287acc6f097c0c18e54401b611a877a3083b68c Mon Sep 17 00:00:00 2001
+From: Martin KaFai Lau <kafai at fb.com>
+Date: Wed, 16 Mar 2022 10:38:23 -0700
+Subject: [PATCH 1/3] bpf: selftests: Add helpers to directly use the capget
+ and capset syscall
+
+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.
+
+The bpf selftest usage only needs to enable and disable the effective
+caps of the running task.  It is easier to directly syscall the
+capget and capset instead.  It can also remove the libcap
+library dependency.
+
+The cap_helpers.{c,h} is added.  One __u64 is used for all CAP_*
+bits instead of two __u32.
+
+Signed-off-by: Martin KaFai Lau <kafai at fb.com>
+Signed-off-by: Alexei Starovoitov <ast at kernel.org>
+Acked-by: John Fastabend <john.fastabend at gmail.com>
+Link: https://lore.kernel.org/bpf/20220316173823.2036955-1-kafai@fb.com
+---
+ tools/testing/selftests/bpf/cap_helpers.c | 67 +++++++++++++++++++++++
+ tools/testing/selftests/bpf/cap_helpers.h | 19 +++++++
+ 2 files changed, 86 insertions(+)
+ create mode 100644 tools/testing/selftests/bpf/cap_helpers.c
+ create mode 100644 tools/testing/selftests/bpf/cap_helpers.h
+
+--- /dev/null
++++ b/tools/testing/selftests/bpf/cap_helpers.c
+@@ -0,0 +1,67 @@
++// 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;
++}
+--- /dev/null
++++ b/tools/testing/selftests/bpf/cap_helpers.h
+@@ -0,0 +1,19 @@
++/* 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
diff --git a/target/linux/generic/backport-5.15/060-v5.18-02-bpf-selftests-Remove-libcap-usage-from-test_verifier.patch b/target/linux/generic/backport-5.15/060-v5.18-02-bpf-selftests-Remove-libcap-usage-from-test_verifier.patch
new file mode 100644
index 0000000000..cc60b54340
--- /dev/null
+++ b/target/linux/generic/backport-5.15/060-v5.18-02-bpf-selftests-Remove-libcap-usage-from-test_verifier.patch
@@ -0,0 +1,188 @@
+From 847a6b7ee906be874f0cae279c8de902a7d3f092 Mon Sep 17 00:00:00 2001
+From: Martin KaFai Lau <kafai at fb.com>
+Date: Wed, 16 Mar 2022 10:38:29 -0700
+Subject: [PATCH 2/3] bpf: selftests: Remove libcap usage from test_verifier
+
+This patch removes the libcap usage from test_verifier.
+The cap_*_effective() helpers added in the earlier patch are
+used instead.
+
+Signed-off-by: Martin KaFai Lau <kafai at fb.com>
+Signed-off-by: Alexei Starovoitov <ast at kernel.org>
+Acked-by: John Fastabend <john.fastabend at gmail.com>
+Link: https://lore.kernel.org/bpf/20220316173829.2038682-1-kafai@fb.com
+---
+ tools/testing/selftests/bpf/Makefile        | 31 +++++---
+ tools/testing/selftests/bpf/test_verifier.c | 88 ++++++---------------
+ 2 files changed, 46 insertions(+), 73 deletions(-)
+
+--- a/tools/testing/selftests/bpf/Makefile
++++ b/tools/testing/selftests/bpf/Makefile
+@@ -189,16 +189,27 @@ TEST_GEN_PROGS_EXTENDED += $(DEFAULT_BPF
+ 
+ $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/test_stub.o $(BPFOBJ)
+ 
+-$(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
+-$(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c
+-$(OUTPUT)/test_sock: cgroup_helpers.c
+-$(OUTPUT)/test_sock_addr: cgroup_helpers.c
+-$(OUTPUT)/test_sockmap: cgroup_helpers.c
+-$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
+-$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
+-$(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
+-$(OUTPUT)/test_sock_fields: cgroup_helpers.c
+-$(OUTPUT)/test_sysctl: cgroup_helpers.c
++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)
++$(OUTPUT)/test_sock: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_sock_addr: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)
++$(OUTPUT)/get_cgroup_id_user: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_cgroup_storage: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_sysctl: $(CGROUP_HELPERS) $(TESTING_HELPERS)
++$(OUTPUT)/test_tag: $(TESTING_HELPERS)
++$(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) $(CAP_HELPERS)
+ 
+ BPFTOOL ?= $(DEFAULT_BPFTOOL)
+ $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)    \
+--- a/tools/testing/selftests/bpf/test_verifier.c
++++ b/tools/testing/selftests/bpf/test_verifier.c
+@@ -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>
+@@ -43,6 +41,7 @@
+ # endif
+ #endif
+ #include "bpf_rlimit.h"
++#include "cap_helpers.h"
+ #include "bpf_rand.h"
+ #include "bpf_util.h"
+ #include "test_btf.h"
+@@ -59,6 +58,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;
+@@ -940,47 +943,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,
+@@ -1246,31 +1221,18 @@ fail_log:
+ 
+ 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()
diff --git a/target/linux/generic/backport-5.15/060-v5.18-03-bpf-selftests-Remove-libcap-usage-from-test_progs.patch b/target/linux/generic/backport-5.15/060-v5.18-03-bpf-selftests-Remove-libcap-usage-from-test_progs.patch
new file mode 100644
index 0000000000..9badba7f8e
--- /dev/null
+++ b/target/linux/generic/backport-5.15/060-v5.18-03-bpf-selftests-Remove-libcap-usage-from-test_progs.patch
@@ -0,0 +1,122 @@
+From 1ac00fea13c576e2b13dabf9a72ad3034e3bb804 Mon Sep 17 00:00:00 2001
+From: Martin KaFai Lau <kafai at fb.com>
+Date: Wed, 16 Mar 2022 10:38:35 -0700
+Subject: [PATCH 3/3] bpf: selftests: Remove libcap usage from test_progs
+
+This patch removes the libcap usage from test_progs.
+bind_perm.c is the only user.  cap_*_effective() helpers added in the
+earlier patch are directly used instead.
+
+No other selftest binary is using libcap, so '-lcap' is also removed
+from the Makefile.
+
+Signed-off-by: Martin KaFai Lau <kafai at fb.com>
+Signed-off-by: Alexei Starovoitov <ast at kernel.org>
+Reviewed-by: Stanislav Fomichev <sdf at google.com>
+Acked-by: John Fastabend <john.fastabend at gmail.com>
+Link: https://lore.kernel.org/bpf/20220316173835.2039334-1-kafai@fb.com
+---
+ tools/testing/selftests/bpf/Makefile          |  5 ++-
+ .../selftests/bpf/prog_tests/bind_perm.c      | 44 ++++---------------
+ 2 files changed, 11 insertions(+), 38 deletions(-)
+
+--- a/tools/testing/selftests/bpf/Makefile
++++ b/tools/testing/selftests/bpf/Makefile
+@@ -26,7 +26,7 @@ CFLAGS += -g -O0 -rdynamic -Wall $(GENFL
+ 	  -I$(TOOLSINCDIR) -I$(APIDIR) -I$(OUTPUT)			\
+ 	  -Dbpf_prog_load=bpf_prog_test_load				\
+ 	  -Dbpf_load_program=bpf_test_load_program
+-LDLIBS += -lcap -lelf -lz -lrt -lpthread
++LDLIBS += -lelf -lz -lrt -lpthread
+ 
+ # Silence some warnings when compiled with clang
+ ifneq ($(LLVM),)
+@@ -471,7 +471,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)
+--- a/tools/testing/selftests/bpf/prog_tests/bind_perm.c
++++ b/tools/testing/selftests/bpf/prog_tests/bind_perm.c
+@@ -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 @@ close_socket:
+ 		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);




More information about the lede-commits mailing list