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