diff --git a/include/linux/bpf.h b/include/linux/bpf.h index bd435b20bca140f15255d75091260d9ada850ea0..4e6955bf06e7fee0866370d8847ed52788409887 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -179,6 +179,7 @@ struct bpf_map_owner { bool jited; u64 storage_cookie[MAX_BPF_CGROUP_STORAGE_TYPE]; const struct btf_type *attach_func_proto; + enum bpf_attach_type expected_attach_type; }; struct bpf_map { diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 4d72a8651de58557bc3e11ce1a9c097dac9f6d02..6abc58b682a486139c0d1e282681216b9bcee711 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1794,6 +1794,7 @@ bool bpf_prog_map_compatible(struct bpf_map *map, map->owner->type = prog_type; map->owner->jited = fp->jited; map->owner->attach_func_proto = aux->attach_func_proto; + map->owner->expected_attach_type = fp->expected_attach_type; for_each_cgroup_storage_type(i) { map->owner->storage_cookie[i] = aux->cgroup_storage[i] ? @@ -1803,6 +1804,10 @@ bool bpf_prog_map_compatible(struct bpf_map *map, } else { ret = map->owner->type == prog_type && map->owner->jited == fp->jited; + if (ret && + map->map_type == BPF_MAP_TYPE_PROG_ARRAY && + map->owner->expected_attach_type != fp->expected_attach_type) + ret = false; for_each_cgroup_storage_type(i) { if (!ret) break; diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c b/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c index 88ef3ec8ac4c21ca6972da8b925ec4e4afb4612d..572616eff047b7bb815d170b368db46d7436194c 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c @@ -4,6 +4,8 @@ #include #include "test_xdp_devmap_helpers.skel.h" +#include "test_xdp_devmap_tailcall.skel.h" +#include "xdp_tailcall_devmap.skel.h" #include "test_xdp_with_devmap_helpers.skel.h" #define IFINDEX_LO 1 @@ -78,12 +80,70 @@ void test_neg_xdp_devmap_helpers(void) } } +static void test_xdp_devmap_tailcall(enum bpf_attach_type prog_dev, + enum bpf_attach_type prog_tail, + bool expect_reject) +{ + struct test_xdp_devmap_tailcall *skel = NULL; + struct xdp_tailcall_devmap *tailcall_skel = NULL; + __u32 duration = 0, zero = 0; + int err, map_fd, prog_fd; + + skel = test_xdp_devmap_tailcall__open(); + if (!ASSERT_OK_PTR(skel, "test_xdp_devmap_tailcall__open")) + return; + + bpf_program__set_expected_attach_type(skel->progs.xdp_devmap, prog_dev); + err = test_xdp_devmap_tailcall__load(skel); + if (prog_dev == BPF_XDP_DEVMAP) { + if (!ASSERT_OK(err, "test_xdp_devmap_tailcall__load")) + goto out_close; + } else { + /* + * Will be rejected by verifier due to xdp_is_valid_access + * failure, because programs with expected_attach_type + * other than BPF_XDP_DEVMAP are not allowed to obtain + * ctx->egress_ifindex. + */ + ASSERT_ERR(err, "test_xdp_devmap_tailcall__load"); + goto out_close; + } + + map_fd = bpf_map__fd(skel->maps.xdp_map); + prog_fd = bpf_program__fd(skel->progs.xdp_devmap); + err = bpf_map_update_elem(map_fd, &zero, &prog_fd, BPF_ANY); + CHECK(err, "Add program to devmap entry", + "err %d errno %d\n", err, errno); + + tailcall_skel = xdp_tailcall_devmap__open(); + if (!ASSERT_OK_PTR(tailcall_skel, "xdp_tailcall_devmap__open")) + goto out_close; + + bpf_map__reuse_fd(tailcall_skel->maps.xdp_map, map_fd); + bpf_program__set_expected_attach_type(tailcall_skel->progs.xdp_entry, + prog_tail); + + err = xdp_tailcall_devmap__load(tailcall_skel); + if (expect_reject) + ASSERT_ERR(err, "xdp_tailcall_devmap__load"); + else + ASSERT_OK(err, "xdp_tailcall_devmap__load"); + +out_close: + test_xdp_devmap_tailcall__destroy(skel); + xdp_tailcall_devmap__destroy(tailcall_skel); +} void test_xdp_devmap_attach(void) { if (test__start_subtest("DEVMAP with programs in entries")) test_xdp_with_devmap_helpers(); - if (test__start_subtest("Verifier check of DEVMAP programs")) + if (test__start_subtest("Verifier check of DEVMAP programs")) { test_neg_xdp_devmap_helpers(); + test_xdp_devmap_tailcall(BPF_XDP_DEVMAP, BPF_XDP_DEVMAP, false); + test_xdp_devmap_tailcall(0, 0, true); + test_xdp_devmap_tailcall(BPF_XDP_DEVMAP, 0, true); + test_xdp_devmap_tailcall(0, BPF_XDP_DEVMAP, true); + } } diff --git a/tools/testing/selftests/bpf/progs/test_xdp_devmap_tailcall.c b/tools/testing/selftests/bpf/progs/test_xdp_devmap_tailcall.c new file mode 100644 index 0000000000000000000000000000000000000000..e4f203142c4ed6e28d782a3220ba903d00256af7 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_devmap_tailcall.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} xdp_map SEC(".maps"); + +SEC("xdp") +int xdp_devmap(struct xdp_md *ctx) +{ + return ctx->egress_ifindex; +} diff --git a/tools/testing/selftests/bpf/progs/xdp_tailcall_devmap.c b/tools/testing/selftests/bpf/progs/xdp_tailcall_devmap.c new file mode 100644 index 0000000000000000000000000000000000000000..a047c22661820a574d7244fb954f22d5e7b67510 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/xdp_tailcall_devmap.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "vmlinux.h" +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} xdp_map SEC(".maps"); + +SEC("xdp") +int xdp_entry(struct xdp_md *ctx) +{ + bpf_tail_call(ctx, &xdp_map, 0); + return 0; +}