146565696SKumar Kartikeya Dwivedi // SPDX-License-Identifier: GPL-2.0
246565696SKumar Kartikeya Dwivedi #include <unistd.h>
346565696SKumar Kartikeya Dwivedi #include <pthread.h>
446565696SKumar Kartikeya Dwivedi #include <sys/mman.h>
546565696SKumar Kartikeya Dwivedi #include <stdatomic.h>
646565696SKumar Kartikeya Dwivedi #include <test_progs.h>
746565696SKumar Kartikeya Dwivedi #include <sys/syscall.h>
846565696SKumar Kartikeya Dwivedi #include <linux/module.h>
946565696SKumar Kartikeya Dwivedi #include <linux/userfaultfd.h>
1046565696SKumar Kartikeya Dwivedi 
1146565696SKumar Kartikeya Dwivedi #include "ksym_race.skel.h"
1246565696SKumar Kartikeya Dwivedi #include "bpf_mod_race.skel.h"
1346565696SKumar Kartikeya Dwivedi #include "kfunc_call_race.skel.h"
14*11642eb9SJiri Olsa #include "testing_helpers.h"
1546565696SKumar Kartikeya Dwivedi 
1646565696SKumar Kartikeya Dwivedi /* This test crafts a race between btf_try_get_module and do_init_module, and
1746565696SKumar Kartikeya Dwivedi  * checks whether btf_try_get_module handles the invocation for a well-formed
1846565696SKumar Kartikeya Dwivedi  * but uninitialized module correctly. Unless the module has completed its
1946565696SKumar Kartikeya Dwivedi  * initcalls, the verifier should fail the program load and return ENXIO.
2046565696SKumar Kartikeya Dwivedi  *
2146565696SKumar Kartikeya Dwivedi  * userfaultfd is used to trigger a fault in an fmod_ret program, and make it
2246565696SKumar Kartikeya Dwivedi  * sleep, then the BPF program is loaded and the return value from verifier is
2346565696SKumar Kartikeya Dwivedi  * inspected. After this, the userfaultfd is closed so that the module loading
2446565696SKumar Kartikeya Dwivedi  * thread makes forward progress, and fmod_ret injects an error so that the
2546565696SKumar Kartikeya Dwivedi  * module load fails and it is freed.
2646565696SKumar Kartikeya Dwivedi  *
2746565696SKumar Kartikeya Dwivedi  * If the verifier succeeded in loading the supplied program, it will end up
2846565696SKumar Kartikeya Dwivedi  * taking reference to freed module, and trigger a crash when the program fd
2946565696SKumar Kartikeya Dwivedi  * is closed later. This is true for both kfuncs and ksyms. In both cases,
3046565696SKumar Kartikeya Dwivedi  * the crash is triggered inside bpf_prog_free_deferred, when module reference
3146565696SKumar Kartikeya Dwivedi  * is finally released.
3246565696SKumar Kartikeya Dwivedi  */
3346565696SKumar Kartikeya Dwivedi 
3446565696SKumar Kartikeya Dwivedi struct test_config {
3546565696SKumar Kartikeya Dwivedi 	const char *str_open;
3646565696SKumar Kartikeya Dwivedi 	void *(*bpf_open_and_load)();
3746565696SKumar Kartikeya Dwivedi 	void (*bpf_destroy)(void *);
3846565696SKumar Kartikeya Dwivedi };
3946565696SKumar Kartikeya Dwivedi 
402324257dSMykola Lysenko enum bpf_test_state {
4146565696SKumar Kartikeya Dwivedi 	_TS_INVALID,
4246565696SKumar Kartikeya Dwivedi 	TS_MODULE_LOAD,
4346565696SKumar Kartikeya Dwivedi 	TS_MODULE_LOAD_FAIL,
4446565696SKumar Kartikeya Dwivedi };
4546565696SKumar Kartikeya Dwivedi 
462324257dSMykola Lysenko static _Atomic enum bpf_test_state state = _TS_INVALID;
4746565696SKumar Kartikeya Dwivedi 
load_module_thread(void * p)4846565696SKumar Kartikeya Dwivedi static void *load_module_thread(void *p)
4946565696SKumar Kartikeya Dwivedi {
5046565696SKumar Kartikeya Dwivedi 
51*11642eb9SJiri Olsa 	if (!ASSERT_NEQ(load_bpf_testmod(false), 0, "load_module_thread must fail"))
5246565696SKumar Kartikeya Dwivedi 		atomic_store(&state, TS_MODULE_LOAD);
5346565696SKumar Kartikeya Dwivedi 	else
5446565696SKumar Kartikeya Dwivedi 		atomic_store(&state, TS_MODULE_LOAD_FAIL);
5546565696SKumar Kartikeya Dwivedi 	return p;
5646565696SKumar Kartikeya Dwivedi }
5746565696SKumar Kartikeya Dwivedi 
sys_userfaultfd(int flags)5846565696SKumar Kartikeya Dwivedi static int sys_userfaultfd(int flags)
5946565696SKumar Kartikeya Dwivedi {
6046565696SKumar Kartikeya Dwivedi 	return syscall(__NR_userfaultfd, flags);
6146565696SKumar Kartikeya Dwivedi }
6246565696SKumar Kartikeya Dwivedi 
test_setup_uffd(void * fault_addr)6346565696SKumar Kartikeya Dwivedi static int test_setup_uffd(void *fault_addr)
6446565696SKumar Kartikeya Dwivedi {
6546565696SKumar Kartikeya Dwivedi 	struct uffdio_register uffd_register = {};
6646565696SKumar Kartikeya Dwivedi 	struct uffdio_api uffd_api = {};
6746565696SKumar Kartikeya Dwivedi 	int uffd;
6846565696SKumar Kartikeya Dwivedi 
6946565696SKumar Kartikeya Dwivedi 	uffd = sys_userfaultfd(O_CLOEXEC);
7046565696SKumar Kartikeya Dwivedi 	if (uffd < 0)
7146565696SKumar Kartikeya Dwivedi 		return -errno;
7246565696SKumar Kartikeya Dwivedi 
7346565696SKumar Kartikeya Dwivedi 	uffd_api.api = UFFD_API;
7446565696SKumar Kartikeya Dwivedi 	uffd_api.features = 0;
7546565696SKumar Kartikeya Dwivedi 	if (ioctl(uffd, UFFDIO_API, &uffd_api)) {
7646565696SKumar Kartikeya Dwivedi 		close(uffd);
7746565696SKumar Kartikeya Dwivedi 		return -1;
7846565696SKumar Kartikeya Dwivedi 	}
7946565696SKumar Kartikeya Dwivedi 
8046565696SKumar Kartikeya Dwivedi 	uffd_register.range.start = (unsigned long)fault_addr;
8146565696SKumar Kartikeya Dwivedi 	uffd_register.range.len = 4096;
8246565696SKumar Kartikeya Dwivedi 	uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
8346565696SKumar Kartikeya Dwivedi 	if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
8446565696SKumar Kartikeya Dwivedi 		close(uffd);
8546565696SKumar Kartikeya Dwivedi 		return -1;
8646565696SKumar Kartikeya Dwivedi 	}
8746565696SKumar Kartikeya Dwivedi 	return uffd;
8846565696SKumar Kartikeya Dwivedi }
8946565696SKumar Kartikeya Dwivedi 
test_bpf_mod_race_config(const struct test_config * config)9046565696SKumar Kartikeya Dwivedi static void test_bpf_mod_race_config(const struct test_config *config)
9146565696SKumar Kartikeya Dwivedi {
9246565696SKumar Kartikeya Dwivedi 	void *fault_addr, *skel_fail;
9346565696SKumar Kartikeya Dwivedi 	struct bpf_mod_race *skel;
9446565696SKumar Kartikeya Dwivedi 	struct uffd_msg uffd_msg;
9546565696SKumar Kartikeya Dwivedi 	pthread_t load_mod_thrd;
9646565696SKumar Kartikeya Dwivedi 	_Atomic int *blockingp;
9746565696SKumar Kartikeya Dwivedi 	int uffd, ret;
9846565696SKumar Kartikeya Dwivedi 
9946565696SKumar Kartikeya Dwivedi 	fault_addr = mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
10046565696SKumar Kartikeya Dwivedi 	if (!ASSERT_NEQ(fault_addr, MAP_FAILED, "mmap for uffd registration"))
10146565696SKumar Kartikeya Dwivedi 		return;
10246565696SKumar Kartikeya Dwivedi 
103*11642eb9SJiri Olsa 	if (!ASSERT_OK(unload_bpf_testmod(false), "unload bpf_testmod"))
10446565696SKumar Kartikeya Dwivedi 		goto end_mmap;
10546565696SKumar Kartikeya Dwivedi 
10646565696SKumar Kartikeya Dwivedi 	skel = bpf_mod_race__open();
10746565696SKumar Kartikeya Dwivedi 	if (!ASSERT_OK_PTR(skel, "bpf_mod_kfunc_race__open"))
10846565696SKumar Kartikeya Dwivedi 		goto end_module;
10946565696SKumar Kartikeya Dwivedi 
11046565696SKumar Kartikeya Dwivedi 	skel->rodata->bpf_mod_race_config.tgid = getpid();
11146565696SKumar Kartikeya Dwivedi 	skel->rodata->bpf_mod_race_config.inject_error = -4242;
11246565696SKumar Kartikeya Dwivedi 	skel->rodata->bpf_mod_race_config.fault_addr = fault_addr;
11346565696SKumar Kartikeya Dwivedi 	if (!ASSERT_OK(bpf_mod_race__load(skel), "bpf_mod___load"))
11446565696SKumar Kartikeya Dwivedi 		goto end_destroy;
11546565696SKumar Kartikeya Dwivedi 	blockingp = (_Atomic int *)&skel->bss->bpf_blocking;
11646565696SKumar Kartikeya Dwivedi 
11746565696SKumar Kartikeya Dwivedi 	if (!ASSERT_OK(bpf_mod_race__attach(skel), "bpf_mod_kfunc_race__attach"))
11846565696SKumar Kartikeya Dwivedi 		goto end_destroy;
11946565696SKumar Kartikeya Dwivedi 
12046565696SKumar Kartikeya Dwivedi 	uffd = test_setup_uffd(fault_addr);
12146565696SKumar Kartikeya Dwivedi 	if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
12246565696SKumar Kartikeya Dwivedi 		goto end_destroy;
12346565696SKumar Kartikeya Dwivedi 
12446565696SKumar Kartikeya Dwivedi 	if (!ASSERT_OK(pthread_create(&load_mod_thrd, NULL, load_module_thread, NULL),
12546565696SKumar Kartikeya Dwivedi 		       "load module thread"))
12646565696SKumar Kartikeya Dwivedi 		goto end_uffd;
12746565696SKumar Kartikeya Dwivedi 
12846565696SKumar Kartikeya Dwivedi 	/* Now, we either fail loading module, or block in bpf prog, spin to find out */
12946565696SKumar Kartikeya Dwivedi 	while (!atomic_load(&state) && !atomic_load(blockingp))
13046565696SKumar Kartikeya Dwivedi 		;
13146565696SKumar Kartikeya Dwivedi 	if (!ASSERT_EQ(state, _TS_INVALID, "module load should block"))
13246565696SKumar Kartikeya Dwivedi 		goto end_join;
13346565696SKumar Kartikeya Dwivedi 	if (!ASSERT_EQ(*blockingp, 1, "module load blocked")) {
13446565696SKumar Kartikeya Dwivedi 		pthread_kill(load_mod_thrd, SIGKILL);
13546565696SKumar Kartikeya Dwivedi 		goto end_uffd;
13646565696SKumar Kartikeya Dwivedi 	}
13746565696SKumar Kartikeya Dwivedi 
13846565696SKumar Kartikeya Dwivedi 	/* We might have set bpf_blocking to 1, but may have not blocked in
13946565696SKumar Kartikeya Dwivedi 	 * bpf_copy_from_user. Read userfaultfd descriptor to verify that.
14046565696SKumar Kartikeya Dwivedi 	 */
14146565696SKumar Kartikeya Dwivedi 	if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg),
14246565696SKumar Kartikeya Dwivedi 		       "read uffd block event"))
14346565696SKumar Kartikeya Dwivedi 		goto end_join;
14446565696SKumar Kartikeya Dwivedi 	if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "read uffd event is pagefault"))
14546565696SKumar Kartikeya Dwivedi 		goto end_join;
14646565696SKumar Kartikeya Dwivedi 
14746565696SKumar Kartikeya Dwivedi 	/* We know that load_mod_thrd is blocked in the fmod_ret program, the
14846565696SKumar Kartikeya Dwivedi 	 * module state is still MODULE_STATE_COMING because mod->init hasn't
14946565696SKumar Kartikeya Dwivedi 	 * returned. This is the time we try to load a program calling kfunc and
15046565696SKumar Kartikeya Dwivedi 	 * check if we get ENXIO from verifier.
15146565696SKumar Kartikeya Dwivedi 	 */
15246565696SKumar Kartikeya Dwivedi 	skel_fail = config->bpf_open_and_load();
15346565696SKumar Kartikeya Dwivedi 	ret = errno;
15446565696SKumar Kartikeya Dwivedi 	if (!ASSERT_EQ(skel_fail, NULL, config->str_open)) {
15546565696SKumar Kartikeya Dwivedi 		/* Close uffd to unblock load_mod_thrd */
15646565696SKumar Kartikeya Dwivedi 		close(uffd);
15746565696SKumar Kartikeya Dwivedi 		uffd = -1;
15846565696SKumar Kartikeya Dwivedi 		while (atomic_load(blockingp) != 2)
15946565696SKumar Kartikeya Dwivedi 			;
16046565696SKumar Kartikeya Dwivedi 		ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu");
16146565696SKumar Kartikeya Dwivedi 		config->bpf_destroy(skel_fail);
16246565696SKumar Kartikeya Dwivedi 		goto end_join;
16346565696SKumar Kartikeya Dwivedi 
16446565696SKumar Kartikeya Dwivedi 	}
16546565696SKumar Kartikeya Dwivedi 	ASSERT_EQ(ret, ENXIO, "verifier returns ENXIO");
16646565696SKumar Kartikeya Dwivedi 	ASSERT_EQ(skel->data->res_try_get_module, false, "btf_try_get_module == false");
16746565696SKumar Kartikeya Dwivedi 
16846565696SKumar Kartikeya Dwivedi 	close(uffd);
16946565696SKumar Kartikeya Dwivedi 	uffd = -1;
17046565696SKumar Kartikeya Dwivedi end_join:
17146565696SKumar Kartikeya Dwivedi 	pthread_join(load_mod_thrd, NULL);
17246565696SKumar Kartikeya Dwivedi 	if (uffd < 0)
17346565696SKumar Kartikeya Dwivedi 		ASSERT_EQ(atomic_load(&state), TS_MODULE_LOAD_FAIL, "load_mod_thrd success");
17446565696SKumar Kartikeya Dwivedi end_uffd:
17546565696SKumar Kartikeya Dwivedi 	if (uffd >= 0)
17646565696SKumar Kartikeya Dwivedi 		close(uffd);
17746565696SKumar Kartikeya Dwivedi end_destroy:
17846565696SKumar Kartikeya Dwivedi 	bpf_mod_race__destroy(skel);
17946565696SKumar Kartikeya Dwivedi 	ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu");
18046565696SKumar Kartikeya Dwivedi end_module:
181*11642eb9SJiri Olsa 	unload_bpf_testmod(false);
182*11642eb9SJiri Olsa 	ASSERT_OK(load_bpf_testmod(false), "restore bpf_testmod");
18346565696SKumar Kartikeya Dwivedi end_mmap:
18446565696SKumar Kartikeya Dwivedi 	munmap(fault_addr, 4096);
18546565696SKumar Kartikeya Dwivedi 	atomic_store(&state, _TS_INVALID);
18646565696SKumar Kartikeya Dwivedi }
18746565696SKumar Kartikeya Dwivedi 
18846565696SKumar Kartikeya Dwivedi static const struct test_config ksym_config = {
18946565696SKumar Kartikeya Dwivedi 	.str_open = "ksym_race__open_and_load",
19046565696SKumar Kartikeya Dwivedi 	.bpf_open_and_load = (void *)ksym_race__open_and_load,
19146565696SKumar Kartikeya Dwivedi 	.bpf_destroy = (void *)ksym_race__destroy,
19246565696SKumar Kartikeya Dwivedi };
19346565696SKumar Kartikeya Dwivedi 
19446565696SKumar Kartikeya Dwivedi static const struct test_config kfunc_config = {
19546565696SKumar Kartikeya Dwivedi 	.str_open = "kfunc_call_race__open_and_load",
19646565696SKumar Kartikeya Dwivedi 	.bpf_open_and_load = (void *)kfunc_call_race__open_and_load,
19746565696SKumar Kartikeya Dwivedi 	.bpf_destroy = (void *)kfunc_call_race__destroy,
19846565696SKumar Kartikeya Dwivedi };
19946565696SKumar Kartikeya Dwivedi 
serial_test_bpf_mod_race(void)20046565696SKumar Kartikeya Dwivedi void serial_test_bpf_mod_race(void)
20146565696SKumar Kartikeya Dwivedi {
20246565696SKumar Kartikeya Dwivedi 	if (test__start_subtest("ksym (used_btfs UAF)"))
20346565696SKumar Kartikeya Dwivedi 		test_bpf_mod_race_config(&ksym_config);
20446565696SKumar Kartikeya Dwivedi 	if (test__start_subtest("kfunc (kfunc_btf_tab UAF)"))
20546565696SKumar Kartikeya Dwivedi 		test_bpf_mod_race_config(&kfunc_config);
20646565696SKumar Kartikeya Dwivedi }
207