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