0001
0002 #include <unistd.h>
0003 #include <pthread.h>
0004 #include <sys/mman.h>
0005 #include <stdatomic.h>
0006 #include <test_progs.h>
0007 #include <sys/syscall.h>
0008 #include <linux/module.h>
0009 #include <linux/userfaultfd.h>
0010
0011 #include "ksym_race.skel.h"
0012 #include "bpf_mod_race.skel.h"
0013 #include "kfunc_call_race.skel.h"
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 struct test_config {
0034 const char *str_open;
0035 void *(*bpf_open_and_load)();
0036 void (*bpf_destroy)(void *);
0037 };
0038
0039 enum bpf_test_state {
0040 _TS_INVALID,
0041 TS_MODULE_LOAD,
0042 TS_MODULE_LOAD_FAIL,
0043 };
0044
0045 static _Atomic enum bpf_test_state state = _TS_INVALID;
0046
0047 static int sys_finit_module(int fd, const char *param_values, int flags)
0048 {
0049 return syscall(__NR_finit_module, fd, param_values, flags);
0050 }
0051
0052 static int sys_delete_module(const char *name, unsigned int flags)
0053 {
0054 return syscall(__NR_delete_module, name, flags);
0055 }
0056
0057 static int load_module(const char *mod)
0058 {
0059 int ret, fd;
0060
0061 fd = open("bpf_testmod.ko", O_RDONLY);
0062 if (fd < 0)
0063 return fd;
0064
0065 ret = sys_finit_module(fd, "", 0);
0066 close(fd);
0067 if (ret < 0)
0068 return ret;
0069 return 0;
0070 }
0071
0072 static void *load_module_thread(void *p)
0073 {
0074
0075 if (!ASSERT_NEQ(load_module("bpf_testmod.ko"), 0, "load_module_thread must fail"))
0076 atomic_store(&state, TS_MODULE_LOAD);
0077 else
0078 atomic_store(&state, TS_MODULE_LOAD_FAIL);
0079 return p;
0080 }
0081
0082 static int sys_userfaultfd(int flags)
0083 {
0084 return syscall(__NR_userfaultfd, flags);
0085 }
0086
0087 static int test_setup_uffd(void *fault_addr)
0088 {
0089 struct uffdio_register uffd_register = {};
0090 struct uffdio_api uffd_api = {};
0091 int uffd;
0092
0093 uffd = sys_userfaultfd(O_CLOEXEC);
0094 if (uffd < 0)
0095 return -errno;
0096
0097 uffd_api.api = UFFD_API;
0098 uffd_api.features = 0;
0099 if (ioctl(uffd, UFFDIO_API, &uffd_api)) {
0100 close(uffd);
0101 return -1;
0102 }
0103
0104 uffd_register.range.start = (unsigned long)fault_addr;
0105 uffd_register.range.len = 4096;
0106 uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
0107 if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
0108 close(uffd);
0109 return -1;
0110 }
0111 return uffd;
0112 }
0113
0114 static void test_bpf_mod_race_config(const struct test_config *config)
0115 {
0116 void *fault_addr, *skel_fail;
0117 struct bpf_mod_race *skel;
0118 struct uffd_msg uffd_msg;
0119 pthread_t load_mod_thrd;
0120 _Atomic int *blockingp;
0121 int uffd, ret;
0122
0123 fault_addr = mmap(0, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0124 if (!ASSERT_NEQ(fault_addr, MAP_FAILED, "mmap for uffd registration"))
0125 return;
0126
0127 if (!ASSERT_OK(sys_delete_module("bpf_testmod", 0), "unload bpf_testmod"))
0128 goto end_mmap;
0129
0130 skel = bpf_mod_race__open();
0131 if (!ASSERT_OK_PTR(skel, "bpf_mod_kfunc_race__open"))
0132 goto end_module;
0133
0134 skel->rodata->bpf_mod_race_config.tgid = getpid();
0135 skel->rodata->bpf_mod_race_config.inject_error = -4242;
0136 skel->rodata->bpf_mod_race_config.fault_addr = fault_addr;
0137 if (!ASSERT_OK(bpf_mod_race__load(skel), "bpf_mod___load"))
0138 goto end_destroy;
0139 blockingp = (_Atomic int *)&skel->bss->bpf_blocking;
0140
0141 if (!ASSERT_OK(bpf_mod_race__attach(skel), "bpf_mod_kfunc_race__attach"))
0142 goto end_destroy;
0143
0144 uffd = test_setup_uffd(fault_addr);
0145 if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
0146 goto end_destroy;
0147
0148 if (!ASSERT_OK(pthread_create(&load_mod_thrd, NULL, load_module_thread, NULL),
0149 "load module thread"))
0150 goto end_uffd;
0151
0152
0153 while (!atomic_load(&state) && !atomic_load(blockingp))
0154 ;
0155 if (!ASSERT_EQ(state, _TS_INVALID, "module load should block"))
0156 goto end_join;
0157 if (!ASSERT_EQ(*blockingp, 1, "module load blocked")) {
0158 pthread_kill(load_mod_thrd, SIGKILL);
0159 goto end_uffd;
0160 }
0161
0162
0163
0164
0165 if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg),
0166 "read uffd block event"))
0167 goto end_join;
0168 if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "read uffd event is pagefault"))
0169 goto end_join;
0170
0171
0172
0173
0174
0175
0176 skel_fail = config->bpf_open_and_load();
0177 ret = errno;
0178 if (!ASSERT_EQ(skel_fail, NULL, config->str_open)) {
0179
0180 close(uffd);
0181 uffd = -1;
0182 while (atomic_load(blockingp) != 2)
0183 ;
0184 ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu");
0185 config->bpf_destroy(skel_fail);
0186 goto end_join;
0187
0188 }
0189 ASSERT_EQ(ret, ENXIO, "verifier returns ENXIO");
0190 ASSERT_EQ(skel->data->res_try_get_module, false, "btf_try_get_module == false");
0191
0192 close(uffd);
0193 uffd = -1;
0194 end_join:
0195 pthread_join(load_mod_thrd, NULL);
0196 if (uffd < 0)
0197 ASSERT_EQ(atomic_load(&state), TS_MODULE_LOAD_FAIL, "load_mod_thrd success");
0198 end_uffd:
0199 if (uffd >= 0)
0200 close(uffd);
0201 end_destroy:
0202 bpf_mod_race__destroy(skel);
0203 ASSERT_OK(kern_sync_rcu(), "kern_sync_rcu");
0204 end_module:
0205 sys_delete_module("bpf_testmod", 0);
0206 ASSERT_OK(load_module("bpf_testmod.ko"), "restore bpf_testmod");
0207 end_mmap:
0208 munmap(fault_addr, 4096);
0209 atomic_store(&state, _TS_INVALID);
0210 }
0211
0212 static const struct test_config ksym_config = {
0213 .str_open = "ksym_race__open_and_load",
0214 .bpf_open_and_load = (void *)ksym_race__open_and_load,
0215 .bpf_destroy = (void *)ksym_race__destroy,
0216 };
0217
0218 static const struct test_config kfunc_config = {
0219 .str_open = "kfunc_call_race__open_and_load",
0220 .bpf_open_and_load = (void *)kfunc_call_race__open_and_load,
0221 .bpf_destroy = (void *)kfunc_call_race__destroy,
0222 };
0223
0224 void serial_test_bpf_mod_race(void)
0225 {
0226 if (test__start_subtest("ksym (used_btfs UAF)"))
0227 test_bpf_mod_race_config(&ksym_config);
0228 if (test__start_subtest("kfunc (kfunc_btf_tab UAF)"))
0229 test_bpf_mod_race_config(&kfunc_config);
0230 }