Message ID | 3f6a9d8ae850532b5ef864ef16327b0f7a669063.1678432753.git.vmalik@redhat.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 2a6427ca8f3a8f08498c7b60e486c95af88d0afd |
Delegated to: | BPF |
Headers | show |
Series | Fix attaching fentry/fexit/fmod_ret/lsm to modules | expand |
Please add linux-modules in the future. My review below. On Fri, Mar 10, 2023 at 08:40:59AM +0100, Viktor Malik wrote: > This resolves two problems with attachment of fentry/fexit/fmod_ret/lsm > to functions located in modules: > > 1. The verifier tries to find the address to attach to in kallsyms. This > is always done by searching the entire kallsyms, not respecting the > module in which the function is located. Such approach causes an > incorrect attachment address to be computed if the function to attach > to is shadowed by a function of the same name located earlier in > kallsyms. Just a heads up, I realize you have tried to fix the issue here using semantics to get the module in other ways, but Nick's future work expands kallsyms to allow symbols for modules to be looked for, even if there are duplicates in kallsysms. He is working on that to help with tooling for tracing, but here seems to be an example use case which may be in-kernel. But you seem to indicate you've found an alternative solution anyway. Jiri, does live patching hunt for symbols in such opaque way too? Or does it resolve it using something similar as the technique here with a module notifier / its own linked list? > 2. If the address to attach to is located in a module, the module > reference is only acquired in register_fentry. If the module is > unloaded between the place where the address is found > (bpf_check_attach_target in the verifier) and register_fentry, it is > possible that another module is loaded to the same address which may > lead to potential errors. > > Since the attachment must contain the BTF of the program to attach to, > we extract the module from it and search for the function address in the > correct module (resolving problem no. 1). Then, the module reference is > taken directly in bpf_check_attach_target and stored in the bpf program > (in bpf_prog_aux). The reference is only released when the program is > unloaded (resolving problem no. 2). > > Signed-off-by: Viktor Malik <vmalik@redhat.com> > Acked-by: Jiri Olsa <jolsa@kernel.org> > --- My review of the critical part below. > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 45a082284464..3905bb20b9a1 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -18432,8 +18434,17 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, > else > addr = (long) tgt_prog->aux->func[subprog]->bpf_func; > } else { > - addr = kallsyms_lookup_name(tname); > + if (btf_is_module(btf)) { > + mod = btf_try_get_module(btf); > + if (mod) > + addr = find_kallsyms_symbol_value(mod, tname); > + else > + addr = 0; > + } else { > + addr = kallsyms_lookup_name(tname); > + } > if (!addr) { > + module_put(mod); > bpf_log(log, > "The address of function %s cannot be found\n", > tname); If btf_modules linked list is ensured to not remove the btf module during this operation, sure this is safe, as per the new guidelines I've posted for try_module_get() this seems to be using try_module_get() using the implied protection. Please review the docs. *If* it respects that usage then feel free to add: Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Luis
On 3/10/23 20:16, Luis Chamberlain wrote: > Please add linux-modules in the future. My review below. Sorry for missing that, I'll add it next time. > > On Fri, Mar 10, 2023 at 08:40:59AM +0100, Viktor Malik wrote: [snip] > My review of the critical part below. > >> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c >> index 45a082284464..3905bb20b9a1 100644 >> --- a/kernel/bpf/verifier.c >> +++ b/kernel/bpf/verifier.c >> @@ -18432,8 +18434,17 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, >> else >> addr = (long) tgt_prog->aux->func[subprog]->bpf_func; >> } else { >> - addr = kallsyms_lookup_name(tname); >> + if (btf_is_module(btf)) { >> + mod = btf_try_get_module(btf); >> + if (mod) >> + addr = find_kallsyms_symbol_value(mod, tname); >> + else >> + addr = 0; >> + } else { >> + addr = kallsyms_lookup_name(tname); >> + } >> if (!addr) { >> + module_put(mod); >> bpf_log(log, >> "The address of function %s cannot be found\n", >> tname); > > If btf_modules linked list is ensured to not remove the btf module > during this operation, sure this is safe, as per the new guidelines I've > posted for try_module_get() this seems to be using try_module_get() > using the implied protection. I believe that is the case. btf_try_get_module checks the BTF_F_MODULE_LIVE flag before calling try_module_get and the flag is set only when the module notifier callback is called with MODULE_STATE_LIVE. In addition, all BTF module operations are called under the same mutex, so the module cannot be removed in-between. > > Please review the docs. *If* it respects that usage then feel free to > add: > > Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Thanks for the review. Unless there are more change requests, I'll leave it up to the maintainers to add the tag. Viktor > > Luis >
Hi Viktor, Thank you for the patch! Yet something to improve: [auto build test ERROR on bpf-next/master] url: https://github.com/intel-lab-lkp/linux/commits/Viktor-Malik/bpf-Fix-attaching-fentry-fexit-fmod_ret-lsm-to-modules/20230310-154848 base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master patch link: https://lore.kernel.org/r/3f6a9d8ae850532b5ef864ef16327b0f7a669063.1678432753.git.vmalik%40redhat.com patch subject: [PATCH bpf-next v10 1/2] bpf: Fix attaching fentry/fexit/fmod_ret/lsm to modules config: arm-buildonly-randconfig-r005-20230312 (https://download.01.org/0day-ci/archive/20230316/202303160919.SGyfD0uE-lkp@intel.com/config) compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project 67409911353323ca5edf2049ef0df54132fa1ca7) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install arm cross compiling tool for clang build # apt-get install binutils-arm-linux-gnueabi # https://github.com/intel-lab-lkp/linux/commit/a6713fb8bbf7954ee98fec48f2a1f1e33814d92a git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Viktor-Malik/bpf-Fix-attaching-fentry-fexit-fmod_ret-lsm-to-modules/20230310-154848 git checkout a6713fb8bbf7954ee98fec48f2a1f1e33814d92a # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash kernel/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> | Link: https://lore.kernel.org/oe-kbuild-all/202303160919.SGyfD0uE-lkp@intel.com/ All errors (new ones prefixed by >>): In file included from kernel/bpf/verifier.c:27: >> kernel/bpf/../module/internal.h:260:14: error: expected ')' const char *name) ^ kernel/bpf/../module/internal.h:259:55: note: to match this '(' static inline unsigned long find_kallsyms_symbol_value(struct module *mod ^ >> kernel/bpf/verifier.c:18440:45: error: too many arguments to function call, expected single argument 'mod', have 2 arguments addr = find_kallsyms_symbol_value(mod, tname); ~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~~~~ kernel/bpf/../module/internal.h:259:29: note: 'find_kallsyms_symbol_value' declared here static inline unsigned long find_kallsyms_symbol_value(struct module *mod ^ 2 errors generated. vim +260 kernel/bpf/../module/internal.h 250 251 static inline bool sect_empty(const Elf_Shdr *sect) 252 { 253 return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; 254 } 255 #else /* !CONFIG_KALLSYMS */ 256 static inline void init_build_id(struct module *mod, const struct load_info *info) { } 257 static inline void layout_symtab(struct module *mod, struct load_info *info) { } 258 static inline void add_kallsyms(struct module *mod, const struct load_info *info) { } 259 static inline unsigned long find_kallsyms_symbol_value(struct module *mod > 260 const char *name) 261 { 262 return 0; 263 } 264 #endif /* CONFIG_KALLSYMS */ 265
On Wed, Mar 15, 2023 at 6:32 PM kernel test robot <lkp@intel.com> wrote: > > Hi Viktor, > > Thank you for the patch! Yet something to improve: Argh. Comma is missing. Viktor, please send a fix asap.
On Wed, Mar 15, 2023 at 6:34 PM Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote: > > On Wed, Mar 15, 2023 at 6:32 PM kernel test robot <lkp@intel.com> wrote: > > > > Hi Viktor, > > > > Thank you for the patch! Yet something to improve: > > Argh. Comma is missing. > Viktor, > please send a fix asap. Actually, since it was at the top of bpf-next I fixed it myself and force pushed bpf-next.
On 3/16/23 02:39, Alexei Starovoitov wrote: > On Wed, Mar 15, 2023 at 6:34 PM Alexei Starovoitov > <alexei.starovoitov@gmail.com> wrote: >> >> On Wed, Mar 15, 2023 at 6:32 PM kernel test robot <lkp@intel.com> wrote: >>> >>> Hi Viktor, >>> >>> Thank you for the patch! Yet something to improve: >> >> Argh. Comma is missing. >> Viktor, >> please send a fix asap. > > Actually, since it was at the top of bpf-next > I fixed it myself and force pushed bpf-next. > Ah, I'm sorry, I completely missed that. Thanks for fixing it, Alexei! Viktor
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e64ff1e89fb2..4aade43f61f0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1098,6 +1098,7 @@ struct bpf_trampoline { struct bpf_attach_target_info { struct btf_func_model fmodel; long tgt_addr; + struct module *tgt_mod; const char *tgt_name; const struct btf_type *tgt_type; }; @@ -1401,6 +1402,7 @@ struct bpf_prog_aux { * main prog always has linfo_idx == 0 */ u32 linfo_idx; + struct module *mod; u32 num_exentries; struct exception_table_entry *extable; union { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index f406dfa13792..30fac7d9eddb 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2051,6 +2051,7 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred) { bpf_prog_kallsyms_del_all(prog); btf_put(prog->aux->btf); + module_put(prog->aux->mod); kvfree(prog->aux->jited_linfo); kvfree(prog->aux->linfo); kfree(prog->aux->kfunc_tab); @@ -3097,6 +3098,11 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog, if (err) goto out_unlock; + if (tgt_info.tgt_mod) { + module_put(prog->aux->mod); + prog->aux->mod = tgt_info.tgt_mod; + } + tr = bpf_trampoline_get(key, &tgt_info); if (!tr) { err = -ENOMEM; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index d0ed7d6f5eec..f61d5138b12b 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -9,7 +9,6 @@ #include <linux/btf.h> #include <linux/rcupdate_trace.h> #include <linux/rcupdate_wait.h> -#include <linux/module.h> #include <linux/static_call.h> #include <linux/bpf_verifier.h> #include <linux/bpf_lsm.h> @@ -172,26 +171,6 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key) return tr; } -static int bpf_trampoline_module_get(struct bpf_trampoline *tr) -{ - struct module *mod; - int err = 0; - - preempt_disable(); - mod = __module_text_address((unsigned long) tr->func.addr); - if (mod && !try_module_get(mod)) - err = -ENOENT; - preempt_enable(); - tr->mod = mod; - return err; -} - -static void bpf_trampoline_module_put(struct bpf_trampoline *tr) -{ - module_put(tr->mod); - tr->mod = NULL; -} - static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr) { void *ip = tr->func.addr; @@ -202,8 +181,6 @@ static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr) else ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL); - if (!ret) - bpf_trampoline_module_put(tr); return ret; } @@ -238,9 +215,6 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr) tr->func.ftrace_managed = true; } - if (bpf_trampoline_module_get(tr)) - return -ENOENT; - if (tr->func.ftrace_managed) { ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1); ret = register_ftrace_direct_multi(tr->fops, (long)new_addr); @@ -248,8 +222,6 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr) ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr); } - if (ret) - bpf_trampoline_module_put(tr); return ret; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 45a082284464..3905bb20b9a1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -24,6 +24,7 @@ #include <linux/bpf_lsm.h> #include <linux/btf_ids.h> #include <linux/poison.h> +#include "../module/internal.h" #include "disasm.h" @@ -18259,6 +18260,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, const char *tname; struct btf *btf; long addr = 0; + struct module *mod = NULL; if (!btf_id) { bpf_log(log, "Tracing programs must provide btf_id\n"); @@ -18432,8 +18434,17 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, else addr = (long) tgt_prog->aux->func[subprog]->bpf_func; } else { - addr = kallsyms_lookup_name(tname); + if (btf_is_module(btf)) { + mod = btf_try_get_module(btf); + if (mod) + addr = find_kallsyms_symbol_value(mod, tname); + else + addr = 0; + } else { + addr = kallsyms_lookup_name(tname); + } if (!addr) { + module_put(mod); bpf_log(log, "The address of function %s cannot be found\n", tname); @@ -18473,11 +18484,13 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, break; } if (ret) { + module_put(mod); bpf_log(log, "%s is not sleepable\n", tname); return ret; } } else if (prog->expected_attach_type == BPF_MODIFY_RETURN) { if (tgt_prog) { + module_put(mod); bpf_log(log, "can't modify return codes of BPF programs\n"); return -EINVAL; } @@ -18486,6 +18499,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, !check_attach_modify_return(addr, tname)) ret = 0; if (ret) { + module_put(mod); bpf_log(log, "%s() is not modifiable\n", tname); return ret; } @@ -18496,6 +18510,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, tgt_info->tgt_addr = addr; tgt_info->tgt_name = tname; tgt_info->tgt_type = t; + tgt_info->tgt_mod = mod; return 0; } @@ -18575,6 +18590,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) /* store info about the attachment target that will be used later */ prog->aux->attach_func_proto = tgt_info.tgt_type; prog->aux->attach_func_name = tgt_info.tgt_name; + prog->aux->mod = tgt_info.tgt_mod; if (tgt_prog) { prog->aux->saved_dst_prog_type = tgt_prog->type; diff --git a/kernel/module/internal.h b/kernel/module/internal.h index 2e2bf236f558..5cb103a46018 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -256,6 +256,11 @@ static inline bool sect_empty(const Elf_Shdr *sect) static inline void init_build_id(struct module *mod, const struct load_info *info) { } static inline void layout_symtab(struct module *mod, struct load_info *info) { } static inline void add_kallsyms(struct module *mod, const struct load_info *info) { } +static inline unsigned long find_kallsyms_symbol_value(struct module *mod + const char *name) +{ + return 0; +} #endif /* CONFIG_KALLSYMS */ #ifdef CONFIG_SYSFS