1*d5288c51SFlorent Revest (Anthropic) // SPDX-License-Identifier: GPL-2.0
2*d5288c51SFlorent Revest (Anthropic) #include <test_progs.h>
3*d5288c51SFlorent Revest (Anthropic) #include <pthread.h>
4*d5288c51SFlorent Revest (Anthropic) #include <poll.h>
5*d5288c51SFlorent Revest (Anthropic) #include <sys/mman.h>
6*d5288c51SFlorent Revest (Anthropic) #include <linux/userfaultfd.h>
7*d5288c51SFlorent Revest (Anthropic) #include "tramp_prog_detach.skel.h"
8*d5288c51SFlorent Revest (Anthropic) #include "testing_helpers.h"
9*d5288c51SFlorent Revest (Anthropic)
10*d5288c51SFlorent Revest (Anthropic) /*
11*d5288c51SFlorent Revest (Anthropic) * Detach and free progs while a task sleeps in the prog that runs before them
12*d5288c51SFlorent Revest (Anthropic) * in the same trampoline image, then let that task continue through the
13*d5288c51SFlorent Revest (Anthropic) * image. It must not call into the freed progs.
14*d5288c51SFlorent Revest (Anthropic) *
15*d5288c51SFlorent Revest (Anthropic) * The task is held in a sleepable prog with userfaultfd, like bpf_mod_race
16*d5288c51SFlorent Revest (Anthropic) * does.
17*d5288c51SFlorent Revest (Anthropic) */
18*d5288c51SFlorent Revest (Anthropic)
pick_prog(struct tramp_prog_detach * skel,bool fexit,bool sleepable)19*d5288c51SFlorent Revest (Anthropic) static struct bpf_program *pick_prog(struct tramp_prog_detach *skel,
20*d5288c51SFlorent Revest (Anthropic) bool fexit, bool sleepable)
21*d5288c51SFlorent Revest (Anthropic) {
22*d5288c51SFlorent Revest (Anthropic) if (fexit)
23*d5288c51SFlorent Revest (Anthropic) return sleepable ? skel->progs.fexit_sleepable :
24*d5288c51SFlorent Revest (Anthropic) skel->progs.fexit_victim;
25*d5288c51SFlorent Revest (Anthropic) return sleepable ? skel->progs.fentry_sleepable :
26*d5288c51SFlorent Revest (Anthropic) skel->progs.fentry_victim;
27*d5288c51SFlorent Revest (Anthropic) }
28*d5288c51SFlorent Revest (Anthropic)
29*d5288c51SFlorent Revest (Anthropic) static struct bpf_program *sleepable_prog;
30*d5288c51SFlorent Revest (Anthropic)
run_sleepable(void * arg)31*d5288c51SFlorent Revest (Anthropic) static void *run_sleepable(void *arg)
32*d5288c51SFlorent Revest (Anthropic) {
33*d5288c51SFlorent Revest (Anthropic) LIBBPF_OPTS(bpf_test_run_opts, topts);
34*d5288c51SFlorent Revest (Anthropic)
35*d5288c51SFlorent Revest (Anthropic) /* calls bpf_fentry_test1() */
36*d5288c51SFlorent Revest (Anthropic) return (void *)(long)bpf_prog_test_run_opts(bpf_program__fd(sleepable_prog),
37*d5288c51SFlorent Revest (Anthropic) &topts);
38*d5288c51SFlorent Revest (Anthropic) }
39*d5288c51SFlorent Revest (Anthropic)
load_one(bool fexit,bool sleepable)40*d5288c51SFlorent Revest (Anthropic) static struct tramp_prog_detach *load_one(bool fexit, bool sleepable)
41*d5288c51SFlorent Revest (Anthropic) {
42*d5288c51SFlorent Revest (Anthropic) struct tramp_prog_detach *skel;
43*d5288c51SFlorent Revest (Anthropic) int err;
44*d5288c51SFlorent Revest (Anthropic)
45*d5288c51SFlorent Revest (Anthropic) skel = tramp_prog_detach__open();
46*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK_PTR(skel, "open"))
47*d5288c51SFlorent Revest (Anthropic) return NULL;
48*d5288c51SFlorent Revest (Anthropic)
49*d5288c51SFlorent Revest (Anthropic) bpf_program__set_autoload(pick_prog(skel, fexit, sleepable), true);
50*d5288c51SFlorent Revest (Anthropic) err = tramp_prog_detach__load(skel);
51*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK(err, "load"))
52*d5288c51SFlorent Revest (Anthropic) goto err;
53*d5288c51SFlorent Revest (Anthropic) skel->bss->pid = getpid();
54*d5288c51SFlorent Revest (Anthropic) err = tramp_prog_detach__attach(skel);
55*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK(err, "attach"))
56*d5288c51SFlorent Revest (Anthropic) goto err;
57*d5288c51SFlorent Revest (Anthropic) return skel;
58*d5288c51SFlorent Revest (Anthropic) err:
59*d5288c51SFlorent Revest (Anthropic) tramp_prog_detach__destroy(skel);
60*d5288c51SFlorent Revest (Anthropic) return NULL;
61*d5288c51SFlorent Revest (Anthropic) }
62*d5288c51SFlorent Revest (Anthropic)
63*d5288c51SFlorent Revest (Anthropic) /* The .bss map of a destroyed skeleton goes away when its prog is freed */
wait_for_map_free(__u32 map_id)64*d5288c51SFlorent Revest (Anthropic) static bool wait_for_map_free(__u32 map_id)
65*d5288c51SFlorent Revest (Anthropic) {
66*d5288c51SFlorent Revest (Anthropic) int i, fd;
67*d5288c51SFlorent Revest (Anthropic)
68*d5288c51SFlorent Revest (Anthropic) for (i = 0; i < 100; i++) {
69*d5288c51SFlorent Revest (Anthropic) fd = bpf_map_get_fd_by_id(map_id);
70*d5288c51SFlorent Revest (Anthropic) if (fd < 0)
71*d5288c51SFlorent Revest (Anthropic) return true;
72*d5288c51SFlorent Revest (Anthropic) close(fd);
73*d5288c51SFlorent Revest (Anthropic) usleep(100 * 1000);
74*d5288c51SFlorent Revest (Anthropic) }
75*d5288c51SFlorent Revest (Anthropic) return false;
76*d5288c51SFlorent Revest (Anthropic) }
77*d5288c51SFlorent Revest (Anthropic)
test_detach(bool sleepable_fexit,bool victim_fexit)78*d5288c51SFlorent Revest (Anthropic) static void test_detach(bool sleepable_fexit, bool victim_fexit)
79*d5288c51SFlorent Revest (Anthropic) {
80*d5288c51SFlorent Revest (Anthropic) struct tramp_prog_detach *sleepable = NULL, *victims[2] = {};
81*d5288c51SFlorent Revest (Anthropic) struct pollfd pfd = { .events = POLLIN };
82*d5288c51SFlorent Revest (Anthropic) struct uffdio_copy uffd_copy = {};
83*d5288c51SFlorent Revest (Anthropic) struct bpf_map_info map_info = {};
84*d5288c51SFlorent Revest (Anthropic) __u32 map_info_len = sizeof(map_info);
85*d5288c51SFlorent Revest (Anthropic) struct uffd_msg uffd_msg;
86*d5288c51SFlorent Revest (Anthropic) void *fault_page, *src_page = MAP_FAILED;
87*d5288c51SFlorent Revest (Anthropic) long page_size = getpagesize();
88*d5288c51SFlorent Revest (Anthropic) bool started = false;
89*d5288c51SFlorent Revest (Anthropic) void *thread_ret;
90*d5288c51SFlorent Revest (Anthropic) pthread_t thread;
91*d5288c51SFlorent Revest (Anthropic) int i, uffd = -1;
92*d5288c51SFlorent Revest (Anthropic)
93*d5288c51SFlorent Revest (Anthropic) fault_page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
94*d5288c51SFlorent Revest (Anthropic) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
95*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_NEQ(fault_page, MAP_FAILED, "mmap fault_page"))
96*d5288c51SFlorent Revest (Anthropic) return;
97*d5288c51SFlorent Revest (Anthropic) src_page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
98*d5288c51SFlorent Revest (Anthropic) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
99*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_NEQ(src_page, MAP_FAILED, "mmap src_page"))
100*d5288c51SFlorent Revest (Anthropic) goto out;
101*d5288c51SFlorent Revest (Anthropic)
102*d5288c51SFlorent Revest (Anthropic) /* The most recently attached prog runs first */
103*d5288c51SFlorent Revest (Anthropic) for (i = 0; i < ARRAY_SIZE(victims); i++) {
104*d5288c51SFlorent Revest (Anthropic) victims[i] = load_one(victim_fexit, false);
105*d5288c51SFlorent Revest (Anthropic) if (!victims[i])
106*d5288c51SFlorent Revest (Anthropic) goto out;
107*d5288c51SFlorent Revest (Anthropic) }
108*d5288c51SFlorent Revest (Anthropic) sleepable = load_one(sleepable_fexit, true);
109*d5288c51SFlorent Revest (Anthropic) if (!sleepable)
110*d5288c51SFlorent Revest (Anthropic) goto out;
111*d5288c51SFlorent Revest (Anthropic) sleepable_prog = pick_prog(sleepable, sleepable_fexit, true);
112*d5288c51SFlorent Revest (Anthropic)
113*d5288c51SFlorent Revest (Anthropic) /* Not armed yet so this doesn't block, make sure sleepable runs first */
114*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK((long)run_sleepable(NULL), "dry run"))
115*d5288c51SFlorent Revest (Anthropic) goto out;
116*d5288c51SFlorent Revest (Anthropic) for (i = 0; i < ARRAY_SIZE(victims); i++)
117*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_LT(sleepable->bss->ts, victims[i]->bss->ts, "prog order"))
118*d5288c51SFlorent Revest (Anthropic) goto out;
119*d5288c51SFlorent Revest (Anthropic)
120*d5288c51SFlorent Revest (Anthropic) uffd = uffd_block_page(fault_page);
121*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
122*d5288c51SFlorent Revest (Anthropic) goto out;
123*d5288c51SFlorent Revest (Anthropic) sleepable->bss->fault_addr = fault_page;
124*d5288c51SFlorent Revest (Anthropic)
125*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK(pthread_create(&thread, NULL, run_sleepable, NULL),
126*d5288c51SFlorent Revest (Anthropic) "pthread_create"))
127*d5288c51SFlorent Revest (Anthropic) goto out;
128*d5288c51SFlorent Revest (Anthropic) started = true;
129*d5288c51SFlorent Revest (Anthropic)
130*d5288c51SFlorent Revest (Anthropic) /* Wait for the thread to sleep in bpf_copy_from_user() */
131*d5288c51SFlorent Revest (Anthropic) pfd.fd = uffd;
132*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_EQ(poll(&pfd, 1, 10000), 1, "poll uffd"))
133*d5288c51SFlorent Revest (Anthropic) goto out;
134*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg),
135*d5288c51SFlorent Revest (Anthropic) "read uffd"))
136*d5288c51SFlorent Revest (Anthropic) goto out;
137*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "uffd pagefault"))
138*d5288c51SFlorent Revest (Anthropic) goto out;
139*d5288c51SFlorent Revest (Anthropic)
140*d5288c51SFlorent Revest (Anthropic) /*
141*d5288c51SFlorent Revest (Anthropic) * Detach and unload the victim progs and wait for them to be freed.
142*d5288c51SFlorent Revest (Anthropic) * After the first one, the task is in an image that isn't the
143*d5288c51SFlorent Revest (Anthropic) * trampoline's current one anymore.
144*d5288c51SFlorent Revest (Anthropic) */
145*d5288c51SFlorent Revest (Anthropic) for (i = 0; i < ARRAY_SIZE(victims); i++) {
146*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_OK(bpf_map_get_info_by_fd(bpf_map__fd(victims[i]->maps.bss),
147*d5288c51SFlorent Revest (Anthropic) &map_info, &map_info_len),
148*d5288c51SFlorent Revest (Anthropic) "victim bss info"))
149*d5288c51SFlorent Revest (Anthropic) goto out;
150*d5288c51SFlorent Revest (Anthropic) tramp_prog_detach__destroy(victims[i]);
151*d5288c51SFlorent Revest (Anthropic) victims[i] = NULL;
152*d5288c51SFlorent Revest (Anthropic) if (!ASSERT_TRUE(wait_for_map_free(map_info.id), "victim freed"))
153*d5288c51SFlorent Revest (Anthropic) goto out;
154*d5288c51SFlorent Revest (Anthropic) }
155*d5288c51SFlorent Revest (Anthropic)
156*d5288c51SFlorent Revest (Anthropic) out:
157*d5288c51SFlorent Revest (Anthropic) /* Let the thread proceed with the rest of the trampoline */
158*d5288c51SFlorent Revest (Anthropic) if (uffd >= 0) {
159*d5288c51SFlorent Revest (Anthropic) uffd_copy.dst = (unsigned long)fault_page;
160*d5288c51SFlorent Revest (Anthropic) uffd_copy.src = (unsigned long)src_page;
161*d5288c51SFlorent Revest (Anthropic) uffd_copy.len = page_size;
162*d5288c51SFlorent Revest (Anthropic) ASSERT_OK(ioctl(uffd, UFFDIO_COPY, &uffd_copy), "uffd copy");
163*d5288c51SFlorent Revest (Anthropic) close(uffd);
164*d5288c51SFlorent Revest (Anthropic) }
165*d5288c51SFlorent Revest (Anthropic) if (started &&
166*d5288c51SFlorent Revest (Anthropic) ASSERT_OK(pthread_join(thread, &thread_ret), "pthread_join"))
167*d5288c51SFlorent Revest (Anthropic) ASSERT_NULL(thread_ret, "blocking run");
168*d5288c51SFlorent Revest (Anthropic)
169*d5288c51SFlorent Revest (Anthropic) for (i = 0; i < ARRAY_SIZE(victims); i++)
170*d5288c51SFlorent Revest (Anthropic) tramp_prog_detach__destroy(victims[i]);
171*d5288c51SFlorent Revest (Anthropic) tramp_prog_detach__destroy(sleepable);
172*d5288c51SFlorent Revest (Anthropic) if (src_page != MAP_FAILED)
173*d5288c51SFlorent Revest (Anthropic) munmap(src_page, page_size);
174*d5288c51SFlorent Revest (Anthropic) munmap(fault_page, page_size);
175*d5288c51SFlorent Revest (Anthropic) }
176*d5288c51SFlorent Revest (Anthropic)
serial_test_tramp_prog_detach(void)177*d5288c51SFlorent Revest (Anthropic) void serial_test_tramp_prog_detach(void)
178*d5288c51SFlorent Revest (Anthropic) {
179*d5288c51SFlorent Revest (Anthropic) /* a task sleeping before the original function is called */
180*d5288c51SFlorent Revest (Anthropic) if (test__start_subtest("fentry"))
181*d5288c51SFlorent Revest (Anthropic) test_detach(false, false);
182*d5288c51SFlorent Revest (Anthropic) /* a task sleeping after the original function returned */
183*d5288c51SFlorent Revest (Anthropic) if (test__start_subtest("fexit"))
184*d5288c51SFlorent Revest (Anthropic) test_detach(true, true);
185*d5288c51SFlorent Revest (Anthropic) /* the original function runs in between and must still be called */
186*d5288c51SFlorent Revest (Anthropic) if (test__start_subtest("fentry_fexit"))
187*d5288c51SFlorent Revest (Anthropic) test_detach(false, true);
188*d5288c51SFlorent Revest (Anthropic) }
189