1 // SPDX-License-Identifier: GPL-2.0
2 #include <vmlinux.h>
3 #include <bpf/bpf_helpers.h>
4 #include <bpf/bpf_tracing.h>
5 
6 const 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 
15 int bpf_blocking = 0;
16 int res_try_get_module = -1;
17 
check_thread_id(void)18 static __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 point
33  *         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 ret
44  *
45  * At this point, module loading thread is blocked, we now load the program:
46  *
47  * bpf_check
48  *   add_kfunc_call/check_pseudo_btf_id
49  *     btf_try_get_module
50  *       try_get_module_live == false
51  *     return -ENXIO
52  *
53  * Without the fix (try_get_module_live in btf_try_get_module):
54  *
55  * bpf_check
56  *   add_kfunc_call/check_pseudo_btf_id
57  *     btf_try_get_module
58  *       try_get_module == true
59  *     <store module reference in btf_kfunc_tab or used_btf array>
60  *   ...
61  * return fd
62  *
63  * Now, if we inject an error in the blocked program, our module will be freed
64  * (going straight from MODULE_STATE_COMING to MODULE_STATE_GOING).
65  * Later, when bpf program is freed, it will try to module_put already freed
66  * module. This is why try_get_module_live returns false if mod->state is not
67  * MODULE_STATE_LIVE.
68  */
69 
70 SEC("fmod_ret.s/bpf_fentry_test1")
BPF_PROG(widen_race,int a,int ret)71 int 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 
83 SEC("fexit/do_init_module")
BPF_PROG(fexit_init_module,struct module * mod,int ret)84 int 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 
93 SEC("fexit/btf_try_get_module")
BPF_PROG(fexit_module_get,const struct btf * btf,struct module * mod)94 int BPF_PROG(fexit_module_get, const struct btf *btf, struct module *mod)
95 {
96 	res_try_get_module = !!mod;
97 	return 0;
98 }
99 
100 char _license[] SEC("license") = "GPL";
101