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