Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <vmlinux.h>
0003 #include <bpf/bpf_helpers.h>
0004 #include <bpf/bpf_tracing.h>
0005 
0006 const volatile struct {
0007     /* thread to activate trace programs for */
0008     pid_t tgid;
0009     /* return error from __init function */
0010     int inject_error;
0011     /* uffd monitored range start address */
0012     void *fault_addr;
0013 } bpf_mod_race_config = { -1 };
0014 
0015 int bpf_blocking = 0;
0016 int res_try_get_module = -1;
0017 
0018 static __always_inline bool check_thread_id(void)
0019 {
0020     struct task_struct *task = bpf_get_current_task_btf();
0021 
0022     return task->tgid == bpf_mod_race_config.tgid;
0023 }
0024 
0025 /* The trace of execution is something like this:
0026  *
0027  * finit_module()
0028  *   load_module()
0029  *     prepare_coming_module()
0030  *       notifier_call(MODULE_STATE_COMING)
0031  *         btf_parse_module()
0032  *         btf_alloc_id()       // Visible to userspace at this point
0033  *         list_add(btf_mod->list, &btf_modules)
0034  *     do_init_module()
0035  *       freeinit = kmalloc()
0036  *       ret = mod->init()
0037  *         bpf_prog_widen_race()
0038  *           bpf_copy_from_user()
0039  *             ...<sleep>...
0040  *       if (ret < 0)
0041  *         ...
0042  *         free_module()
0043  * return ret
0044  *
0045  * At this point, module loading thread is blocked, we now load the program:
0046  *
0047  * bpf_check
0048  *   add_kfunc_call/check_pseudo_btf_id
0049  *     btf_try_get_module
0050  *       try_get_module_live == false
0051  *     return -ENXIO
0052  *
0053  * Without the fix (try_get_module_live in btf_try_get_module):
0054  *
0055  * bpf_check
0056  *   add_kfunc_call/check_pseudo_btf_id
0057  *     btf_try_get_module
0058  *       try_get_module == true
0059  *     <store module reference in btf_kfunc_tab or used_btf array>
0060  *   ...
0061  * return fd
0062  *
0063  * Now, if we inject an error in the blocked program, our module will be freed
0064  * (going straight from MODULE_STATE_COMING to MODULE_STATE_GOING).
0065  * Later, when bpf program is freed, it will try to module_put already freed
0066  * module. This is why try_get_module_live returns false if mod->state is not
0067  * MODULE_STATE_LIVE.
0068  */
0069 
0070 SEC("fmod_ret.s/bpf_fentry_test1")
0071 int BPF_PROG(widen_race, int a, int ret)
0072 {
0073     char dst;
0074 
0075     if (!check_thread_id())
0076         return 0;
0077     /* Indicate that we will attempt to block */
0078     bpf_blocking = 1;
0079     bpf_copy_from_user(&dst, 1, bpf_mod_race_config.fault_addr);
0080     return bpf_mod_race_config.inject_error;
0081 }
0082 
0083 SEC("fexit/do_init_module")
0084 int BPF_PROG(fexit_init_module, struct module *mod, int ret)
0085 {
0086     if (!check_thread_id())
0087         return 0;
0088     /* Indicate that we finished blocking */
0089     bpf_blocking = 2;
0090     return 0;
0091 }
0092 
0093 SEC("fexit/btf_try_get_module")
0094 int BPF_PROG(fexit_module_get, const struct btf *btf, struct module *mod)
0095 {
0096     res_try_get_module = !!mod;
0097     return 0;
0098 }
0099 
0100 char _license[] SEC("license") = "GPL";