diff --git a/include/linux/bpf.h b/include/linux/bpf.h index fc67547477812feb4d8ccc057a58e1a4d4876a24..c9c7e17e0d1b6932f6b6e21b81692dfcef25760e 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -945,6 +945,7 @@ struct bpf_array_aux { * the same prog type and JITed flag. */ struct { + const struct btf_type *attach_func_proto; spinlock_t lock; enum bpf_prog_type type; bool jited; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 3884803e030520b46173403f0eb55c38b277fbc9..bc9bc96c0c4b36586759fd83d08da96d80cc6ff3 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1779,6 +1779,7 @@ bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp) { bool ret; + struct bpf_prog_aux *aux = fp->aux; if (fp->kprobe_override) return false; @@ -1791,10 +1792,24 @@ bool bpf_prog_array_compatible(struct bpf_array *array, */ array->aux->owner.type = fp->type; array->aux->owner.jited = fp->jited; + array->aux->owner.attach_func_proto = aux->attach_func_proto; ret = true; } else { ret = array->aux->owner.type == fp->type && array->aux->owner.jited == fp->jited; + if (ret && + array->aux->owner.attach_func_proto != aux->attach_func_proto) { + switch (fp->type) { + case BPF_PROG_TYPE_TRACING: + case BPF_PROG_TYPE_LSM: + case BPF_PROG_TYPE_EXT: + case BPF_PROG_TYPE_STRUCT_OPS: + ret = false; + break; + default: + break; + } + } } spin_unlock(&array->aux->owner.lock); return ret; diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c index 6ab29226c99b657c7a62aa223bed7fdef86fc275..18c2e3046da32657ffaae586eb81f6e49a47a66a 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c +++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c @@ -13,6 +13,7 @@ #include #include "lsm.skel.h" +#include "lsm_tailcall.skel.h" char *CMD_ARGS[] = {"true", NULL}; @@ -52,7 +53,7 @@ int exec_cmd(int *monitored_pid) return -EINVAL; } -void test_test_lsm(void) +static void test_lsm_basic(void) { struct lsm *skel = NULL; int err, duration = 0; @@ -93,3 +94,46 @@ void test_test_lsm(void) close_prog: lsm__destroy(skel); } + +static void test_lsm_tailcall(void) +{ + struct lsm_tailcall *skel = NULL; + int map_fd, prog_fd; + int err, key; + + skel = lsm_tailcall__open_and_load(); + if (!ASSERT_OK_PTR(skel, "lsm_tailcall__skel_load")) + goto close_prog; + + map_fd = bpf_map__fd(skel->maps.jmp_table); + if (CHECK_FAIL(map_fd < 0)) + goto close_prog; + + prog_fd = bpf_program__fd(skel->progs.lsm_file_permission_prog); + if (CHECK_FAIL(prog_fd < 0)) + goto close_prog; + + key = 0; + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); + if (CHECK_FAIL(!err)) + goto close_prog; + + prog_fd = bpf_program__fd(skel->progs.lsm_file_alloc_security_prog); + if (CHECK_FAIL(prog_fd < 0)) + goto close_prog; + + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); + if (CHECK_FAIL(err)) + goto close_prog; + +close_prog: + lsm_tailcall__destroy(skel); +} + +void test_test_lsm(void) +{ + if (test__start_subtest("lsm_basic")) + test_lsm_basic(); + if (test__start_subtest("lsm_tailcall")) + test_lsm_tailcall(); +} diff --git a/tools/testing/selftests/bpf/progs/lsm_tailcall.c b/tools/testing/selftests/bpf/progs/lsm_tailcall.c new file mode 100644 index 0000000000000000000000000000000000000000..49c075ce2d4c361e7c1962f0eaf030617a2a2f06 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_tailcall.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Huawei Technologies Co., Ltd */ + +#include "vmlinux.h" +#include +#include + +char _license[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} jmp_table SEC(".maps"); + +SEC("lsm/file_permission") +int lsm_file_permission_prog(void *ctx) +{ + return 0; +} + +SEC("lsm/file_alloc_security") +int lsm_file_alloc_security_prog(void *ctx) +{ + return 0; +} + +SEC("lsm/file_alloc_security") +int lsm_file_alloc_security_entry(void *ctx) +{ + bpf_tail_call_static(ctx, &jmp_table, 0); + return 0; +}