| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2023 Red Hat, Inc. */ |
| #include <test_progs.h> |
| #include "fentry_recursive.skel.h" |
| #include "fentry_recursive_target.skel.h" |
| #include <bpf/btf.h> |
| #include "bpf/libbpf_internal.h" |
| |
| /* Test recursive attachment of tracing progs with more than one nesting level |
| * is not possible. Create a chain of attachment, verify that the last prog |
| * will fail. Depending on the arguments, following cases are tested: |
| * |
| * - Recursive loading of tracing progs, without attaching (attach = false, |
| * detach = false). The chain looks like this: |
| * load target |
| * load fentry1 -> target |
| * load fentry2 -> fentry1 (fail) |
| * |
| * - Recursive attach of tracing progs (attach = true, detach = false). The |
| * chain looks like this: |
| * load target |
| * load fentry1 -> target |
| * attach fentry1 -> target |
| * load fentry2 -> fentry1 (fail) |
| * |
| * - Recursive attach and detach of tracing progs (attach = true, detach = |
| * true). This validates that attach_tracing_prog flag will be set throughout |
| * the whole lifecycle of an fentry prog, independently from whether it's |
| * detached. The chain looks like this: |
| * load target |
| * load fentry1 -> target |
| * attach fentry1 -> target |
| * detach fentry1 |
| * load fentry2 -> fentry1 (fail) |
| */ |
| static void test_recursive_fentry_chain(bool attach, bool detach) |
| { |
| struct fentry_recursive_target *target_skel = NULL; |
| struct fentry_recursive *tracing_chain[2] = {}; |
| struct bpf_program *prog; |
| int prev_fd, err; |
| |
| target_skel = fentry_recursive_target__open_and_load(); |
| if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load")) |
| return; |
| |
| /* Create an attachment chain with two fentry progs */ |
| for (int i = 0; i < 2; i++) { |
| tracing_chain[i] = fentry_recursive__open(); |
| if (!ASSERT_OK_PTR(tracing_chain[i], "fentry_recursive__open")) |
| goto close_prog; |
| |
| /* The first prog in the chain is going to be attached to the target |
| * fentry program, the second one to the previous in the chain. |
| */ |
| prog = tracing_chain[i]->progs.recursive_attach; |
| if (i == 0) { |
| prev_fd = bpf_program__fd(target_skel->progs.test1); |
| err = bpf_program__set_attach_target(prog, prev_fd, "test1"); |
| } else { |
| prev_fd = bpf_program__fd(tracing_chain[i-1]->progs.recursive_attach); |
| err = bpf_program__set_attach_target(prog, prev_fd, "recursive_attach"); |
| } |
| |
| if (!ASSERT_OK(err, "bpf_program__set_attach_target")) |
| goto close_prog; |
| |
| err = fentry_recursive__load(tracing_chain[i]); |
| /* The first attach should succeed, the second fail */ |
| if (i == 0) { |
| if (!ASSERT_OK(err, "fentry_recursive__load")) |
| goto close_prog; |
| |
| if (attach) { |
| err = fentry_recursive__attach(tracing_chain[i]); |
| if (!ASSERT_OK(err, "fentry_recursive__attach")) |
| goto close_prog; |
| } |
| |
| if (detach) { |
| /* Flag attach_tracing_prog should still be set, preventing |
| * attachment of the following prog. |
| */ |
| fentry_recursive__detach(tracing_chain[i]); |
| } |
| } else { |
| if (!ASSERT_ERR(err, "fentry_recursive__load")) |
| goto close_prog; |
| } |
| } |
| |
| close_prog: |
| fentry_recursive_target__destroy(target_skel); |
| for (int i = 0; i < 2; i++) { |
| fentry_recursive__destroy(tracing_chain[i]); |
| } |
| } |
| |
| void test_recursive_fentry(void) |
| { |
| if (test__start_subtest("attach")) |
| test_recursive_fentry_chain(true, false); |
| if (test__start_subtest("load")) |
| test_recursive_fentry_chain(false, false); |
| if (test__start_subtest("detach")) |
| test_recursive_fentry_chain(true, true); |
| } |
| |
| /* Test that a tracing prog reattachment (when we land in |
| * "prog->aux->dst_trampoline and tgt_prog is NULL" branch in |
| * bpf_tracing_prog_attach) does not lead to a crash due to missing attach_btf |
| */ |
| void test_fentry_attach_btf_presence(void) |
| { |
| struct fentry_recursive_target *target_skel = NULL; |
| struct fentry_recursive *tracing_skel = NULL; |
| struct bpf_program *prog; |
| int err, link_fd, tgt_prog_fd; |
| |
| target_skel = fentry_recursive_target__open_and_load(); |
| if (!ASSERT_OK_PTR(target_skel, "fentry_recursive_target__open_and_load")) |
| goto close_prog; |
| |
| tracing_skel = fentry_recursive__open(); |
| if (!ASSERT_OK_PTR(tracing_skel, "fentry_recursive__open")) |
| goto close_prog; |
| |
| prog = tracing_skel->progs.recursive_attach; |
| tgt_prog_fd = bpf_program__fd(target_skel->progs.fentry_target); |
| err = bpf_program__set_attach_target(prog, tgt_prog_fd, "fentry_target"); |
| if (!ASSERT_OK(err, "bpf_program__set_attach_target")) |
| goto close_prog; |
| |
| err = fentry_recursive__load(tracing_skel); |
| if (!ASSERT_OK(err, "fentry_recursive__load")) |
| goto close_prog; |
| |
| tgt_prog_fd = bpf_program__fd(tracing_skel->progs.recursive_attach); |
| link_fd = bpf_link_create(tgt_prog_fd, 0, BPF_TRACE_FENTRY, NULL); |
| if (!ASSERT_GE(link_fd, 0, "link_fd")) |
| goto close_prog; |
| |
| fentry_recursive__detach(tracing_skel); |
| |
| err = fentry_recursive__attach(tracing_skel); |
| ASSERT_ERR(err, "fentry_recursive__attach"); |
| |
| close_prog: |
| fentry_recursive_target__destroy(target_skel); |
| fentry_recursive__destroy(tracing_skel); |
| } |