1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "test_attach_kprobe_sleepable.skel.h" 4 #include "test_attach_probe_manual.skel.h" 5 #include "test_attach_probe.skel.h" 6 7 /* this is how USDT semaphore is actually defined, except volatile modifier */ 8 volatile unsigned short uprobe_ref_ctr __attribute__((unused)) __attribute((section(".probes"))); 9 10 /* uprobe attach point */ 11 static noinline void trigger_func(void) 12 { 13 asm volatile (""); 14 } 15 16 /* attach point for byname uprobe */ 17 static noinline void trigger_func2(void) 18 { 19 asm volatile (""); 20 } 21 22 /* attach point for byname sleepable uprobe */ 23 static noinline void trigger_func3(void) 24 { 25 asm volatile (""); 26 } 27 28 /* attach point for ref_ctr */ 29 static noinline void trigger_func4(void) 30 { 31 asm volatile (""); 32 } 33 34 static char test_data[] = "test_data"; 35 36 /* manual attach kprobe/kretprobe/uprobe/uretprobe testings */ 37 static void test_attach_probe_manual(enum probe_attach_mode attach_mode) 38 { 39 DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 40 DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts); 41 struct bpf_link *kprobe_link, *kretprobe_link; 42 struct bpf_link *uprobe_link, *uretprobe_link; 43 struct test_attach_probe_manual *skel; 44 ssize_t uprobe_offset; 45 46 skel = test_attach_probe_manual__open_and_load(); 47 if (!ASSERT_OK_PTR(skel, "skel_kprobe_manual_open_and_load")) 48 return; 49 50 uprobe_offset = get_uprobe_offset(&trigger_func); 51 if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) 52 goto cleanup; 53 54 /* manual-attach kprobe/kretprobe */ 55 kprobe_opts.attach_mode = attach_mode; 56 kprobe_opts.retprobe = false; 57 kprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe, 58 SYS_NANOSLEEP_KPROBE_NAME, 59 &kprobe_opts); 60 if (!ASSERT_OK_PTR(kprobe_link, "attach_kprobe")) 61 goto cleanup; 62 skel->links.handle_kprobe = kprobe_link; 63 64 kprobe_opts.retprobe = true; 65 kretprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe, 66 SYS_NANOSLEEP_KPROBE_NAME, 67 &kprobe_opts); 68 if (!ASSERT_OK_PTR(kretprobe_link, "attach_kretprobe")) 69 goto cleanup; 70 skel->links.handle_kretprobe = kretprobe_link; 71 72 /* manual-attach uprobe/uretprobe */ 73 uprobe_opts.attach_mode = attach_mode; 74 uprobe_opts.ref_ctr_offset = 0; 75 uprobe_opts.retprobe = false; 76 uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe, 77 0 /* self pid */, 78 "/proc/self/exe", 79 uprobe_offset, 80 &uprobe_opts); 81 if (!ASSERT_OK_PTR(uprobe_link, "attach_uprobe")) 82 goto cleanup; 83 skel->links.handle_uprobe = uprobe_link; 84 85 uprobe_opts.retprobe = true; 86 uretprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe, 87 -1 /* any pid */, 88 "/proc/self/exe", 89 uprobe_offset, &uprobe_opts); 90 if (!ASSERT_OK_PTR(uretprobe_link, "attach_uretprobe")) 91 goto cleanup; 92 skel->links.handle_uretprobe = uretprobe_link; 93 94 /* attach uprobe by function name manually */ 95 uprobe_opts.func_name = "trigger_func2"; 96 uprobe_opts.retprobe = false; 97 uprobe_opts.ref_ctr_offset = 0; 98 skel->links.handle_uprobe_byname = 99 bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_byname, 100 0 /* this pid */, 101 "/proc/self/exe", 102 0, &uprobe_opts); 103 if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname, "attach_uprobe_byname")) 104 goto cleanup; 105 106 /* trigger & validate kprobe && kretprobe */ 107 usleep(1); 108 109 /* trigger & validate uprobe & uretprobe */ 110 trigger_func(); 111 112 /* trigger & validate uprobe attached by name */ 113 trigger_func2(); 114 115 ASSERT_EQ(skel->bss->kprobe_res, 1, "check_kprobe_res"); 116 ASSERT_EQ(skel->bss->kretprobe_res, 2, "check_kretprobe_res"); 117 ASSERT_EQ(skel->bss->uprobe_res, 3, "check_uprobe_res"); 118 ASSERT_EQ(skel->bss->uretprobe_res, 4, "check_uretprobe_res"); 119 ASSERT_EQ(skel->bss->uprobe_byname_res, 5, "check_uprobe_byname_res"); 120 121 cleanup: 122 test_attach_probe_manual__destroy(skel); 123 } 124 125 static void test_attach_probe_auto(struct test_attach_probe *skel) 126 { 127 struct bpf_link *uprobe_err_link; 128 129 /* auto-attachable kprobe and kretprobe */ 130 skel->links.handle_kprobe_auto = bpf_program__attach(skel->progs.handle_kprobe_auto); 131 ASSERT_OK_PTR(skel->links.handle_kprobe_auto, "attach_kprobe_auto"); 132 133 skel->links.handle_kretprobe_auto = bpf_program__attach(skel->progs.handle_kretprobe_auto); 134 ASSERT_OK_PTR(skel->links.handle_kretprobe_auto, "attach_kretprobe_auto"); 135 136 /* verify auto-attach fails for old-style uprobe definition */ 137 uprobe_err_link = bpf_program__attach(skel->progs.handle_uprobe_byname); 138 if (!ASSERT_EQ(libbpf_get_error(uprobe_err_link), -EOPNOTSUPP, 139 "auto-attach should fail for old-style name")) 140 return; 141 142 /* verify auto-attach works */ 143 skel->links.handle_uretprobe_byname = 144 bpf_program__attach(skel->progs.handle_uretprobe_byname); 145 if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname, "attach_uretprobe_byname")) 146 return; 147 148 /* trigger & validate kprobe && kretprobe */ 149 usleep(1); 150 151 /* trigger & validate uprobe attached by name */ 152 trigger_func2(); 153 154 ASSERT_EQ(skel->bss->kprobe2_res, 11, "check_kprobe_auto_res"); 155 ASSERT_EQ(skel->bss->kretprobe2_res, 22, "check_kretprobe_auto_res"); 156 ASSERT_EQ(skel->bss->uretprobe_byname_res, 6, "check_uretprobe_byname_res"); 157 } 158 159 static void test_uprobe_lib(struct test_attach_probe *skel) 160 { 161 DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 162 FILE *devnull; 163 164 /* test attach by name for a library function, using the library 165 * as the binary argument. libc.so.6 will be resolved via dlopen()/dlinfo(). 166 */ 167 uprobe_opts.func_name = "fopen"; 168 uprobe_opts.retprobe = false; 169 skel->links.handle_uprobe_byname2 = 170 bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_byname2, 171 0 /* this pid */, 172 "libc.so.6", 173 0, &uprobe_opts); 174 if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname2, "attach_uprobe_byname2")) 175 return; 176 177 uprobe_opts.func_name = "fclose"; 178 uprobe_opts.retprobe = true; 179 skel->links.handle_uretprobe_byname2 = 180 bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe_byname2, 181 -1 /* any pid */, 182 "libc.so.6", 183 0, &uprobe_opts); 184 if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname2, "attach_uretprobe_byname2")) 185 return; 186 187 /* trigger & validate shared library u[ret]probes attached by name */ 188 devnull = fopen("/dev/null", "r"); 189 fclose(devnull); 190 191 ASSERT_EQ(skel->bss->uprobe_byname2_res, 7, "check_uprobe_byname2_res"); 192 ASSERT_EQ(skel->bss->uretprobe_byname2_res, 8, "check_uretprobe_byname2_res"); 193 } 194 195 static void test_uprobe_ref_ctr(struct test_attach_probe *skel) 196 { 197 DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 198 struct bpf_link *uprobe_link, *uretprobe_link; 199 ssize_t uprobe_offset, ref_ctr_offset; 200 201 uprobe_offset = get_uprobe_offset(&trigger_func4); 202 if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset_ref_ctr")) 203 return; 204 205 ref_ctr_offset = get_rel_offset((uintptr_t)&uprobe_ref_ctr); 206 if (!ASSERT_GE(ref_ctr_offset, 0, "ref_ctr_offset")) 207 return; 208 209 ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_before"); 210 211 uprobe_opts.retprobe = false; 212 uprobe_opts.ref_ctr_offset = ref_ctr_offset; 213 uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_ref_ctr, 214 0 /* self pid */, 215 "/proc/self/exe", 216 uprobe_offset, 217 &uprobe_opts); 218 if (!ASSERT_OK_PTR(uprobe_link, "attach_uprobe_ref_ctr")) 219 return; 220 skel->links.handle_uprobe_ref_ctr = uprobe_link; 221 222 ASSERT_GT(uprobe_ref_ctr, 0, "uprobe_ref_ctr_after"); 223 224 /* if uprobe uses ref_ctr, uretprobe has to use ref_ctr as well */ 225 uprobe_opts.retprobe = true; 226 uprobe_opts.ref_ctr_offset = ref_ctr_offset; 227 uretprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe_ref_ctr, 228 -1 /* any pid */, 229 "/proc/self/exe", 230 uprobe_offset, &uprobe_opts); 231 if (!ASSERT_OK_PTR(uretprobe_link, "attach_uretprobe_ref_ctr")) 232 return; 233 skel->links.handle_uretprobe_ref_ctr = uretprobe_link; 234 } 235 236 static void test_kprobe_sleepable(void) 237 { 238 struct test_attach_kprobe_sleepable *skel; 239 240 skel = test_attach_kprobe_sleepable__open(); 241 if (!ASSERT_OK_PTR(skel, "skel_kprobe_sleepable_open")) 242 return; 243 244 /* sleepable kprobe test case needs flags set before loading */ 245 if (!ASSERT_OK(bpf_program__set_flags(skel->progs.handle_kprobe_sleepable, 246 BPF_F_SLEEPABLE), "kprobe_sleepable_flags")) 247 goto cleanup; 248 249 if (!ASSERT_OK(test_attach_kprobe_sleepable__load(skel), 250 "skel_kprobe_sleepable_load")) 251 goto cleanup; 252 253 /* sleepable kprobes should not attach successfully */ 254 skel->links.handle_kprobe_sleepable = bpf_program__attach(skel->progs.handle_kprobe_sleepable); 255 ASSERT_ERR_PTR(skel->links.handle_kprobe_sleepable, "attach_kprobe_sleepable"); 256 257 cleanup: 258 test_attach_kprobe_sleepable__destroy(skel); 259 } 260 261 static void test_uprobe_sleepable(struct test_attach_probe *skel) 262 { 263 /* test sleepable uprobe and uretprobe variants */ 264 skel->links.handle_uprobe_byname3_sleepable = bpf_program__attach(skel->progs.handle_uprobe_byname3_sleepable); 265 if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname3_sleepable, "attach_uprobe_byname3_sleepable")) 266 return; 267 268 skel->links.handle_uprobe_byname3 = bpf_program__attach(skel->progs.handle_uprobe_byname3); 269 if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname3, "attach_uprobe_byname3")) 270 return; 271 272 skel->links.handle_uretprobe_byname3_sleepable = bpf_program__attach(skel->progs.handle_uretprobe_byname3_sleepable); 273 if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname3_sleepable, "attach_uretprobe_byname3_sleepable")) 274 return; 275 276 skel->links.handle_uretprobe_byname3 = bpf_program__attach(skel->progs.handle_uretprobe_byname3); 277 if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname3, "attach_uretprobe_byname3")) 278 return; 279 280 skel->bss->user_ptr = test_data; 281 282 /* trigger & validate sleepable uprobe attached by name */ 283 trigger_func3(); 284 285 ASSERT_EQ(skel->bss->uprobe_byname3_sleepable_res, 9, "check_uprobe_byname3_sleepable_res"); 286 ASSERT_EQ(skel->bss->uprobe_byname3_str_sleepable_res, 10, "check_uprobe_byname3_str_sleepable_res"); 287 ASSERT_EQ(skel->bss->uprobe_byname3_res, 11, "check_uprobe_byname3_res"); 288 ASSERT_EQ(skel->bss->uretprobe_byname3_sleepable_res, 12, "check_uretprobe_byname3_sleepable_res"); 289 ASSERT_EQ(skel->bss->uretprobe_byname3_str_sleepable_res, 13, "check_uretprobe_byname3_str_sleepable_res"); 290 ASSERT_EQ(skel->bss->uretprobe_byname3_res, 14, "check_uretprobe_byname3_res"); 291 } 292 293 void test_attach_probe(void) 294 { 295 struct test_attach_probe *skel; 296 297 skel = test_attach_probe__open(); 298 if (!ASSERT_OK_PTR(skel, "skel_open")) 299 return; 300 301 if (!ASSERT_OK(test_attach_probe__load(skel), "skel_load")) 302 goto cleanup; 303 if (!ASSERT_OK_PTR(skel->bss, "check_bss")) 304 goto cleanup; 305 306 if (test__start_subtest("manual-default")) 307 test_attach_probe_manual(PROBE_ATTACH_MODE_DEFAULT); 308 if (test__start_subtest("manual-legacy")) 309 test_attach_probe_manual(PROBE_ATTACH_MODE_LEGACY); 310 if (test__start_subtest("manual-perf")) 311 test_attach_probe_manual(PROBE_ATTACH_MODE_PERF); 312 if (test__start_subtest("manual-link")) 313 test_attach_probe_manual(PROBE_ATTACH_MODE_LINK); 314 315 if (test__start_subtest("auto")) 316 test_attach_probe_auto(skel); 317 if (test__start_subtest("kprobe-sleepable")) 318 test_kprobe_sleepable(); 319 if (test__start_subtest("uprobe-lib")) 320 test_uprobe_lib(skel); 321 if (test__start_subtest("uprobe-sleepable")) 322 test_uprobe_sleepable(skel); 323 if (test__start_subtest("uprobe-ref_ctr")) 324 test_uprobe_ref_ctr(skel); 325 326 cleanup: 327 test_attach_probe__destroy(skel); 328 ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_cleanup"); 329 } 330