xref: /linux/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c (revision d5a821f66047c2effd06147b84775f9b2ce0f4aa)
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