Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add custom SEC() handling selftest

Add a selftest validating various aspects of libbpf's handling of custom
SEC() handlers. It also demonstrates how libraries can ensure very early
callbacks registration and unregistration using
__attribute__((constructor))/__attribute__((destructor)) functions.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
Link: https://lore.kernel.org/bpf/20220305010129.1549719-4-andrii@kernel.org

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
aa963bcb 697f104d

+239
+176
tools/testing/selftests/bpf/prog_tests/custom_sec_handlers.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2022 Facebook */ 3 + 4 + #include <test_progs.h> 5 + #include "test_custom_sec_handlers.skel.h" 6 + 7 + #define COOKIE_ABC1 1 8 + #define COOKIE_ABC2 2 9 + #define COOKIE_CUSTOM 3 10 + #define COOKIE_FALLBACK 4 11 + #define COOKIE_KPROBE 5 12 + 13 + static int custom_setup_prog(struct bpf_program *prog, long cookie) 14 + { 15 + if (cookie == COOKIE_ABC1) 16 + bpf_program__set_autoload(prog, false); 17 + 18 + return 0; 19 + } 20 + 21 + static int custom_prepare_load_prog(struct bpf_program *prog, 22 + struct bpf_prog_load_opts *opts, long cookie) 23 + { 24 + if (cookie == COOKIE_FALLBACK) 25 + opts->prog_flags |= BPF_F_SLEEPABLE; 26 + else if (cookie == COOKIE_ABC1) 27 + ASSERT_FALSE(true, "unexpected preload for abc"); 28 + 29 + return 0; 30 + } 31 + 32 + static int custom_attach_prog(const struct bpf_program *prog, long cookie, 33 + struct bpf_link **link) 34 + { 35 + switch (cookie) { 36 + case COOKIE_ABC2: 37 + *link = bpf_program__attach_raw_tracepoint(prog, "sys_enter"); 38 + return libbpf_get_error(*link); 39 + case COOKIE_CUSTOM: 40 + *link = bpf_program__attach_tracepoint(prog, "syscalls", "sys_enter_nanosleep"); 41 + return libbpf_get_error(*link); 42 + case COOKIE_KPROBE: 43 + case COOKIE_FALLBACK: 44 + /* no auto-attach for SEC("xyz") and SEC("kprobe") */ 45 + *link = NULL; 46 + return 0; 47 + default: 48 + ASSERT_FALSE(true, "unexpected cookie"); 49 + return -EINVAL; 50 + } 51 + } 52 + 53 + static int abc1_id; 54 + static int abc2_id; 55 + static int custom_id; 56 + static int fallback_id; 57 + static int kprobe_id; 58 + 59 + __attribute__((constructor)) 60 + static void register_sec_handlers(void) 61 + { 62 + LIBBPF_OPTS(libbpf_prog_handler_opts, abc1_opts, 63 + .cookie = COOKIE_ABC1, 64 + .prog_setup_fn = custom_setup_prog, 65 + .prog_prepare_load_fn = custom_prepare_load_prog, 66 + .prog_attach_fn = NULL, 67 + ); 68 + LIBBPF_OPTS(libbpf_prog_handler_opts, abc2_opts, 69 + .cookie = COOKIE_ABC2, 70 + .prog_setup_fn = custom_setup_prog, 71 + .prog_prepare_load_fn = custom_prepare_load_prog, 72 + .prog_attach_fn = custom_attach_prog, 73 + ); 74 + LIBBPF_OPTS(libbpf_prog_handler_opts, custom_opts, 75 + .cookie = COOKIE_CUSTOM, 76 + .prog_setup_fn = NULL, 77 + .prog_prepare_load_fn = NULL, 78 + .prog_attach_fn = custom_attach_prog, 79 + ); 80 + 81 + abc1_id = libbpf_register_prog_handler("abc", BPF_PROG_TYPE_RAW_TRACEPOINT, 0, &abc1_opts); 82 + abc2_id = libbpf_register_prog_handler("abc/", BPF_PROG_TYPE_RAW_TRACEPOINT, 0, &abc2_opts); 83 + custom_id = libbpf_register_prog_handler("custom+", BPF_PROG_TYPE_TRACEPOINT, 0, &custom_opts); 84 + } 85 + 86 + __attribute__((destructor)) 87 + static void unregister_sec_handlers(void) 88 + { 89 + libbpf_unregister_prog_handler(abc1_id); 90 + libbpf_unregister_prog_handler(abc2_id); 91 + libbpf_unregister_prog_handler(custom_id); 92 + } 93 + 94 + void test_custom_sec_handlers(void) 95 + { 96 + LIBBPF_OPTS(libbpf_prog_handler_opts, opts, 97 + .prog_setup_fn = custom_setup_prog, 98 + .prog_prepare_load_fn = custom_prepare_load_prog, 99 + .prog_attach_fn = custom_attach_prog, 100 + ); 101 + struct test_custom_sec_handlers* skel; 102 + int err; 103 + 104 + ASSERT_GT(abc1_id, 0, "abc1_id"); 105 + ASSERT_GT(abc2_id, 0, "abc2_id"); 106 + ASSERT_GT(custom_id, 0, "custom_id"); 107 + 108 + /* override libbpf's handle of SEC("kprobe/...") but also allow pure 109 + * SEC("kprobe") due to "kprobe+" specifier. Register it as 110 + * TRACEPOINT, just for fun. 111 + */ 112 + opts.cookie = COOKIE_KPROBE; 113 + kprobe_id = libbpf_register_prog_handler("kprobe+", BPF_PROG_TYPE_TRACEPOINT, 0, &opts); 114 + /* fallback treats everything as BPF_PROG_TYPE_SYSCALL program to test 115 + * setting custom BPF_F_SLEEPABLE bit in preload handler 116 + */ 117 + opts.cookie = COOKIE_FALLBACK; 118 + fallback_id = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_SYSCALL, 0, &opts); 119 + 120 + if (!ASSERT_GT(fallback_id, 0, "fallback_id") /* || !ASSERT_GT(kprobe_id, 0, "kprobe_id")*/) { 121 + if (fallback_id > 0) 122 + libbpf_unregister_prog_handler(fallback_id); 123 + if (kprobe_id > 0) 124 + libbpf_unregister_prog_handler(kprobe_id); 125 + return; 126 + } 127 + 128 + /* open skeleton and validate assumptions */ 129 + skel = test_custom_sec_handlers__open(); 130 + if (!ASSERT_OK_PTR(skel, "skel_open")) 131 + goto cleanup; 132 + 133 + ASSERT_EQ(bpf_program__type(skel->progs.abc1), BPF_PROG_TYPE_RAW_TRACEPOINT, "abc1_type"); 134 + ASSERT_FALSE(bpf_program__autoload(skel->progs.abc1), "abc1_autoload"); 135 + 136 + ASSERT_EQ(bpf_program__type(skel->progs.abc2), BPF_PROG_TYPE_RAW_TRACEPOINT, "abc2_type"); 137 + ASSERT_EQ(bpf_program__type(skel->progs.custom1), BPF_PROG_TYPE_TRACEPOINT, "custom1_type"); 138 + ASSERT_EQ(bpf_program__type(skel->progs.custom2), BPF_PROG_TYPE_TRACEPOINT, "custom2_type"); 139 + ASSERT_EQ(bpf_program__type(skel->progs.kprobe1), BPF_PROG_TYPE_TRACEPOINT, "kprobe1_type"); 140 + ASSERT_EQ(bpf_program__type(skel->progs.xyz), BPF_PROG_TYPE_SYSCALL, "xyz_type"); 141 + 142 + skel->rodata->my_pid = getpid(); 143 + 144 + /* now attempt to load everything */ 145 + err = test_custom_sec_handlers__load(skel); 146 + if (!ASSERT_OK(err, "skel_load")) 147 + goto cleanup; 148 + 149 + /* now try to auto-attach everything */ 150 + err = test_custom_sec_handlers__attach(skel); 151 + if (!ASSERT_OK(err, "skel_attach")) 152 + goto cleanup; 153 + 154 + skel->links.xyz = bpf_program__attach(skel->progs.kprobe1); 155 + ASSERT_EQ(errno, EOPNOTSUPP, "xyz_attach_err"); 156 + ASSERT_ERR_PTR(skel->links.xyz, "xyz_attach"); 157 + 158 + /* trigger programs */ 159 + usleep(1); 160 + 161 + /* SEC("abc") is set to not auto-loaded */ 162 + ASSERT_FALSE(skel->bss->abc1_called, "abc1_called"); 163 + ASSERT_TRUE(skel->bss->abc2_called, "abc2_called"); 164 + ASSERT_TRUE(skel->bss->custom1_called, "custom1_called"); 165 + ASSERT_TRUE(skel->bss->custom2_called, "custom2_called"); 166 + /* SEC("kprobe") shouldn't be auto-attached */ 167 + ASSERT_FALSE(skel->bss->kprobe1_called, "kprobe1_called"); 168 + /* SEC("xyz") shouldn't be auto-attached */ 169 + ASSERT_FALSE(skel->bss->xyz_called, "xyz_called"); 170 + 171 + cleanup: 172 + test_custom_sec_handlers__destroy(skel); 173 + 174 + ASSERT_OK(libbpf_unregister_prog_handler(fallback_id), "unregister_fallback"); 175 + ASSERT_OK(libbpf_unregister_prog_handler(kprobe_id), "unregister_kprobe"); 176 + }
+63
tools/testing/selftests/bpf/progs/test_custom_sec_handlers.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2022 Facebook */ 3 + 4 + #include "vmlinux.h" 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_tracing.h> 7 + 8 + const volatile int my_pid; 9 + 10 + bool abc1_called; 11 + bool abc2_called; 12 + bool custom1_called; 13 + bool custom2_called; 14 + bool kprobe1_called; 15 + bool xyz_called; 16 + 17 + SEC("abc") 18 + int abc1(void *ctx) 19 + { 20 + abc1_called = true; 21 + return 0; 22 + } 23 + 24 + SEC("abc/whatever") 25 + int abc2(void *ctx) 26 + { 27 + abc2_called = true; 28 + return 0; 29 + } 30 + 31 + SEC("custom") 32 + int custom1(void *ctx) 33 + { 34 + custom1_called = true; 35 + return 0; 36 + } 37 + 38 + SEC("custom/something") 39 + int custom2(void *ctx) 40 + { 41 + custom2_called = true; 42 + return 0; 43 + } 44 + 45 + SEC("kprobe") 46 + int kprobe1(void *ctx) 47 + { 48 + kprobe1_called = true; 49 + return 0; 50 + } 51 + 52 + SEC("xyz/blah") 53 + int xyz(void *ctx) 54 + { 55 + int whatever; 56 + 57 + /* use sleepable helper, custom handler should set sleepable flag */ 58 + bpf_copy_from_user(&whatever, sizeof(whatever), NULL); 59 + xyz_called = true; 60 + return 0; 61 + } 62 + 63 + char _license[] SEC("license") = "GPL";