From 04981c716acab6b7a81f672f217e5c47ee42a0b6 Mon Sep 17 00:00:00 2001 From: Tony Ambardar Date: Mon, 28 Nov 2022 20:43:19 -0800 Subject: [PATCH] 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 --- ...d-helpers-to-directly-use-the-capget.patch | 123 ++++++++++++ ...move-libcap-usage-from-test_verifier.patch | 188 ++++++++++++++++++ ...-Remove-libcap-usage-from-test_progs.patch | 122 ++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 target/linux/generic/backport-5.15/060-v5.18-01-bpf-selftests-Add-helpers-to-directly-use-the-capget.patch create mode 100644 target/linux/generic/backport-5.15/060-v5.18-02-bpf-selftests-Remove-libcap-usage-from-test_verifier.patch create mode 100644 target/linux/generic/backport-5.15/060-v5.18-03-bpf-selftests-Remove-libcap-usage-from-test_progs.patch 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 +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 +Signed-off-by: Alexei Starovoitov +Acked-by: John Fastabend +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 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 ++#include ++ ++#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 +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 +Signed-off-by: Alexei Starovoitov +Acked-by: John Fastabend +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 + #include + +-#include +- + #include + #include + #include +@@ -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 +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 +Signed-off-by: Alexei Starovoitov +Reviewed-by: Stanislav Fomichev +Acked-by: John Fastabend +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 + #include + #include +-#include + + #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); -- 2.30.2