101 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <vmlinux.h>3#include <bpf/bpf_helpers.h>4#include <bpf/bpf_tracing.h>5 6const volatile struct {7 /* thread to activate trace programs for */8 pid_t tgid;9 /* return error from __init function */10 int inject_error;11 /* uffd monitored range start address */12 void *fault_addr;13} bpf_mod_race_config = { -1 };14 15int bpf_blocking = 0;16int res_try_get_module = -1;17 18static __always_inline bool check_thread_id(void)19{20 struct task_struct *task = bpf_get_current_task_btf();21 22 return task->tgid == bpf_mod_race_config.tgid;23}24 25/* The trace of execution is something like this:26 *27 * finit_module()28 * load_module()29 * prepare_coming_module()30 * notifier_call(MODULE_STATE_COMING)31 * btf_parse_module()32 * btf_alloc_id() // Visible to userspace at this point33 * list_add(btf_mod->list, &btf_modules)34 * do_init_module()35 * freeinit = kmalloc()36 * ret = mod->init()37 * bpf_prog_widen_race()38 * bpf_copy_from_user()39 * ...<sleep>...40 * if (ret < 0)41 * ...42 * free_module()43 * return ret44 *45 * At this point, module loading thread is blocked, we now load the program:46 *47 * bpf_check48 * add_kfunc_call/check_pseudo_btf_id49 * btf_try_get_module50 * try_get_module_live == false51 * return -ENXIO52 *53 * Without the fix (try_get_module_live in btf_try_get_module):54 *55 * bpf_check56 * add_kfunc_call/check_pseudo_btf_id57 * btf_try_get_module58 * try_get_module == true59 * <store module reference in btf_kfunc_tab or used_btf array>60 * ...61 * return fd62 *63 * Now, if we inject an error in the blocked program, our module will be freed64 * (going straight from MODULE_STATE_COMING to MODULE_STATE_GOING).65 * Later, when bpf program is freed, it will try to module_put already freed66 * module. This is why try_get_module_live returns false if mod->state is not67 * MODULE_STATE_LIVE.68 */69 70SEC("fmod_ret.s/bpf_fentry_test1")71int BPF_PROG(widen_race, int a, int ret)72{73 char dst;74 75 if (!check_thread_id())76 return 0;77 /* Indicate that we will attempt to block */78 bpf_blocking = 1;79 bpf_copy_from_user(&dst, 1, bpf_mod_race_config.fault_addr);80 return bpf_mod_race_config.inject_error;81}82 83SEC("fexit/do_init_module")84int BPF_PROG(fexit_init_module, struct module *mod, int ret)85{86 if (!check_thread_id())87 return 0;88 /* Indicate that we finished blocking */89 bpf_blocking = 2;90 return 0;91}92 93SEC("fexit/btf_try_get_module")94int BPF_PROG(fexit_module_get, const struct btf *btf, struct module *mod)95{96 res_try_get_module = !!mod;97 return 0;98}99 100char _license[] SEC("license") = "GPL";101