Message ID | 20220222141303.1392190-10-atomlin@redhat.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | module: core code clean up | expand |
Le 22/02/2022 à 15:12, Aaron Tomlin a écrit : > No functional change. > > This patch migrates kallsyms code out of core module > code kernel/module/kallsyms.c > > Signed-off-by: Aaron Tomlin <atomlin@redhat.com> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> > --- > kernel/module/Makefile | 1 + > kernel/module/internal.h | 29 +++ > kernel/module/kallsyms.c | 506 +++++++++++++++++++++++++++++++++++++ > kernel/module/main.c | 531 +-------------------------------------- > 4 files changed, 542 insertions(+), 525 deletions(-) > create mode 100644 kernel/module/kallsyms.c > > diff --git a/kernel/module/Makefile b/kernel/module/Makefile > index 12388627725c..9901bed3ab5b 100644 > --- a/kernel/module/Makefile > +++ b/kernel/module/Makefile > @@ -14,3 +14,4 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o > obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o > obj-$(CONFIG_STRICT_MODULE_RWX) += strict_rwx.o > obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o > +obj-$(CONFIG_KALLSYMS) += kallsyms.o > diff --git a/kernel/module/internal.h b/kernel/module/internal.h > index b0c360839f63..44ca05b9eb8f 100644 > --- a/kernel/module/internal.h > +++ b/kernel/module/internal.h > @@ -68,6 +68,19 @@ struct load_info { > }; > > int mod_verify_sig(const void *mod, struct load_info *info); > +struct module *find_module_all(const char *name, size_t len, bool even_unformed); > +int cmp_name(const void *name, const void *sym); > +long module_get_offset(struct module *mod, unsigned int *size, Elf_Shdr *sechdr, > + unsigned int section); > + > +static inline unsigned long kernel_symbol_value(const struct kernel_symbol *sym) > +{ > +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS > + return (unsigned long)offset_to_ptr(&sym->value_offset); > +#else > + return sym->value; > +#endif > +} > > #ifdef CONFIG_LIVEPATCH > int copy_module_elf(struct module *mod, struct load_info *info); > @@ -174,3 +187,19 @@ void kmemleak_load_module(const struct module *mod, const struct load_info *info > static inline void kmemleak_load_module(const struct module *mod, > const struct load_info *info) { } > #endif /* CONFIG_DEBUG_KMEMLEAK */ > + > +#ifdef CONFIG_KALLSYMS > +void init_build_id(struct module *mod, const struct load_info *info); > +void layout_symtab(struct module *mod, struct load_info *info); > +void add_kallsyms(struct module *mod, const struct load_info *info); > +unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name); > + > +static inline bool sect_empty(const Elf_Shdr *sect) > +{ > + return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; > +} > +#else /* !CONFIG_KALLSYMS */ > +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) { } > +#endif /* CONFIG_KALLSYMS */ > diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c > new file mode 100644 > index 000000000000..b6d49bb5afed > --- /dev/null > +++ b/kernel/module/kallsyms.c > @@ -0,0 +1,506 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Module kallsyms support > + * > + * Copyright (C) 2010 Rusty Russell > + */ > + > +#include <linux/module.h> > +#include <linux/kallsyms.h> > +#include <linux/buildid.h> > +#include <linux/bsearch.h> > +#include "internal.h" > + > +/* Lookup exported symbol in given range of kernel_symbols */ > +static const struct kernel_symbol *lookup_exported_symbol(const char *name, > + const struct kernel_symbol *start, > + const struct kernel_symbol *stop) > +{ > + return bsearch(name, start, stop - start, > + sizeof(struct kernel_symbol), cmp_name); > +} > + > +static int is_exported(const char *name, unsigned long value, > + const struct module *mod) > +{ > + const struct kernel_symbol *ks; > + > + if (!mod) > + ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab); > + else > + ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms); > + > + return ks && kernel_symbol_value(ks) == value; > +} > + > +/* As per nm */ > +static char elf_type(const Elf_Sym *sym, const struct load_info *info) > +{ > + const Elf_Shdr *sechdrs = info->sechdrs; > + > + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { > + if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) > + return 'v'; > + else > + return 'w'; > + } > + if (sym->st_shndx == SHN_UNDEF) > + return 'U'; > + if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) > + return 'a'; > + if (sym->st_shndx >= SHN_LORESERVE) > + return '?'; > + if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) > + return 't'; > + if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC && > + sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { > + if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) > + return 'r'; > + else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) > + return 'g'; > + else > + return 'd'; > + } > + if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { > + if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) > + return 's'; > + else > + return 'b'; > + } > + if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, > + ".debug")) { > + return 'n'; > + } > + return '?'; > +} > + > +static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, > + unsigned int shnum, unsigned int pcpundx) > +{ > + const Elf_Shdr *sec; > + > + if (src->st_shndx == SHN_UNDEF || > + src->st_shndx >= shnum || > + !src->st_name) > + return false; > + > +#ifdef CONFIG_KALLSYMS_ALL > + if (src->st_shndx == pcpundx) > + return true; > +#endif > + > + sec = sechdrs + src->st_shndx; > + if (!(sec->sh_flags & SHF_ALLOC) > +#ifndef CONFIG_KALLSYMS_ALL > + || !(sec->sh_flags & SHF_EXECINSTR) > +#endif > + || (sec->sh_entsize & INIT_OFFSET_MASK)) > + return false; > + > + return true; > +} > + > +/* > + * We only allocate and copy the strings needed by the parts of symtab > + * we keep. This is simple, but has the effect of making multiple > + * copies of duplicates. We could be more sophisticated, see > + * linux-kernel thread starting with > + * <73defb5e4bca04a6431392cc341112b1@localhost>. > + */ > +void layout_symtab(struct module *mod, struct load_info *info) > +{ > + Elf_Shdr *symsect = info->sechdrs + info->index.sym; > + Elf_Shdr *strsect = info->sechdrs + info->index.str; > + const Elf_Sym *src; > + unsigned int i, nsrc, ndst, strtab_size = 0; > + > + /* Put symbol section at end of init part of module. */ > + symsect->sh_flags |= SHF_ALLOC; > + symsect->sh_entsize = module_get_offset(mod, &mod->init_layout.size, symsect, > + info->index.sym) | INIT_OFFSET_MASK; > + pr_debug("\t%s\n", info->secstrings + symsect->sh_name); > + > + src = (void *)info->hdr + symsect->sh_offset; > + nsrc = symsect->sh_size / sizeof(*src); > + > + /* Compute total space required for the core symbols' strtab. */ > + for (ndst = i = 0; i < nsrc; i++) { > + if (i == 0 || is_livepatch_module(mod) || > + is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, > + info->index.pcpu)) { > + strtab_size += strlen(&info->strtab[src[i].st_name]) + 1; > + ndst++; > + } > + } > + > + /* Append room for core symbols at end of core part. */ > + info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); > + info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); > + mod->core_layout.size += strtab_size; > + info->core_typeoffs = mod->core_layout.size; > + mod->core_layout.size += ndst * sizeof(char); > + mod->core_layout.size = debug_align(mod->core_layout.size); > + > + /* Put string table section at end of init part of module. */ > + strsect->sh_flags |= SHF_ALLOC; > + strsect->sh_entsize = module_get_offset(mod, &mod->init_layout.size, strsect, > + info->index.str) | INIT_OFFSET_MASK; > + pr_debug("\t%s\n", info->secstrings + strsect->sh_name); > + > + /* We'll tack temporary mod_kallsyms on the end. */ > + mod->init_layout.size = ALIGN(mod->init_layout.size, > + __alignof__(struct mod_kallsyms)); > + info->mod_kallsyms_init_off = mod->init_layout.size; > + mod->init_layout.size += sizeof(struct mod_kallsyms); > + info->init_typeoffs = mod->init_layout.size; > + mod->init_layout.size += nsrc * sizeof(char); > + mod->init_layout.size = debug_align(mod->init_layout.size); > +} > + > +/* > + * We use the full symtab and strtab which layout_symtab arranged to > + * be appended to the init section. Later we switch to the cut-down > + * core-only ones. > + */ > +void add_kallsyms(struct module *mod, const struct load_info *info) > +{ > + unsigned int i, ndst; > + const Elf_Sym *src; > + Elf_Sym *dst; > + char *s; > + Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; > + > + /* Set up to point into init section. */ > + mod->kallsyms = (void __rcu *)mod->init_layout.base + > + info->mod_kallsyms_init_off; > + > + /* The following is safe since this pointer cannot change */ > + rcu_dereference_sched(mod->kallsyms)->symtab = (void *)symsec->sh_addr; > + rcu_dereference_sched(mod->kallsyms)->num_symtab = symsec->sh_size / sizeof(Elf_Sym); > + /* Make sure we get permanent strtab: don't use info->strtab. */ > + rcu_dereference_sched(mod->kallsyms)->strtab = > + (void *)info->sechdrs[info->index.str].sh_addr; > + rcu_dereference_sched(mod->kallsyms)->typetab = > + mod->init_layout.base + info->init_typeoffs; > + > + /* > + * Now populate the cut down core kallsyms for after init > + * and set types up while we still have access to sections. > + */ > + mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; > + mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; > + mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs; > + src = rcu_dereference_sched(mod->kallsyms)->symtab; > + for (ndst = i = 0; i < rcu_dereference_sched(mod->kallsyms)->num_symtab; i++) { > + rcu_dereference_sched(mod->kallsyms)->typetab[i] = elf_type(src + i, info); > + if (i == 0 || is_livepatch_module(mod) || > + is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, > + info->index.pcpu)) { > + mod->core_kallsyms.typetab[ndst] = > + rcu_dereference_sched(mod->kallsyms)->typetab[i]; > + dst[ndst] = src[i]; > + dst[ndst++].st_name = s - mod->core_kallsyms.strtab; > + s += strscpy(s, > + &rcu_dereference_sched(mod->kallsyms)->strtab[src[i].st_name], > + KSYM_NAME_LEN) + 1; > + } > + } > + mod->core_kallsyms.num_symtab = ndst; > +} > + > +#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) > +void init_build_id(struct module *mod, const struct load_info *info) > +{ > + const Elf_Shdr *sechdr; > + unsigned int i; > + > + for (i = 0; i < info->hdr->e_shnum; i++) { > + sechdr = &info->sechdrs[i]; > + if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE && > + !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id, > + sechdr->sh_size)) > + break; > + } > +} > +#else > +void init_build_id(struct module *mod, const struct load_info *info) > +{ > +} > +#endif > + > +/* > + * This ignores the intensely annoying "mapping symbols" found > + * in ARM ELF files: $a, $t and $d. > + */ > +static inline int is_arm_mapping_symbol(const char *str) > +{ > + if (str[0] == '.' && str[1] == 'L') > + return true; > + return str[0] == '$' && strchr("axtd", str[1]) && > + (str[2] == '\0' || str[2] == '.'); > +} > + > +static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) > +{ > + return kallsyms->strtab + kallsyms->symtab[symnum].st_name; > +} > + > +/* > + * Given a module and address, find the corresponding symbol and return its name > + * while providing its size and offset if needed. > + */ > +static const char *find_kallsyms_symbol(struct module *mod, > + unsigned long addr, > + unsigned long *size, > + unsigned long *offset) > +{ > + unsigned int i, best = 0; > + unsigned long nextval, bestval; > + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); > + > + /* At worse, next value is at end of module */ > + if (within_module_init(addr, mod)) > + nextval = (unsigned long)mod->init_layout.base + mod->init_layout.text_size; > + else > + nextval = (unsigned long)mod->core_layout.base + mod->core_layout.text_size; > + > + bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); > + > + /* > + * Scan for closest preceding symbol, and next symbol. (ELF > + * starts real symbols at 1). > + */ > + for (i = 1; i < kallsyms->num_symtab; i++) { > + const Elf_Sym *sym = &kallsyms->symtab[i]; > + unsigned long thisval = kallsyms_symbol_value(sym); > + > + if (sym->st_shndx == SHN_UNDEF) > + continue; > + > + /* > + * We ignore unnamed symbols: they're uninformative > + * and inserted at a whim. > + */ > + if (*kallsyms_symbol_name(kallsyms, i) == '\0' || > + is_arm_mapping_symbol(kallsyms_symbol_name(kallsyms, i))) > + continue; > + > + if (thisval <= addr && thisval > bestval) { > + best = i; > + bestval = thisval; > + } > + if (thisval > addr && thisval < nextval) > + nextval = thisval; > + } > + > + if (!best) > + return NULL; > + > + if (size) > + *size = nextval - bestval; > + if (offset) > + *offset = addr - bestval; > + > + return kallsyms_symbol_name(kallsyms, best); > +} > + > +void * __weak dereference_module_function_descriptor(struct module *mod, > + void *ptr) > +{ > + return ptr; > +} > + > +/* > + * For kallsyms to ask for address resolution. NULL means not found. Careful > + * not to lock to avoid deadlock on oopses, simply disable preemption. > + */ > +const char *module_address_lookup(unsigned long addr, > + unsigned long *size, > + unsigned long *offset, > + char **modname, > + const unsigned char **modbuildid, > + char *namebuf) > +{ > + const char *ret = NULL; > + struct module *mod; > + > + preempt_disable(); > + mod = __module_address(addr); > + if (mod) { > + if (modname) > + *modname = mod->name; > + if (modbuildid) { > +#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) > + *modbuildid = mod->build_id; > +#else > + *modbuildid = NULL; > +#endif > + } > + > + ret = find_kallsyms_symbol(mod, addr, size, offset); > + } > + /* Make a copy in here where it's safe */ > + if (ret) { > + strncpy(namebuf, ret, KSYM_NAME_LEN - 1); > + ret = namebuf; > + } > + preempt_enable(); > + > + return ret; > +} > + > +int lookup_module_symbol_name(unsigned long addr, char *symname) > +{ > + struct module *mod; > + > + preempt_disable(); > + list_for_each_entry_rcu(mod, &modules, list) { > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + if (within_module(addr, mod)) { > + const char *sym; > + > + sym = find_kallsyms_symbol(mod, addr, NULL, NULL); > + if (!sym) > + goto out; > + > + strscpy(symname, sym, KSYM_NAME_LEN); > + preempt_enable(); > + return 0; > + } > + } > +out: > + preempt_enable(); > + return -ERANGE; > +} > + > +int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, > + unsigned long *offset, char *modname, char *name) > +{ > + struct module *mod; > + > + preempt_disable(); > + list_for_each_entry_rcu(mod, &modules, list) { > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + if (within_module(addr, mod)) { > + const char *sym; > + > + sym = find_kallsyms_symbol(mod, addr, size, offset); > + if (!sym) > + goto out; > + if (modname) > + strscpy(modname, mod->name, MODULE_NAME_LEN); > + if (name) > + strscpy(name, sym, KSYM_NAME_LEN); > + preempt_enable(); > + return 0; > + } > + } > +out: > + preempt_enable(); > + return -ERANGE; > +} > + > +int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, > + char *name, char *module_name, int *exported) > +{ > + struct module *mod; > + > + preempt_disable(); > + list_for_each_entry_rcu(mod, &modules, list) { > + struct mod_kallsyms *kallsyms; > + > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + kallsyms = rcu_dereference_sched(mod->kallsyms); > + if (symnum < kallsyms->num_symtab) { > + const Elf_Sym *sym = &kallsyms->symtab[symnum]; > + > + *value = kallsyms_symbol_value(sym); > + *type = kallsyms->typetab[symnum]; > + strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN); > + strscpy(module_name, mod->name, MODULE_NAME_LEN); > + *exported = is_exported(name, *value, mod); > + preempt_enable(); > + return 0; > + } > + symnum -= kallsyms->num_symtab; > + } > + preempt_enable(); > + return -ERANGE; > +} > + > +/* Given a module and name of symbol, find and return the symbol's value */ > +unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) > +{ > + unsigned int i; > + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); > + > + for (i = 0; i < kallsyms->num_symtab; i++) { > + const Elf_Sym *sym = &kallsyms->symtab[i]; > + > + if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 && > + sym->st_shndx != SHN_UNDEF) > + return kallsyms_symbol_value(sym); > + } > + return 0; > +} > + > +/* Look for this name: can be of form module:name. */ > +unsigned long module_kallsyms_lookup_name(const char *name) > +{ > + struct module *mod; > + char *colon; > + unsigned long ret = 0; > + > + /* Don't lock: we're in enough trouble already. */ > + preempt_disable(); > + if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { > + if ((mod = find_module_all(name, colon - name, false)) != NULL) > + ret = find_kallsyms_symbol_value(mod, colon + 1); > + } else { > + list_for_each_entry_rcu(mod, &modules, list) { > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + if ((ret = find_kallsyms_symbol_value(mod, name)) != 0) > + break; > + } > + } > + preempt_enable(); > + return ret; > +} > + > +#ifdef CONFIG_LIVEPATCH > +int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, > + struct module *, unsigned long), > + void *data) > +{ > + struct module *mod; > + unsigned int i; > + int ret = 0; > + > + mutex_lock(&module_mutex); > + list_for_each_entry(mod, &modules, list) { > + /* Still use rcu_dereference_sched to remain compliant with sparse */ > + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); > + > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + for (i = 0; i < kallsyms->num_symtab; i++) { > + const Elf_Sym *sym = &kallsyms->symtab[i]; > + > + if (sym->st_shndx == SHN_UNDEF) > + continue; > + > + ret = fn(data, kallsyms_symbol_name(kallsyms, i), > + mod, kallsyms_symbol_value(sym)); > + if (ret != 0) > + goto out; > + } > + } > +out: > + mutex_unlock(&module_mutex); > + return ret; > +} > +#endif /* CONFIG_LIVEPATCH */ > diff --git a/kernel/module/main.c b/kernel/module/main.c > index 7dd283959c5c..952079987ea4 100644 > --- a/kernel/module/main.c > +++ b/kernel/module/main.c > @@ -288,15 +288,6 @@ static bool check_exported_symbol(const struct symsearch *syms, > return true; > } > > -static unsigned long kernel_symbol_value(const struct kernel_symbol *sym) > -{ > -#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS > - return (unsigned long)offset_to_ptr(&sym->value_offset); > -#else > - return sym->value; > -#endif > -} > - > static const char *kernel_symbol_name(const struct kernel_symbol *sym) > { > #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS > @@ -317,7 +308,7 @@ static const char *kernel_symbol_namespace(const struct kernel_symbol *sym) > #endif > } > > -static int cmp_name(const void *name, const void *sym) > +int cmp_name(const void *name, const void *sym) > { > return strcmp(name, kernel_symbol_name(sym)); > } > @@ -387,8 +378,8 @@ static bool find_symbol(struct find_symbol_arg *fsa) > * Search for module by name: must hold module_mutex (or preempt disabled > * for read-only access). > */ > -static struct module *find_module_all(const char *name, size_t len, > - bool even_unformed) > +struct module *find_module_all(const char *name, size_t len, > + bool even_unformed) > { > struct module *mod; > > @@ -1294,13 +1285,6 @@ resolve_symbol_wait(struct module *mod, > return ksym; > } > > -#ifdef CONFIG_KALLSYMS > -static inline bool sect_empty(const Elf_Shdr *sect) > -{ > - return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; > -} > -#endif > - > /* > * /sys/module/foo/sections stuff > * J. Corbet <corbet@lwn.net> > @@ -2065,7 +2049,7 @@ unsigned int __weak arch_mod_section_prepend(struct module *mod, > } > > /* Update size with this section: return offset. */ > -static long get_offset(struct module *mod, unsigned int *size, > +long module_get_offset(struct module *mod, unsigned int *size, > Elf_Shdr *sechdr, unsigned int section) > { > long ret; > @@ -2121,7 +2105,7 @@ static void layout_sections(struct module *mod, struct load_info *info) > || s->sh_entsize != ~0UL > || module_init_layout_section(sname)) > continue; > - s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i); > + s->sh_entsize = module_get_offset(mod, &mod->core_layout.size, s, i); > pr_debug("\t%s\n", sname); > } > switch (m) { > @@ -2154,7 +2138,7 @@ static void layout_sections(struct module *mod, struct load_info *info) > || s->sh_entsize != ~0UL > || !module_init_layout_section(sname)) > continue; > - s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i) > + s->sh_entsize = (module_get_offset(mod, &mod->init_layout.size, s, i) > | INIT_OFFSET_MASK); > pr_debug("\t%s\n", sname); > } > @@ -2267,228 +2251,6 @@ static void free_modinfo(struct module *mod) > } > } > > -#ifdef CONFIG_KALLSYMS > - > -/* Lookup exported symbol in given range of kernel_symbols */ > -static const struct kernel_symbol *lookup_exported_symbol(const char *name, > - const struct kernel_symbol *start, > - const struct kernel_symbol *stop) > -{ > - return bsearch(name, start, stop - start, > - sizeof(struct kernel_symbol), cmp_name); > -} > - > -static int is_exported(const char *name, unsigned long value, > - const struct module *mod) > -{ > - const struct kernel_symbol *ks; > - if (!mod) > - ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab); > - else > - ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms); > - > - return ks != NULL && kernel_symbol_value(ks) == value; > -} > - > -/* As per nm */ > -static char elf_type(const Elf_Sym *sym, const struct load_info *info) > -{ > - const Elf_Shdr *sechdrs = info->sechdrs; > - > - if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { > - if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) > - return 'v'; > - else > - return 'w'; > - } > - if (sym->st_shndx == SHN_UNDEF) > - return 'U'; > - if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) > - return 'a'; > - if (sym->st_shndx >= SHN_LORESERVE) > - return '?'; > - if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) > - return 't'; > - if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC > - && sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { > - if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) > - return 'r'; > - else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) > - return 'g'; > - else > - return 'd'; > - } > - if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { > - if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) > - return 's'; > - else > - return 'b'; > - } > - if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, > - ".debug")) { > - return 'n'; > - } > - return '?'; > -} > - > -static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, > - unsigned int shnum, unsigned int pcpundx) > -{ > - const Elf_Shdr *sec; > - > - if (src->st_shndx == SHN_UNDEF > - || src->st_shndx >= shnum > - || !src->st_name) > - return false; > - > -#ifdef CONFIG_KALLSYMS_ALL > - if (src->st_shndx == pcpundx) > - return true; > -#endif > - > - sec = sechdrs + src->st_shndx; > - if (!(sec->sh_flags & SHF_ALLOC) > -#ifndef CONFIG_KALLSYMS_ALL > - || !(sec->sh_flags & SHF_EXECINSTR) > -#endif > - || (sec->sh_entsize & INIT_OFFSET_MASK)) > - return false; > - > - return true; > -} > - > -/* > - * We only allocate and copy the strings needed by the parts of symtab > - * we keep. This is simple, but has the effect of making multiple > - * copies of duplicates. We could be more sophisticated, see > - * linux-kernel thread starting with > - * <73defb5e4bca04a6431392cc341112b1@localhost>. > - */ > -static void layout_symtab(struct module *mod, struct load_info *info) > -{ > - Elf_Shdr *symsect = info->sechdrs + info->index.sym; > - Elf_Shdr *strsect = info->sechdrs + info->index.str; > - const Elf_Sym *src; > - unsigned int i, nsrc, ndst, strtab_size = 0; > - > - /* Put symbol section at end of init part of module. */ > - symsect->sh_flags |= SHF_ALLOC; > - symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect, > - info->index.sym) | INIT_OFFSET_MASK; > - pr_debug("\t%s\n", info->secstrings + symsect->sh_name); > - > - src = (void *)info->hdr + symsect->sh_offset; > - nsrc = symsect->sh_size / sizeof(*src); > - > - /* Compute total space required for the core symbols' strtab. */ > - for (ndst = i = 0; i < nsrc; i++) { > - if (i == 0 || is_livepatch_module(mod) || > - is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, > - info->index.pcpu)) { > - strtab_size += strlen(&info->strtab[src[i].st_name])+1; > - ndst++; > - } > - } > - > - /* Append room for core symbols at end of core part. */ > - info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); > - info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); > - mod->core_layout.size += strtab_size; > - info->core_typeoffs = mod->core_layout.size; > - mod->core_layout.size += ndst * sizeof(char); > - mod->core_layout.size = debug_align(mod->core_layout.size); > - > - /* Put string table section at end of init part of module. */ > - strsect->sh_flags |= SHF_ALLOC; > - strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, > - info->index.str) | INIT_OFFSET_MASK; > - pr_debug("\t%s\n", info->secstrings + strsect->sh_name); > - > - /* We'll tack temporary mod_kallsyms on the end. */ > - mod->init_layout.size = ALIGN(mod->init_layout.size, > - __alignof__(struct mod_kallsyms)); > - info->mod_kallsyms_init_off = mod->init_layout.size; > - mod->init_layout.size += sizeof(struct mod_kallsyms); > - info->init_typeoffs = mod->init_layout.size; > - mod->init_layout.size += nsrc * sizeof(char); > - mod->init_layout.size = debug_align(mod->init_layout.size); > -} > - > -/* > - * We use the full symtab and strtab which layout_symtab arranged to > - * be appended to the init section. Later we switch to the cut-down > - * core-only ones. > - */ > -static void add_kallsyms(struct module *mod, const struct load_info *info) > -{ > - unsigned int i, ndst; > - const Elf_Sym *src; > - Elf_Sym *dst; > - char *s; > - Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; > - > - /* Set up to point into init section. */ > - mod->kallsyms = mod->init_layout.base + info->mod_kallsyms_init_off; > - > - mod->kallsyms->symtab = (void *)symsec->sh_addr; > - mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym); > - /* Make sure we get permanent strtab: don't use info->strtab. */ > - mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr; > - mod->kallsyms->typetab = mod->init_layout.base + info->init_typeoffs; > - > - /* > - * Now populate the cut down core kallsyms for after init > - * and set types up while we still have access to sections. > - */ > - mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; > - mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; > - mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs; > - src = mod->kallsyms->symtab; > - for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { > - mod->kallsyms->typetab[i] = elf_type(src + i, info); > - if (i == 0 || is_livepatch_module(mod) || > - is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, > - info->index.pcpu)) { > - mod->core_kallsyms.typetab[ndst] = > - mod->kallsyms->typetab[i]; > - dst[ndst] = src[i]; > - dst[ndst++].st_name = s - mod->core_kallsyms.strtab; > - s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name], > - KSYM_NAME_LEN) + 1; > - } > - } > - mod->core_kallsyms.num_symtab = ndst; > -} > -#else > -static inline void layout_symtab(struct module *mod, struct load_info *info) > -{ > -} > - > -static void add_kallsyms(struct module *mod, const struct load_info *info) > -{ > -} > -#endif /* CONFIG_KALLSYMS */ > - > -#if IS_ENABLED(CONFIG_KALLSYMS) && IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) > -static void init_build_id(struct module *mod, const struct load_info *info) > -{ > - const Elf_Shdr *sechdr; > - unsigned int i; > - > - for (i = 0; i < info->hdr->e_shnum; i++) { > - sechdr = &info->sechdrs[i]; > - if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE && > - !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id, > - sechdr->sh_size)) > - break; > - } > -} > -#else > -static void init_build_id(struct module *mod, const struct load_info *info) > -{ > -} > -#endif > - > static void dynamic_debug_setup(struct module *mod, struct _ddebug *debug, unsigned int num) > { > if (!debug) > @@ -3799,287 +3561,6 @@ static inline int within(unsigned long addr, void *start, unsigned long size) > return ((void *)addr >= start && (void *)addr < start + size); > } > > -#ifdef CONFIG_KALLSYMS > -/* > - * This ignores the intensely annoying "mapping symbols" found > - * in ARM ELF files: $a, $t and $d. > - */ > -static inline int is_arm_mapping_symbol(const char *str) > -{ > - if (str[0] == '.' && str[1] == 'L') > - return true; > - return str[0] == '$' && strchr("axtd", str[1]) > - && (str[2] == '\0' || str[2] == '.'); > -} > - > -static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) > -{ > - return kallsyms->strtab + kallsyms->symtab[symnum].st_name; > -} > - > -/* > - * Given a module and address, find the corresponding symbol and return its name > - * while providing its size and offset if needed. > - */ > -static const char *find_kallsyms_symbol(struct module *mod, > - unsigned long addr, > - unsigned long *size, > - unsigned long *offset) > -{ > - unsigned int i, best = 0; > - unsigned long nextval, bestval; > - struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); > - > - /* At worse, next value is at end of module */ > - if (within_module_init(addr, mod)) > - nextval = (unsigned long)mod->init_layout.base+mod->init_layout.text_size; > - else > - nextval = (unsigned long)mod->core_layout.base+mod->core_layout.text_size; > - > - bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); > - > - /* > - * Scan for closest preceding symbol, and next symbol. (ELF > - * starts real symbols at 1). > - */ > - for (i = 1; i < kallsyms->num_symtab; i++) { > - const Elf_Sym *sym = &kallsyms->symtab[i]; > - unsigned long thisval = kallsyms_symbol_value(sym); > - > - if (sym->st_shndx == SHN_UNDEF) > - continue; > - > - /* > - * We ignore unnamed symbols: they're uninformative > - * and inserted at a whim. > - */ > - if (*kallsyms_symbol_name(kallsyms, i) == '\0' > - || is_arm_mapping_symbol(kallsyms_symbol_name(kallsyms, i))) > - continue; > - > - if (thisval <= addr && thisval > bestval) { > - best = i; > - bestval = thisval; > - } > - if (thisval > addr && thisval < nextval) > - nextval = thisval; > - } > - > - if (!best) > - return NULL; > - > - if (size) > - *size = nextval - bestval; > - if (offset) > - *offset = addr - bestval; > - > - return kallsyms_symbol_name(kallsyms, best); > -} > - > -void * __weak dereference_module_function_descriptor(struct module *mod, > - void *ptr) > -{ > - return ptr; > -} > - > -/* > - * For kallsyms to ask for address resolution. NULL means not found. Careful > - * not to lock to avoid deadlock on oopses, simply disable preemption. > - */ > -const char *module_address_lookup(unsigned long addr, > - unsigned long *size, > - unsigned long *offset, > - char **modname, > - const unsigned char **modbuildid, > - char *namebuf) > -{ > - const char *ret = NULL; > - struct module *mod; > - > - preempt_disable(); > - mod = __module_address(addr); > - if (mod) { > - if (modname) > - *modname = mod->name; > - if (modbuildid) { > -#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) > - *modbuildid = mod->build_id; > -#else > - *modbuildid = NULL; > -#endif > - } > - > - ret = find_kallsyms_symbol(mod, addr, size, offset); > - } > - /* Make a copy in here where it's safe */ > - if (ret) { > - strncpy(namebuf, ret, KSYM_NAME_LEN - 1); > - ret = namebuf; > - } > - preempt_enable(); > - > - return ret; > -} > - > -int lookup_module_symbol_name(unsigned long addr, char *symname) > -{ > - struct module *mod; > - > - preempt_disable(); > - list_for_each_entry_rcu(mod, &modules, list) { > - if (mod->state == MODULE_STATE_UNFORMED) > - continue; > - if (within_module(addr, mod)) { > - const char *sym; > - > - sym = find_kallsyms_symbol(mod, addr, NULL, NULL); > - if (!sym) > - goto out; > - > - strlcpy(symname, sym, KSYM_NAME_LEN); > - preempt_enable(); > - return 0; > - } > - } > -out: > - preempt_enable(); > - return -ERANGE; > -} > - > -int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, > - unsigned long *offset, char *modname, char *name) > -{ > - struct module *mod; > - > - preempt_disable(); > - list_for_each_entry_rcu(mod, &modules, list) { > - if (mod->state == MODULE_STATE_UNFORMED) > - continue; > - if (within_module(addr, mod)) { > - const char *sym; > - > - sym = find_kallsyms_symbol(mod, addr, size, offset); > - if (!sym) > - goto out; > - if (modname) > - strlcpy(modname, mod->name, MODULE_NAME_LEN); > - if (name) > - strlcpy(name, sym, KSYM_NAME_LEN); > - preempt_enable(); > - return 0; > - } > - } > -out: > - preempt_enable(); > - return -ERANGE; > -} > - > -int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, > - char *name, char *module_name, int *exported) > -{ > - struct module *mod; > - > - preempt_disable(); > - list_for_each_entry_rcu(mod, &modules, list) { > - struct mod_kallsyms *kallsyms; > - > - if (mod->state == MODULE_STATE_UNFORMED) > - continue; > - kallsyms = rcu_dereference_sched(mod->kallsyms); > - if (symnum < kallsyms->num_symtab) { > - const Elf_Sym *sym = &kallsyms->symtab[symnum]; > - > - *value = kallsyms_symbol_value(sym); > - *type = kallsyms->typetab[symnum]; > - strlcpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN); > - strlcpy(module_name, mod->name, MODULE_NAME_LEN); > - *exported = is_exported(name, *value, mod); > - preempt_enable(); > - return 0; > - } > - symnum -= kallsyms->num_symtab; > - } > - preempt_enable(); > - return -ERANGE; > -} > - > -/* Given a module and name of symbol, find and return the symbol's value */ > -static unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) > -{ > - unsigned int i; > - struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); > - > - for (i = 0; i < kallsyms->num_symtab; i++) { > - const Elf_Sym *sym = &kallsyms->symtab[i]; > - > - if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 && > - sym->st_shndx != SHN_UNDEF) > - return kallsyms_symbol_value(sym); > - } > - return 0; > -} > - > -/* Look for this name: can be of form module:name. */ > -unsigned long module_kallsyms_lookup_name(const char *name) > -{ > - struct module *mod; > - char *colon; > - unsigned long ret = 0; > - > - /* Don't lock: we're in enough trouble already. */ > - preempt_disable(); > - if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { > - if ((mod = find_module_all(name, colon - name, false)) != NULL) > - ret = find_kallsyms_symbol_value(mod, colon+1); > - } else { > - list_for_each_entry_rcu(mod, &modules, list) { > - if (mod->state == MODULE_STATE_UNFORMED) > - continue; > - if ((ret = find_kallsyms_symbol_value(mod, name)) != 0) > - break; > - } > - } > - preempt_enable(); > - return ret; > -} > - > -#ifdef CONFIG_LIVEPATCH > -int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, > - struct module *, unsigned long), > - void *data) > -{ > - struct module *mod; > - unsigned int i; > - int ret = 0; > - > - mutex_lock(&module_mutex); > - list_for_each_entry(mod, &modules, list) { > - /* We hold module_mutex: no need for rcu_dereference_sched */ > - struct mod_kallsyms *kallsyms = mod->kallsyms; > - > - if (mod->state == MODULE_STATE_UNFORMED) > - continue; > - for (i = 0; i < kallsyms->num_symtab; i++) { > - const Elf_Sym *sym = &kallsyms->symtab[i]; > - > - if (sym->st_shndx == SHN_UNDEF) > - continue; > - > - ret = fn(data, kallsyms_symbol_name(kallsyms, i), > - mod, kallsyms_symbol_value(sym)); > - if (ret != 0) > - goto out; > - > - cond_resched(); > - } > - } > -out: > - mutex_unlock(&module_mutex); > - return ret; > -} > -#endif /* CONFIG_LIVEPATCH */ > -#endif /* CONFIG_KALLSYMS */ > - > static void cfi_init(struct module *mod) > { > #ifdef CONFIG_CFI_CLANG
On Tue 2022-02-22 14:12:59, Aaron Tomlin wrote: > No functional change. The patch adds rcu_dereference_sched() into several locations. It triggers lockdep warnings, see below. It is good example why avoid any hidden changes when shuffling code. The changes in the code should be done in a preparatory patch or not at all. This patch is even worse because these changes were not mentioned in the commit message. It should describe what is done and why. I wonder how many other changes are hidden in this patchset and if anyone really checked them. > This patch migrates kallsyms code out of core module > code kernel/module/kallsyms.c > diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c > new file mode 100644 > index 000000000000..b6d49bb5afed > --- /dev/null > +++ b/kernel/module/kallsyms.c [...] > +/* > + * We use the full symtab and strtab which layout_symtab arranged to > + * be appended to the init section. Later we switch to the cut-down > + * core-only ones. > + */ > +void add_kallsyms(struct module *mod, const struct load_info *info) > +{ > + unsigned int i, ndst; > + const Elf_Sym *src; > + Elf_Sym *dst; > + char *s; > + Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; > + > + /* Set up to point into init section. */ > + mod->kallsyms = (void __rcu *)mod->init_layout.base + > + info->mod_kallsyms_init_off; > + > + /* The following is safe since this pointer cannot change */ > + rcu_dereference_sched(mod->kallsyms)->symtab = (void *)symsec->sh_addr; I have got the following warning in livepatch self-test: [ 372.740779] ===== TEST: basic function patching ===== [ 372.760921] % modprobe test_klp_livepatch [ 372.766361] test_klp_livepatch: tainting kernel with TAINT_LIVEPATCH [ 372.767319] test_klp_livepatch: module verification failed: signature and/or required key missing - tainting kernel [ 372.769132] ============================= [ 372.769771] WARNING: suspicious RCU usage [ 372.770392] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.770396] ----------------------------- [ 372.770397] kernel/module/kallsyms.c:178 suspicious rcu_dereference_check() usage! [ 372.770400] other info that might help us debug this: [ 372.770401] rcu_scheduler_active = 2, debug_locks = 1 [ 372.770403] no locks held by modprobe/1760. [ 372.770405] stack backtrace: [ 372.770409] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.770412] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.770413] Call Trace: [ 372.770415] <TASK> [ 372.770417] dump_stack_lvl+0x58/0x71 [ 372.770424] add_kallsyms+0x477/0x5c0 [ 372.770434] load_module+0x107c/0x19c0 [ 372.770446] ? kernel_read_file+0x2a3/0x2d0 [ 372.782403] ? __do_sys_finit_module+0xaf/0x120 [ 372.783019] __do_sys_finit_module+0xaf/0x120 [ 372.783038] do_syscall_64+0x37/0x80 [ 372.783042] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.783045] RIP: 0033:0x7f13f53992a9 [ 372.783048] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.783050] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.783052] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.783054] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.783055] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.783056] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.783057] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.783070] </TASK> > + rcu_dereference_sched(mod->kallsyms)->num_symtab = symsec->sh_size / sizeof(Elf_Sym); [ 372.793150] ============================= [ 372.793151] WARNING: suspicious RCU usage [ 372.793153] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.793155] ----------------------------- [ 372.793156] kernel/module/kallsyms.c:179 suspicious rcu_dereference_check() usage! [ 372.793158] other info that might help us debug this: [ 372.797266] rcu_scheduler_active = 2, debug_locks = 1 [ 372.797268] no locks held by modprobe/1760. [ 372.797270] stack backtrace: [ 372.797271] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.797274] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.797275] Call Trace: [ 372.797277] <TASK> [ 372.797278] dump_stack_lvl+0x58/0x71 [ 372.802579] add_kallsyms+0x56f/0x5c0 [ 372.802605] load_module+0x107c/0x19c0 [ 372.803525] ? kernel_read_file+0x2a3/0x2d0 [ 372.803538] ? __do_sys_finit_module+0xaf/0x120 [ 372.803540] __do_sys_finit_module+0xaf/0x120 [ 372.803555] do_syscall_64+0x37/0x80 [ 372.803558] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.803561] RIP: 0033:0x7f13f53992a9 [ 372.803563] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.803565] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.803567] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.803568] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.811447] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.811465] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.811467] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.811479] </TASK> > + /* Make sure we get permanent strtab: don't use info->strtab. */ > + rcu_dereference_sched(mod->kallsyms)->strtab = > + (void *)info->sechdrs[info->index.str].sh_addr; [ 372.814541] ============================= [ 372.815091] WARNING: suspicious RCU usage [ 372.815093] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.815094] ----------------------------- [ 372.815095] kernel/module/kallsyms.c:181 suspicious rcu_dereference_check() usage! [ 372.815096] other info that might help us debug this: [ 372.815097] rcu_scheduler_active = 2, debug_locks = 1 [ 372.815099] no locks held by modprobe/1760. [ 372.815100] stack backtrace: [ 372.815101] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.815102] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.815103] Call Trace: [ 372.815105] <TASK> [ 372.815106] dump_stack_lvl+0x58/0x71 [ 372.815111] add_kallsyms+0x531/0x5c0 [ 372.815119] load_module+0x107c/0x19c0 [ 372.815129] ? kernel_read_file+0x2a3/0x2d0 [ 372.815140] ? __do_sys_finit_module+0xaf/0x120 [ 372.815143] __do_sys_finit_module+0xaf/0x120 [ 372.815157] do_syscall_64+0x37/0x80 [ 372.815160] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.828879] RIP: 0033:0x7f13f53992a9 [ 372.828885] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.828889] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.828892] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.828893] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.828894] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.828895] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.836097] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.836115] </TASK> > + rcu_dereference_sched(mod->kallsyms)->typetab = > + mod->init_layout.base + info->init_typeoffs; [ 372.837224] ============================= [ 372.837224] WARNING: suspicious RCU usage [ 372.837225] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.837227] ----------------------------- [ 372.837227] kernel/module/kallsyms.c:183 suspicious rcu_dereference_check() usage! [ 372.837229] other info that might help us debug this: [ 372.837230] rcu_scheduler_active = 2, debug_locks = 1 [ 372.837231] no locks held by modprobe/1760. [ 372.837232] stack backtrace: [ 372.837233] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.837235] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.837236] Call Trace: [ 372.837237] <TASK> [ 372.837239] dump_stack_lvl+0x58/0x71 [ 372.837243] add_kallsyms+0x4f3/0x5c0 [ 372.837251] load_module+0x107c/0x19c0 [ 372.849013] ? kernel_read_file+0x2a3/0x2d0 [ 372.849026] ? __do_sys_finit_module+0xaf/0x120 [ 372.849930] __do_sys_finit_module+0xaf/0x120 [ 372.849946] do_syscall_64+0x37/0x80 [ 372.850772] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.850775] RIP: 0033:0x7f13f53992a9 [ 372.850778] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.850780] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.854028] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.854030] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.854031] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.854033] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.854034] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.854048] </TASK> > + > + /* > + * Now populate the cut down core kallsyms for after init > + * and set types up while we still have access to sections. > + */ > + mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; > + mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; > + mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs; > + src = rcu_dereference_sched(mod->kallsyms)->symtab; [ 372.854081] ============================= [ 372.854083] WARNING: suspicious RCU usage [ 372.854084] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.854087] ----------------------------- [ 372.854089] kernel/module/kallsyms.c:193 suspicious rcu_dereference_check() usage! [ 372.854091] other info that might help us debug this: [ 372.854093] rcu_scheduler_active = 2, debug_locks = 1 [ 372.854095] no locks held by modprobe/1760. [ 372.854097] stack backtrace: [ 372.854099] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.854102] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.854104] Call Trace: [ 372.854106] <TASK> [ 372.854109] dump_stack_lvl+0x58/0x71 [ 372.854126] add_kallsyms+0x4b5/0x5c0 [ 372.854139] load_module+0x107c/0x19c0 [ 372.866967] ? kernel_read_file+0x2a3/0x2d0 [ 372.866980] ? __do_sys_finit_module+0xaf/0x120 [ 372.867921] __do_sys_finit_module+0xaf/0x120 [ 372.867937] do_syscall_64+0x37/0x80 [ 372.868823] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.868826] RIP: 0033:0x7f13f53992a9 [ 372.868828] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.868830] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.871419] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.871420] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.871422] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.871423] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.871424] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.871438] </TASK> > + for (ndst = i = 0; i < rcu_dereference_sched(mod->kallsyms)->num_symtab; i++) { [ 372.871464] ============================= [ 372.871466] WARNING: suspicious RCU usage [ 372.871467] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.871470] ----------------------------- [ 372.871471] kernel/module/kallsyms.c:194 suspicious rcu_dereference_check() usage! [ 372.878748] other info that might help us debug this: [ 372.878749] rcu_scheduler_active = 2, debug_locks = 1 [ 372.878751] no locks held by modprobe/1760. [ 372.878752] stack backtrace: [ 372.878753] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.878756] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.878757] Call Trace: [ 372.878758] <TASK> [ 372.878760] dump_stack_lvl+0x58/0x71 [ 372.878765] add_kallsyms+0x296/0x5c0 [ 372.878774] load_module+0x107c/0x19c0 [ 372.878785] ? kernel_read_file+0x2a3/0x2d0 [ 372.878797] ? __do_sys_finit_module+0xaf/0x120 [ 372.878800] __do_sys_finit_module+0xaf/0x120 [ 372.878815] do_syscall_64+0x37/0x80 [ 372.886420] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.886423] RIP: 0033:0x7f13f53992a9 [ 372.886425] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.886427] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.886429] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.886431] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.886432] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.886433] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.886435] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.886448] </TASK> > + rcu_dereference_sched(mod->kallsyms)->typetab[i] = elf_type(src + i, info); [ 372.886474] ============================= [ 372.886476] WARNING: suspicious RCU usage [ 372.886477] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.886480] ----------------------------- [ 372.886481] kernel/module/kallsyms.c:195 suspicious rcu_dereference_check() usage! [ 372.886484] other info that might help us debug this: [ 372.886485] rcu_scheduler_active = 2, debug_locks = 1 [ 372.886487] no locks held by modprobe/1760. [ 372.886489] stack backtrace: [ 372.886491] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.886494] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.900968] Call Trace: [ 372.900970] <TASK> [ 372.900972] dump_stack_lvl+0x58/0x71 [ 372.900977] add_kallsyms+0x3c1/0x5c0 [ 372.900986] load_module+0x107c/0x19c0 [ 372.900997] ? kernel_read_file+0x2a3/0x2d0 [ 372.901009] ? __do_sys_finit_module+0xaf/0x120 [ 372.901012] __do_sys_finit_module+0xaf/0x120 [ 372.901027] do_syscall_64+0x37/0x80 [ 372.904379] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.904382] RIP: 0033:0x7f13f53992a9 [ 372.904384] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.904386] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.904389] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.904390] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.904391] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.904392] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.904394] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.904407] </TASK> > + if (i == 0 || is_livepatch_module(mod) || > + is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, > + info->index.pcpu)) { > + mod->core_kallsyms.typetab[ndst] = > + rcu_dereference_sched(mod->kallsyms)->typetab[i]; [ 372.904436] ============================= [ 372.904438] WARNING: suspicious RCU usage [ 372.904440] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.904442] ----------------------------- [ 372.904444] kernel/module/kallsyms.c:200 suspicious rcu_dereference_check() usage! [ 372.904446] other info that might help us debug this: [ 372.904448] rcu_scheduler_active = 2, debug_locks = 1 [ 372.904450] no locks held by modprobe/1760. [ 372.904452] stack backtrace: [ 372.904454] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.904457] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.904459] Call Trace: [ 372.904461] <TASK> [ 372.904464] dump_stack_lvl+0x58/0x71 [ 372.904470] add_kallsyms+0x439/0x5c0 [ 372.904485] load_module+0x107c/0x19c0 [ 372.904504] ? kernel_read_file+0x2a3/0x2d0 [ 372.921165] ? __do_sys_finit_module+0xaf/0x120 [ 372.921171] __do_sys_finit_module+0xaf/0x120 [ 372.921187] do_syscall_64+0x37/0x80 [ 372.922455] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.922458] RIP: 0033:0x7f13f53992a9 [ 372.922461] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.922463] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.922466] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.922467] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.922469] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.922470] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.922472] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.922485] </TASK> > + dst[ndst] = src[i]; > + dst[ndst++].st_name = s - mod->core_kallsyms.strtab; > + s += strscpy(s, > + &rcu_dereference_sched(mod->kallsyms)->strtab[src[i].st_name], [ 372.929324] ============================= [ 372.929325] WARNING: suspicious RCU usage [ 372.929327] 5.17.0-rc5-default+ #335 Tainted: G E K [ 372.929330] ----------------------------- [ 372.929331] kernel/module/kallsyms.c:204 suspicious rcu_dereference_check() usage! [ 372.929334] other info that might help us debug this: [ 372.929335] rcu_scheduler_active = 2, debug_locks = 1 [ 372.929338] no locks held by modprobe/1760. [ 372.929340] stack backtrace: [ 372.929342] CPU: 3 PID: 1760 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 372.929345] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 372.929347] Call Trace: [ 372.929349] <TASK> [ 372.929352] dump_stack_lvl+0x58/0x71 [ 372.929360] add_kallsyms+0x3fb/0x5c0 [ 372.929374] load_module+0x107c/0x19c0 [ 372.929392] ? kernel_read_file+0x2a3/0x2d0 [ 372.939163] ? __do_sys_finit_module+0xaf/0x120 [ 372.939167] __do_sys_finit_module+0xaf/0x120 [ 372.939182] do_syscall_64+0x37/0x80 [ 372.939186] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 372.939188] RIP: 0033:0x7f13f53992a9 [ 372.939190] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 372.939192] RSP: 002b:00007ffca746bf08 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 372.939195] RAX: ffffffffffffffda RBX: 000055bc9b8b8880 RCX: 00007f13f53992a9 [ 372.939196] RDX: 0000000000000000 RSI: 000055bc99c31688 RDI: 0000000000000005 [ 372.939197] RBP: 000055bc99c31688 R08: 0000000000000000 R09: 000055bc9b8b8410 [ 372.939199] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 372.939200] R13: 000055bc9b8b87a0 R14: 0000000000000000 R15: 000055bc9b8b8880 [ 372.939213] </TASK> > + KSYM_NAME_LEN) + 1; > + } > + } > + mod->core_kallsyms.num_symtab = ndst; > +} [...] > +#ifdef CONFIG_LIVEPATCH > +int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, > + struct module *, unsigned long), > + void *data) > +{ > + struct module *mod; > + unsigned int i; > + int ret = 0; > + > + mutex_lock(&module_mutex); > + list_for_each_entry(mod, &modules, list) { > + /* Still use rcu_dereference_sched to remain compliant with sparse */ > + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); I got the following warning when running livepatch selftest: [ 403.430393] ===== TEST: multiple target modules ===== [ 403.452359] % modprobe test_klp_callbacks_busy block_transition=N [ 403.458735] test_klp_callbacks_busy: test_klp_callbacks_busy_init [ 403.459544] test_klp_callbacks_busy: busymod_work_func enter [ 403.460274] test_klp_callbacks_busy: busymod_work_func exit [ 403.476999] % modprobe test_klp_callbacks_demo [ 403.483742] ============================= [ 403.484446] WARNING: suspicious RCU usage [ 403.485158] 5.17.0-rc5-default+ #335 Tainted: G E K [ 403.486490] ----------------------------- [ 403.486496] kernel/module/kallsyms.c:486 suspicious rcu_dereference_check() usage! [ 403.486499] other info that might help us debug this: [ 403.486500] rcu_scheduler_active = 2, debug_locks = 1 [ 403.486502] 2 locks held by modprobe/2479: [ 403.486504] #0: ffffffff94c4f770 (klp_mutex){+.+.}-{3:3}, at: klp_enable_patch.part.12+0x24/0x910 [ 403.486517] #1: ffffffff94c50a50 (module_mutex){+.+.}-{3:3}, at: module_kallsyms_on_each_symbol+0x27/0x110 [ 403.486527] stack backtrace: [ 403.486529] CPU: 3 PID: 2479 Comm: modprobe Tainted: G E K 5.17.0-rc5-default+ #335 [ 403.486532] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.12.0-59-gc9ba527-rebuilt.opensuse.org 04/01/2014 [ 403.486535] Call Trace: [ 403.486536] <TASK> [ 403.486539] dump_stack_lvl+0x58/0x71 [ 403.486546] module_kallsyms_on_each_symbol+0x101/0x110 [ 403.486549] ? kobject_add_internal+0x1ca/0x2c0 [ 403.501245] klp_find_object_symbol+0x5f/0x110 [ 403.501255] klp_init_object_loaded+0xca/0x140 [ 403.501261] klp_enable_patch.part.12+0x5b6/0x910 [ 403.501266] ? pre_patch_callback+0x20/0x20 [test_klp_callbacks_demo] [ 403.501271] ? pre_patch_callback+0x20/0x20 [test_klp_callbacks_demo] [ 403.501276] do_one_initcall+0x58/0x300 [ 403.501286] do_init_module+0x4b/0x1f1 [ 403.501291] load_module+0x1862/0x19c0 [ 403.506243] ? __do_sys_finit_module+0xaf/0x120 [ 403.506247] __do_sys_finit_module+0xaf/0x120 [ 403.506261] do_syscall_64+0x37/0x80 [ 403.506264] entry_SYSCALL_64_after_hwframe+0x44/0xae [ 403.506267] RIP: 0033:0x7f8e5f5f12a9 [ 403.506270] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d bf 0b 2c 00 f7 d8 64 89 01 48 [ 403.510723] RSP: 002b:00007ffc725cfe48 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 [ 403.510727] RAX: ffffffffffffffda RBX: 000055ddd32938d0 RCX: 00007f8e5f5f12a9 [ 403.510729] RDX: 0000000000000000 RSI: 000055ddd2231688 RDI: 0000000000000005 [ 403.510731] RBP: 000055ddd2231688 R08: 0000000000000000 R09: 000055ddd3293410 [ 403.510733] R10: 0000000000000005 R11: 0000000000000246 R12: 0000000000040000 [ 403.510734] R13: 000055ddd32937a0 R14: 0000000000000000 R15: 000055ddd32938d0 [ 403.510750] </TASK> > + > + if (mod->state == MODULE_STATE_UNFORMED) > + continue; > + for (i = 0; i < kallsyms->num_symtab; i++) { > + const Elf_Sym *sym = &kallsyms->symtab[i]; > + > + if (sym->st_shndx == SHN_UNDEF) > + continue; > + > + ret = fn(data, kallsyms_symbol_name(kallsyms, i), > + mod, kallsyms_symbol_value(sym)); > + if (ret != 0) > + goto out; > + } > + } > +out: > + mutex_unlock(&module_mutex); > + return ret; > +}
Le 25/02/2022 à 10:15, Petr Mladek a écrit : > On Tue 2022-02-22 14:12:59, Aaron Tomlin wrote: >> No functional change. > > The patch adds rcu_dereference_sched() into several locations. > It triggers lockdep warnings, see below. > > It is good example why avoid any hidden changes when shuffling > code. The changes in the code should be done in a preparatory > patch or not at all. > > This patch is even worse because these changes were not > mentioned in the commit message. It should describe what > is done and why. > > I wonder how many other changes are hidden in this patchset > and if anyone really checked them. That's probably my fault, when I reviewed version v5 of the series I mentionned all checkpatch and sparse reports asking Aaron to make his series exempt of such warnings. Most warnings where related to style (parenthesis alignment, blank lines, spaces, etc ...) or erroneous casting etc.... But for that particular patch we had: kernel/module/kallsyms.c:174:23: warning: incorrect type in assignment (different address spaces) kernel/module/kallsyms.c:174:23: expected struct mod_kallsyms [noderef] __rcu *kallsyms kernel/module/kallsyms.c:174:23: got void * kernel/module/kallsyms.c:176:12: warning: dereference of noderef expression kernel/module/kallsyms.c:177:12: warning: dereference of noderef expression kernel/module/kallsyms.c:179:12: warning: dereference of noderef expression kernel/module/kallsyms.c:180:12: warning: dereference of noderef expression kernel/module/kallsyms.c:189:18: warning: dereference of noderef expression kernel/module/kallsyms.c:190:35: warning: dereference of noderef expression kernel/module/kallsyms.c:191:20: warning: dereference of noderef expression kernel/module/kallsyms.c:196:32: warning: dereference of noderef expression kernel/module/kallsyms.c:199:45: warning: dereference of noderef expression Aaron used rcu_dereference_sched() in order to fix that. How should this be fixed if using rcu_dereference_sched() is not correct ? Thanks Christophe
On Fri 2022-02-25 09:27:33, Christophe Leroy wrote: > > > Le 25/02/2022 à 10:15, Petr Mladek a écrit : > > On Tue 2022-02-22 14:12:59, Aaron Tomlin wrote: > >> No functional change. > > > > The patch adds rcu_dereference_sched() into several locations. > > It triggers lockdep warnings, see below. > > > > It is good example why avoid any hidden changes when shuffling > > code. The changes in the code should be done in a preparatory > > patch or not at all. > > > > This patch is even worse because these changes were not > > mentioned in the commit message. It should describe what > > is done and why. > > > > I wonder how many other changes are hidden in this patchset > > and if anyone really checked them. > > That's probably my fault, when I reviewed version v5 of the series I > mentionned all checkpatch and sparse reports asking Aaron to make his > series exempt of such warnings. Most warnings where related to style > (parenthesis alignment, blank lines, spaces, etc ...) or erroneous > casting etc.... > > But for that particular patch we had: > > kernel/module/kallsyms.c:174:23: warning: incorrect type in assignment > (different address spaces) > kernel/module/kallsyms.c:174:23: expected struct mod_kallsyms > [noderef] __rcu *kallsyms > kernel/module/kallsyms.c:174:23: got void * > kernel/module/kallsyms.c:176:12: warning: dereference of noderef expression > kernel/module/kallsyms.c:177:12: warning: dereference of noderef expression > kernel/module/kallsyms.c:179:12: warning: dereference of noderef expression > kernel/module/kallsyms.c:180:12: warning: dereference of noderef expression > kernel/module/kallsyms.c:189:18: warning: dereference of noderef expression > kernel/module/kallsyms.c:190:35: warning: dereference of noderef expression > kernel/module/kallsyms.c:191:20: warning: dereference of noderef expression > kernel/module/kallsyms.c:196:32: warning: dereference of noderef expression > kernel/module/kallsyms.c:199:45: warning: dereference of noderef expression > > Aaron used rcu_dereference_sched() in order to fix that. > > How should this be fixed if using rcu_dereference_sched() is not correct ? IMHO, sparse complains that _rcu pointer is not accessed using RCU API. rcu_dereference_sched() makes sparse happy. But lockdep complains because the _rcu pointer is not accessed under: rcu_read_lock_sched(); rcu_read_unlock_sched(); This is not the case here. Note that module_mutex does not disable preemtion. Now, the code is safe. The RCU access makes sure that "mod" can't be freed in the meantime: + add_kallsyms() is called by the module loaded when the module is being loaded. It could not get removed in parallel by definition. + module_kallsyms_on_each_symbol() takes module_mutex. It means that the module could not get removed. IMHO, we have two possibilities here: + Make sparse and lockdep happy by using rcu_dereference_sched() and calling the code under rcu_read_lock_sched(). + Cast (struct mod_kallsyms *)mod->kallsyms when accessing the value. I do not have strong preference. I am fine with both. Anyway, such a fix should be done in a separate patch! Best Regards, Petr
On Fri 2022-02-25 11:15 +0100, Petr Mladek wrote: > rcu_dereference_sched() makes sparse happy. But lockdep complains > because the _rcu pointer is not accessed under: > > rcu_read_lock_sched(); > rcu_read_unlock_sched(); Hi Petr, > > This is not the case here. Note that module_mutex does not > disable preemtion. > > Now, the code is safe. The RCU access makes sure that "mod" > can't be freed in the meantime: > > + add_kallsyms() is called by the module loaded when the module > is being loaded. It could not get removed in parallel > by definition. > > + module_kallsyms_on_each_symbol() takes module_mutex. > It means that the module could not get removed. Indeed, which is why I did not use rcu_read_lock_sched() and rcu_read_unlock_sched() with rcu_dereference_sched(). That being said, I should have mentioned this in the commit message. > IMHO, we have two possibilities here: > > + Make sparse and lockdep happy by using rcu_dereference_sched() > and calling the code under rcu_read_lock_sched(). > > + Cast (struct mod_kallsyms *)mod->kallsyms when accessing > the value. I prefer the first option. > I do not have strong preference. I am fine with both. > > Anyway, such a fix should be done in a separate patch! Agreed. Kind regards,
On Fri 2022-02-25 10:27 +0000, Aaron Tomlin wrote: > On Fri 2022-02-25 11:15 +0100, Petr Mladek wrote: > > rcu_dereference_sched() makes sparse happy. But lockdep complains > > because the _rcu pointer is not accessed under: > > > > rcu_read_lock_sched(); > > rcu_read_unlock_sched(); > > Hi Petr, > > > > > This is not the case here. Note that module_mutex does not > > disable preemtion. > > > > Now, the code is safe. The RCU access makes sure that "mod" > > can't be freed in the meantime: > > > > + add_kallsyms() is called by the module loaded when the module > > is being loaded. It could not get removed in parallel > > by definition. > > > > + module_kallsyms_on_each_symbol() takes module_mutex. > > It means that the module could not get removed. > > Indeed, which is why I did not use rcu_read_lock_sched() and > rcu_read_unlock_sched() with rcu_dereference_sched(). That being said, I > should have mentioned this in the commit message. > > > IMHO, we have two possibilities here: > > > > + Make sparse and lockdep happy by using rcu_dereference_sched() > > and calling the code under rcu_read_lock_sched(). > > > > + Cast (struct mod_kallsyms *)mod->kallsyms when accessing > > the value. > > I prefer the first option. > > > I do not have strong preference. I am fine with both. > > > > Anyway, such a fix should be done in a separate patch! > > Agreed. Luis, If I understand correctly, it might be cleaner to resolve the above in two separate patches for a v9 i.e. a) address the sparse and lockdep feedback and b) refactor the code, before the latest version [1] is merged into module-next. I assume the previous iteration will be reverted first? Please let me know your thoughts [1]: https://lore.kernel.org/all/20220222141303.1392190-1-atomlin@redhat.com/ Kind regards,
Le 25/02/2022 à 13:21, Aaron Tomlin a écrit : > On Fri 2022-02-25 10:27 +0000, Aaron Tomlin wrote: >> On Fri 2022-02-25 11:15 +0100, Petr Mladek wrote: >>> rcu_dereference_sched() makes sparse happy. But lockdep complains >>> because the _rcu pointer is not accessed under: >>> >>> rcu_read_lock_sched(); >>> rcu_read_unlock_sched(); >> >> Hi Petr, >> >>> >>> This is not the case here. Note that module_mutex does not >>> disable preemtion. >>> >>> Now, the code is safe. The RCU access makes sure that "mod" >>> can't be freed in the meantime: >>> >>> + add_kallsyms() is called by the module loaded when the module >>> is being loaded. It could not get removed in parallel >>> by definition. >>> >>> + module_kallsyms_on_each_symbol() takes module_mutex. >>> It means that the module could not get removed. >> >> Indeed, which is why I did not use rcu_read_lock_sched() and >> rcu_read_unlock_sched() with rcu_dereference_sched(). That being said, I >> should have mentioned this in the commit message. >> >>> IMHO, we have two possibilities here: >>> >>> + Make sparse and lockdep happy by using rcu_dereference_sched() >>> and calling the code under rcu_read_lock_sched(). >>> >>> + Cast (struct mod_kallsyms *)mod->kallsyms when accessing >>> the value. >> >> I prefer the first option. >> >>> I do not have strong preference. I am fine with both. >>> >>> Anyway, such a fix should be done in a separate patch! >> >> Agreed. > > Luis, > > If I understand correctly, it might be cleaner to resolve the above in two > separate patches for a v9 i.e. a) address the sparse and lockdep feedback > and b) refactor the code, before the latest version [1] is merged into > module-next. I assume the previous iteration will be reverted first? > > Please let me know your thoughts > > [1]: https://lore.kernel.org/all/20220222141303.1392190-1-atomlin@redhat.com/ > I would do it the other way: first move the code into a separate file, and then handle the sparse __rcu feedback as a followup patch to the series. Regarding module-next, AFAICS at the moment we still have only the 10 first patches of v6 in the tree. I guess the way forward will be to rebase module-next and drop those patches and commit v9 instead. Christophe
On Fri, Feb 25, 2022 at 12:57:34PM +0000, Christophe Leroy wrote: > > > Le 25/02/2022 à 13:21, Aaron Tomlin a écrit : > > On Fri 2022-02-25 10:27 +0000, Aaron Tomlin wrote: > >> On Fri 2022-02-25 11:15 +0100, Petr Mladek wrote: > >>> rcu_dereference_sched() makes sparse happy. But lockdep complains > >>> because the _rcu pointer is not accessed under: > >>> > >>> rcu_read_lock_sched(); > >>> rcu_read_unlock_sched(); > >> > >> Hi Petr, > >> > >>> > >>> This is not the case here. Note that module_mutex does not > >>> disable preemtion. > >>> > >>> Now, the code is safe. The RCU access makes sure that "mod" > >>> can't be freed in the meantime: > >>> > >>> + add_kallsyms() is called by the module loaded when the module > >>> is being loaded. It could not get removed in parallel > >>> by definition. > >>> > >>> + module_kallsyms_on_each_symbol() takes module_mutex. > >>> It means that the module could not get removed. > >> > >> Indeed, which is why I did not use rcu_read_lock_sched() and > >> rcu_read_unlock_sched() with rcu_dereference_sched(). That being said, I > >> should have mentioned this in the commit message. > >> > >>> IMHO, we have two possibilities here: > >>> > >>> + Make sparse and lockdep happy by using rcu_dereference_sched() > >>> and calling the code under rcu_read_lock_sched(). > >>> > >>> + Cast (struct mod_kallsyms *)mod->kallsyms when accessing > >>> the value. > >> > >> I prefer the first option. > >> > >>> I do not have strong preference. I am fine with both. > >>> > >>> Anyway, such a fix should be done in a separate patch! > >> > >> Agreed. > > > > Luis, > > > > If I understand correctly, it might be cleaner to resolve the above in two > > separate patches for a v9 i.e. a) address the sparse and lockdep feedback > > and b) refactor the code, before the latest version [1] is merged into > > module-next. I assume the previous iteration will be reverted first? > > > > Please let me know your thoughts > > > > [1]: https://lore.kernel.org/all/20220222141303.1392190-1-atomlin@redhat.com/ > > > > I would do it the other way: first move the code into a separate file, > and then handle the sparse __rcu feedback as a followup patch to the series. I want to avoid any regressions and new complaints, fixes should be submitted before so that if they are applicable to stable / etc they can be sent there. > Regarding module-next, AFAICS at the moment we still have only the 10 > first patches of v6 in the tree. I guess the way forward will be to > rebase module-next and drop those patches and commit v9 instead. Right, I'll just git fetch and reset to Linus' latest tree, so I'll drop all of the stuff there now. And then the hope is to apply your new fresh new clean v9. Thanks for chugging on with this series! Luis
Le 26/02/2022 à 21:27, Luis Chamberlain a écrit : > On Fri, Feb 25, 2022 at 12:57:34PM +0000, Christophe Leroy wrote: >> >> >> Le 25/02/2022 à 13:21, Aaron Tomlin a écrit : >>> On Fri 2022-02-25 10:27 +0000, Aaron Tomlin wrote: >>>> On Fri 2022-02-25 11:15 +0100, Petr Mladek wrote: >>>>> rcu_dereference_sched() makes sparse happy. But lockdep complains >>>>> because the _rcu pointer is not accessed under: >>>>> >>>>> rcu_read_lock_sched(); >>>>> rcu_read_unlock_sched(); >>>> >>>> Hi Petr, >>>> >>>>> >>>>> This is not the case here. Note that module_mutex does not >>>>> disable preemtion. >>>>> >>>>> Now, the code is safe. The RCU access makes sure that "mod" >>>>> can't be freed in the meantime: >>>>> >>>>> + add_kallsyms() is called by the module loaded when the module >>>>> is being loaded. It could not get removed in parallel >>>>> by definition. >>>>> >>>>> + module_kallsyms_on_each_symbol() takes module_mutex. >>>>> It means that the module could not get removed. >>>> >>>> Indeed, which is why I did not use rcu_read_lock_sched() and >>>> rcu_read_unlock_sched() with rcu_dereference_sched(). That being said, I >>>> should have mentioned this in the commit message. >>>> >>>>> IMHO, we have two possibilities here: >>>>> >>>>> + Make sparse and lockdep happy by using rcu_dereference_sched() >>>>> and calling the code under rcu_read_lock_sched(). >>>>> >>>>> + Cast (struct mod_kallsyms *)mod->kallsyms when accessing >>>>> the value. >>>> >>>> I prefer the first option. >>>> >>>>> I do not have strong preference. I am fine with both. >>>>> >>>>> Anyway, such a fix should be done in a separate patch! >>>> >>>> Agreed. >>> >>> Luis, >>> >>> If I understand correctly, it might be cleaner to resolve the above in two >>> separate patches for a v9 i.e. a) address the sparse and lockdep feedback >>> and b) refactor the code, before the latest version [1] is merged into >>> module-next. I assume the previous iteration will be reverted first? >>> >>> Please let me know your thoughts >>> >>> [1]: https://lore.kernel.org/all/20220222141303.1392190-1-atomlin@redhat.com/ >>> >> >> I would do it the other way: first move the code into a separate file, >> and then handle the sparse __rcu feedback as a followup patch to the series. > > I want to avoid any regressions and new complaints, fixes should be > submitted before so that if they are applicable to stable / etc they > can be sent there. Fair enough, however here we are talking about sparse warning only, and the discussion around it has shown that this is not a real bug, just a warning that can be either fixed with a proper cast or by adding rcu locks which might not be necessary. So I'm not sure this is a good candidate for -stable. In https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html it is said "It must fix a real bug that bothers people (not a, “This could be a problem…” type thing)" But up to you. > >> Regarding module-next, AFAICS at the moment we still have only the 10 >> first patches of v6 in the tree. I guess the way forward will be to >> rebase module-next and drop those patches and commit v9 instead. > > Right, I'll just git fetch and reset to Linus' latest tree, so I'll drop > all of the stuff there now. And then the hope is to apply your new fresh new > clean v9. > Aaron, do you plan to send v9 anytime soon ? Thanks Christophe
On Mon 2022-02-28 09:02 +0000, Christophe Leroy wrote:
> Aaron, do you plan to send v9 anytime soon ?
Hi Christophe,
Yes, today.
As discussed previously, I will resolve the Sparse warnings, in the context
of Kconfig CONFIG_KALLSYMS, with an appropriate statement in the commit
message, as a preliminary patch to the series. That being said, I believe
it makes sense to include the aforementioned patch within the series.
Any objections?
Kind regards,
Le 28/02/2022 à 10:31, Aaron Tomlin a écrit : > On Mon 2022-02-28 09:02 +0000, Christophe Leroy wrote: >> Aaron, do you plan to send v9 anytime soon ? > > Hi Christophe, > > Yes, today. > > As discussed previously, I will resolve the Sparse warnings, in the context > of Kconfig CONFIG_KALLSYMS, with an appropriate statement in the commit > message, as a preliminary patch to the series. That being said, I believe > it makes sense to include the aforementioned patch within the series. > Any objections? > No objection. Thank you Christophe
diff --git a/kernel/module/Makefile b/kernel/module/Makefile index 12388627725c..9901bed3ab5b 100644 --- a/kernel/module/Makefile +++ b/kernel/module/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o obj-$(CONFIG_STRICT_MODULE_RWX) += strict_rwx.o obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o +obj-$(CONFIG_KALLSYMS) += kallsyms.o diff --git a/kernel/module/internal.h b/kernel/module/internal.h index b0c360839f63..44ca05b9eb8f 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -68,6 +68,19 @@ struct load_info { }; int mod_verify_sig(const void *mod, struct load_info *info); +struct module *find_module_all(const char *name, size_t len, bool even_unformed); +int cmp_name(const void *name, const void *sym); +long module_get_offset(struct module *mod, unsigned int *size, Elf_Shdr *sechdr, + unsigned int section); + +static inline unsigned long kernel_symbol_value(const struct kernel_symbol *sym) +{ +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS + return (unsigned long)offset_to_ptr(&sym->value_offset); +#else + return sym->value; +#endif +} #ifdef CONFIG_LIVEPATCH int copy_module_elf(struct module *mod, struct load_info *info); @@ -174,3 +187,19 @@ void kmemleak_load_module(const struct module *mod, const struct load_info *info static inline void kmemleak_load_module(const struct module *mod, const struct load_info *info) { } #endif /* CONFIG_DEBUG_KMEMLEAK */ + +#ifdef CONFIG_KALLSYMS +void init_build_id(struct module *mod, const struct load_info *info); +void layout_symtab(struct module *mod, struct load_info *info); +void add_kallsyms(struct module *mod, const struct load_info *info); +unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name); + +static inline bool sect_empty(const Elf_Shdr *sect) +{ + return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; +} +#else /* !CONFIG_KALLSYMS */ +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) { } +#endif /* CONFIG_KALLSYMS */ diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c new file mode 100644 index 000000000000..b6d49bb5afed --- /dev/null +++ b/kernel/module/kallsyms.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Module kallsyms support + * + * Copyright (C) 2010 Rusty Russell + */ + +#include <linux/module.h> +#include <linux/kallsyms.h> +#include <linux/buildid.h> +#include <linux/bsearch.h> +#include "internal.h" + +/* Lookup exported symbol in given range of kernel_symbols */ +static const struct kernel_symbol *lookup_exported_symbol(const char *name, + const struct kernel_symbol *start, + const struct kernel_symbol *stop) +{ + return bsearch(name, start, stop - start, + sizeof(struct kernel_symbol), cmp_name); +} + +static int is_exported(const char *name, unsigned long value, + const struct module *mod) +{ + const struct kernel_symbol *ks; + + if (!mod) + ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab); + else + ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms); + + return ks && kernel_symbol_value(ks) == value; +} + +/* As per nm */ +static char elf_type(const Elf_Sym *sym, const struct load_info *info) +{ + const Elf_Shdr *sechdrs = info->sechdrs; + + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { + if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) + return 'v'; + else + return 'w'; + } + if (sym->st_shndx == SHN_UNDEF) + return 'U'; + if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) + return 'a'; + if (sym->st_shndx >= SHN_LORESERVE) + return '?'; + if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) + return 't'; + if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC && + sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { + if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) + return 'r'; + else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) + return 'g'; + else + return 'd'; + } + if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { + if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) + return 's'; + else + return 'b'; + } + if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, + ".debug")) { + return 'n'; + } + return '?'; +} + +static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, + unsigned int shnum, unsigned int pcpundx) +{ + const Elf_Shdr *sec; + + if (src->st_shndx == SHN_UNDEF || + src->st_shndx >= shnum || + !src->st_name) + return false; + +#ifdef CONFIG_KALLSYMS_ALL + if (src->st_shndx == pcpundx) + return true; +#endif + + sec = sechdrs + src->st_shndx; + if (!(sec->sh_flags & SHF_ALLOC) +#ifndef CONFIG_KALLSYMS_ALL + || !(sec->sh_flags & SHF_EXECINSTR) +#endif + || (sec->sh_entsize & INIT_OFFSET_MASK)) + return false; + + return true; +} + +/* + * We only allocate and copy the strings needed by the parts of symtab + * we keep. This is simple, but has the effect of making multiple + * copies of duplicates. We could be more sophisticated, see + * linux-kernel thread starting with + * <73defb5e4bca04a6431392cc341112b1@localhost>. + */ +void layout_symtab(struct module *mod, struct load_info *info) +{ + Elf_Shdr *symsect = info->sechdrs + info->index.sym; + Elf_Shdr *strsect = info->sechdrs + info->index.str; + const Elf_Sym *src; + unsigned int i, nsrc, ndst, strtab_size = 0; + + /* Put symbol section at end of init part of module. */ + symsect->sh_flags |= SHF_ALLOC; + symsect->sh_entsize = module_get_offset(mod, &mod->init_layout.size, symsect, + info->index.sym) | INIT_OFFSET_MASK; + pr_debug("\t%s\n", info->secstrings + symsect->sh_name); + + src = (void *)info->hdr + symsect->sh_offset; + nsrc = symsect->sh_size / sizeof(*src); + + /* Compute total space required for the core symbols' strtab. */ + for (ndst = i = 0; i < nsrc; i++) { + if (i == 0 || is_livepatch_module(mod) || + is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, + info->index.pcpu)) { + strtab_size += strlen(&info->strtab[src[i].st_name]) + 1; + ndst++; + } + } + + /* Append room for core symbols at end of core part. */ + info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); + info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); + mod->core_layout.size += strtab_size; + info->core_typeoffs = mod->core_layout.size; + mod->core_layout.size += ndst * sizeof(char); + mod->core_layout.size = debug_align(mod->core_layout.size); + + /* Put string table section at end of init part of module. */ + strsect->sh_flags |= SHF_ALLOC; + strsect->sh_entsize = module_get_offset(mod, &mod->init_layout.size, strsect, + info->index.str) | INIT_OFFSET_MASK; + pr_debug("\t%s\n", info->secstrings + strsect->sh_name); + + /* We'll tack temporary mod_kallsyms on the end. */ + mod->init_layout.size = ALIGN(mod->init_layout.size, + __alignof__(struct mod_kallsyms)); + info->mod_kallsyms_init_off = mod->init_layout.size; + mod->init_layout.size += sizeof(struct mod_kallsyms); + info->init_typeoffs = mod->init_layout.size; + mod->init_layout.size += nsrc * sizeof(char); + mod->init_layout.size = debug_align(mod->init_layout.size); +} + +/* + * We use the full symtab and strtab which layout_symtab arranged to + * be appended to the init section. Later we switch to the cut-down + * core-only ones. + */ +void add_kallsyms(struct module *mod, const struct load_info *info) +{ + unsigned int i, ndst; + const Elf_Sym *src; + Elf_Sym *dst; + char *s; + Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; + + /* Set up to point into init section. */ + mod->kallsyms = (void __rcu *)mod->init_layout.base + + info->mod_kallsyms_init_off; + + /* The following is safe since this pointer cannot change */ + rcu_dereference_sched(mod->kallsyms)->symtab = (void *)symsec->sh_addr; + rcu_dereference_sched(mod->kallsyms)->num_symtab = symsec->sh_size / sizeof(Elf_Sym); + /* Make sure we get permanent strtab: don't use info->strtab. */ + rcu_dereference_sched(mod->kallsyms)->strtab = + (void *)info->sechdrs[info->index.str].sh_addr; + rcu_dereference_sched(mod->kallsyms)->typetab = + mod->init_layout.base + info->init_typeoffs; + + /* + * Now populate the cut down core kallsyms for after init + * and set types up while we still have access to sections. + */ + mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; + mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; + mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs; + src = rcu_dereference_sched(mod->kallsyms)->symtab; + for (ndst = i = 0; i < rcu_dereference_sched(mod->kallsyms)->num_symtab; i++) { + rcu_dereference_sched(mod->kallsyms)->typetab[i] = elf_type(src + i, info); + if (i == 0 || is_livepatch_module(mod) || + is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, + info->index.pcpu)) { + mod->core_kallsyms.typetab[ndst] = + rcu_dereference_sched(mod->kallsyms)->typetab[i]; + dst[ndst] = src[i]; + dst[ndst++].st_name = s - mod->core_kallsyms.strtab; + s += strscpy(s, + &rcu_dereference_sched(mod->kallsyms)->strtab[src[i].st_name], + KSYM_NAME_LEN) + 1; + } + } + mod->core_kallsyms.num_symtab = ndst; +} + +#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) +void init_build_id(struct module *mod, const struct load_info *info) +{ + const Elf_Shdr *sechdr; + unsigned int i; + + for (i = 0; i < info->hdr->e_shnum; i++) { + sechdr = &info->sechdrs[i]; + if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE && + !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id, + sechdr->sh_size)) + break; + } +} +#else +void init_build_id(struct module *mod, const struct load_info *info) +{ +} +#endif + +/* + * This ignores the intensely annoying "mapping symbols" found + * in ARM ELF files: $a, $t and $d. + */ +static inline int is_arm_mapping_symbol(const char *str) +{ + if (str[0] == '.' && str[1] == 'L') + return true; + return str[0] == '$' && strchr("axtd", str[1]) && + (str[2] == '\0' || str[2] == '.'); +} + +static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) +{ + return kallsyms->strtab + kallsyms->symtab[symnum].st_name; +} + +/* + * Given a module and address, find the corresponding symbol and return its name + * while providing its size and offset if needed. + */ +static const char *find_kallsyms_symbol(struct module *mod, + unsigned long addr, + unsigned long *size, + unsigned long *offset) +{ + unsigned int i, best = 0; + unsigned long nextval, bestval; + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); + + /* At worse, next value is at end of module */ + if (within_module_init(addr, mod)) + nextval = (unsigned long)mod->init_layout.base + mod->init_layout.text_size; + else + nextval = (unsigned long)mod->core_layout.base + mod->core_layout.text_size; + + bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); + + /* + * Scan for closest preceding symbol, and next symbol. (ELF + * starts real symbols at 1). + */ + for (i = 1; i < kallsyms->num_symtab; i++) { + const Elf_Sym *sym = &kallsyms->symtab[i]; + unsigned long thisval = kallsyms_symbol_value(sym); + + if (sym->st_shndx == SHN_UNDEF) + continue; + + /* + * We ignore unnamed symbols: they're uninformative + * and inserted at a whim. + */ + if (*kallsyms_symbol_name(kallsyms, i) == '\0' || + is_arm_mapping_symbol(kallsyms_symbol_name(kallsyms, i))) + continue; + + if (thisval <= addr && thisval > bestval) { + best = i; + bestval = thisval; + } + if (thisval > addr && thisval < nextval) + nextval = thisval; + } + + if (!best) + return NULL; + + if (size) + *size = nextval - bestval; + if (offset) + *offset = addr - bestval; + + return kallsyms_symbol_name(kallsyms, best); +} + +void * __weak dereference_module_function_descriptor(struct module *mod, + void *ptr) +{ + return ptr; +} + +/* + * For kallsyms to ask for address resolution. NULL means not found. Careful + * not to lock to avoid deadlock on oopses, simply disable preemption. + */ +const char *module_address_lookup(unsigned long addr, + unsigned long *size, + unsigned long *offset, + char **modname, + const unsigned char **modbuildid, + char *namebuf) +{ + const char *ret = NULL; + struct module *mod; + + preempt_disable(); + mod = __module_address(addr); + if (mod) { + if (modname) + *modname = mod->name; + if (modbuildid) { +#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) + *modbuildid = mod->build_id; +#else + *modbuildid = NULL; +#endif + } + + ret = find_kallsyms_symbol(mod, addr, size, offset); + } + /* Make a copy in here where it's safe */ + if (ret) { + strncpy(namebuf, ret, KSYM_NAME_LEN - 1); + ret = namebuf; + } + preempt_enable(); + + return ret; +} + +int lookup_module_symbol_name(unsigned long addr, char *symname) +{ + struct module *mod; + + preempt_disable(); + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; + if (within_module(addr, mod)) { + const char *sym; + + sym = find_kallsyms_symbol(mod, addr, NULL, NULL); + if (!sym) + goto out; + + strscpy(symname, sym, KSYM_NAME_LEN); + preempt_enable(); + return 0; + } + } +out: + preempt_enable(); + return -ERANGE; +} + +int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, + unsigned long *offset, char *modname, char *name) +{ + struct module *mod; + + preempt_disable(); + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; + if (within_module(addr, mod)) { + const char *sym; + + sym = find_kallsyms_symbol(mod, addr, size, offset); + if (!sym) + goto out; + if (modname) + strscpy(modname, mod->name, MODULE_NAME_LEN); + if (name) + strscpy(name, sym, KSYM_NAME_LEN); + preempt_enable(); + return 0; + } + } +out: + preempt_enable(); + return -ERANGE; +} + +int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, + char *name, char *module_name, int *exported) +{ + struct module *mod; + + preempt_disable(); + list_for_each_entry_rcu(mod, &modules, list) { + struct mod_kallsyms *kallsyms; + + if (mod->state == MODULE_STATE_UNFORMED) + continue; + kallsyms = rcu_dereference_sched(mod->kallsyms); + if (symnum < kallsyms->num_symtab) { + const Elf_Sym *sym = &kallsyms->symtab[symnum]; + + *value = kallsyms_symbol_value(sym); + *type = kallsyms->typetab[symnum]; + strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN); + strscpy(module_name, mod->name, MODULE_NAME_LEN); + *exported = is_exported(name, *value, mod); + preempt_enable(); + return 0; + } + symnum -= kallsyms->num_symtab; + } + preempt_enable(); + return -ERANGE; +} + +/* Given a module and name of symbol, find and return the symbol's value */ +unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) +{ + unsigned int i; + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); + + for (i = 0; i < kallsyms->num_symtab; i++) { + const Elf_Sym *sym = &kallsyms->symtab[i]; + + if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 && + sym->st_shndx != SHN_UNDEF) + return kallsyms_symbol_value(sym); + } + return 0; +} + +/* Look for this name: can be of form module:name. */ +unsigned long module_kallsyms_lookup_name(const char *name) +{ + struct module *mod; + char *colon; + unsigned long ret = 0; + + /* Don't lock: we're in enough trouble already. */ + preempt_disable(); + if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { + if ((mod = find_module_all(name, colon - name, false)) != NULL) + ret = find_kallsyms_symbol_value(mod, colon + 1); + } else { + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; + if ((ret = find_kallsyms_symbol_value(mod, name)) != 0) + break; + } + } + preempt_enable(); + return ret; +} + +#ifdef CONFIG_LIVEPATCH +int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, + struct module *, unsigned long), + void *data) +{ + struct module *mod; + unsigned int i; + int ret = 0; + + mutex_lock(&module_mutex); + list_for_each_entry(mod, &modules, list) { + /* Still use rcu_dereference_sched to remain compliant with sparse */ + struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); + + if (mod->state == MODULE_STATE_UNFORMED) + continue; + for (i = 0; i < kallsyms->num_symtab; i++) { + const Elf_Sym *sym = &kallsyms->symtab[i]; + + if (sym->st_shndx == SHN_UNDEF) + continue; + + ret = fn(data, kallsyms_symbol_name(kallsyms, i), + mod, kallsyms_symbol_value(sym)); + if (ret != 0) + goto out; + } + } +out: + mutex_unlock(&module_mutex); + return ret; +} +#endif /* CONFIG_LIVEPATCH */ diff --git a/kernel/module/main.c b/kernel/module/main.c index 7dd283959c5c..952079987ea4 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -288,15 +288,6 @@ static bool check_exported_symbol(const struct symsearch *syms, return true; } -static unsigned long kernel_symbol_value(const struct kernel_symbol *sym) -{ -#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS - return (unsigned long)offset_to_ptr(&sym->value_offset); -#else - return sym->value; -#endif -} - static const char *kernel_symbol_name(const struct kernel_symbol *sym) { #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS @@ -317,7 +308,7 @@ static const char *kernel_symbol_namespace(const struct kernel_symbol *sym) #endif } -static int cmp_name(const void *name, const void *sym) +int cmp_name(const void *name, const void *sym) { return strcmp(name, kernel_symbol_name(sym)); } @@ -387,8 +378,8 @@ static bool find_symbol(struct find_symbol_arg *fsa) * Search for module by name: must hold module_mutex (or preempt disabled * for read-only access). */ -static struct module *find_module_all(const char *name, size_t len, - bool even_unformed) +struct module *find_module_all(const char *name, size_t len, + bool even_unformed) { struct module *mod; @@ -1294,13 +1285,6 @@ resolve_symbol_wait(struct module *mod, return ksym; } -#ifdef CONFIG_KALLSYMS -static inline bool sect_empty(const Elf_Shdr *sect) -{ - return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; -} -#endif - /* * /sys/module/foo/sections stuff * J. Corbet <corbet@lwn.net> @@ -2065,7 +2049,7 @@ unsigned int __weak arch_mod_section_prepend(struct module *mod, } /* Update size with this section: return offset. */ -static long get_offset(struct module *mod, unsigned int *size, +long module_get_offset(struct module *mod, unsigned int *size, Elf_Shdr *sechdr, unsigned int section) { long ret; @@ -2121,7 +2105,7 @@ static void layout_sections(struct module *mod, struct load_info *info) || s->sh_entsize != ~0UL || module_init_layout_section(sname)) continue; - s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i); + s->sh_entsize = module_get_offset(mod, &mod->core_layout.size, s, i); pr_debug("\t%s\n", sname); } switch (m) { @@ -2154,7 +2138,7 @@ static void layout_sections(struct module *mod, struct load_info *info) || s->sh_entsize != ~0UL || !module_init_layout_section(sname)) continue; - s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i) + s->sh_entsize = (module_get_offset(mod, &mod->init_layout.size, s, i) | INIT_OFFSET_MASK); pr_debug("\t%s\n", sname); } @@ -2267,228 +2251,6 @@ static void free_modinfo(struct module *mod) } } -#ifdef CONFIG_KALLSYMS - -/* Lookup exported symbol in given range of kernel_symbols */ -static const struct kernel_symbol *lookup_exported_symbol(const char *name, - const struct kernel_symbol *start, - const struct kernel_symbol *stop) -{ - return bsearch(name, start, stop - start, - sizeof(struct kernel_symbol), cmp_name); -} - -static int is_exported(const char *name, unsigned long value, - const struct module *mod) -{ - const struct kernel_symbol *ks; - if (!mod) - ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab); - else - ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms); - - return ks != NULL && kernel_symbol_value(ks) == value; -} - -/* As per nm */ -static char elf_type(const Elf_Sym *sym, const struct load_info *info) -{ - const Elf_Shdr *sechdrs = info->sechdrs; - - if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { - if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) - return 'v'; - else - return 'w'; - } - if (sym->st_shndx == SHN_UNDEF) - return 'U'; - if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) - return 'a'; - if (sym->st_shndx >= SHN_LORESERVE) - return '?'; - if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) - return 't'; - if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC - && sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { - if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) - return 'r'; - else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) - return 'g'; - else - return 'd'; - } - if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { - if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) - return 's'; - else - return 'b'; - } - if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, - ".debug")) { - return 'n'; - } - return '?'; -} - -static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, - unsigned int shnum, unsigned int pcpundx) -{ - const Elf_Shdr *sec; - - if (src->st_shndx == SHN_UNDEF - || src->st_shndx >= shnum - || !src->st_name) - return false; - -#ifdef CONFIG_KALLSYMS_ALL - if (src->st_shndx == pcpundx) - return true; -#endif - - sec = sechdrs + src->st_shndx; - if (!(sec->sh_flags & SHF_ALLOC) -#ifndef CONFIG_KALLSYMS_ALL - || !(sec->sh_flags & SHF_EXECINSTR) -#endif - || (sec->sh_entsize & INIT_OFFSET_MASK)) - return false; - - return true; -} - -/* - * We only allocate and copy the strings needed by the parts of symtab - * we keep. This is simple, but has the effect of making multiple - * copies of duplicates. We could be more sophisticated, see - * linux-kernel thread starting with - * <73defb5e4bca04a6431392cc341112b1@localhost>. - */ -static void layout_symtab(struct module *mod, struct load_info *info) -{ - Elf_Shdr *symsect = info->sechdrs + info->index.sym; - Elf_Shdr *strsect = info->sechdrs + info->index.str; - const Elf_Sym *src; - unsigned int i, nsrc, ndst, strtab_size = 0; - - /* Put symbol section at end of init part of module. */ - symsect->sh_flags |= SHF_ALLOC; - symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect, - info->index.sym) | INIT_OFFSET_MASK; - pr_debug("\t%s\n", info->secstrings + symsect->sh_name); - - src = (void *)info->hdr + symsect->sh_offset; - nsrc = symsect->sh_size / sizeof(*src); - - /* Compute total space required for the core symbols' strtab. */ - for (ndst = i = 0; i < nsrc; i++) { - if (i == 0 || is_livepatch_module(mod) || - is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, - info->index.pcpu)) { - strtab_size += strlen(&info->strtab[src[i].st_name])+1; - ndst++; - } - } - - /* Append room for core symbols at end of core part. */ - info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); - info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); - mod->core_layout.size += strtab_size; - info->core_typeoffs = mod->core_layout.size; - mod->core_layout.size += ndst * sizeof(char); - mod->core_layout.size = debug_align(mod->core_layout.size); - - /* Put string table section at end of init part of module. */ - strsect->sh_flags |= SHF_ALLOC; - strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, - info->index.str) | INIT_OFFSET_MASK; - pr_debug("\t%s\n", info->secstrings + strsect->sh_name); - - /* We'll tack temporary mod_kallsyms on the end. */ - mod->init_layout.size = ALIGN(mod->init_layout.size, - __alignof__(struct mod_kallsyms)); - info->mod_kallsyms_init_off = mod->init_layout.size; - mod->init_layout.size += sizeof(struct mod_kallsyms); - info->init_typeoffs = mod->init_layout.size; - mod->init_layout.size += nsrc * sizeof(char); - mod->init_layout.size = debug_align(mod->init_layout.size); -} - -/* - * We use the full symtab and strtab which layout_symtab arranged to - * be appended to the init section. Later we switch to the cut-down - * core-only ones. - */ -static void add_kallsyms(struct module *mod, const struct load_info *info) -{ - unsigned int i, ndst; - const Elf_Sym *src; - Elf_Sym *dst; - char *s; - Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; - - /* Set up to point into init section. */ - mod->kallsyms = mod->init_layout.base + info->mod_kallsyms_init_off; - - mod->kallsyms->symtab = (void *)symsec->sh_addr; - mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym); - /* Make sure we get permanent strtab: don't use info->strtab. */ - mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr; - mod->kallsyms->typetab = mod->init_layout.base + info->init_typeoffs; - - /* - * Now populate the cut down core kallsyms for after init - * and set types up while we still have access to sections. - */ - mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; - mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; - mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs; - src = mod->kallsyms->symtab; - for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { - mod->kallsyms->typetab[i] = elf_type(src + i, info); - if (i == 0 || is_livepatch_module(mod) || - is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, - info->index.pcpu)) { - mod->core_kallsyms.typetab[ndst] = - mod->kallsyms->typetab[i]; - dst[ndst] = src[i]; - dst[ndst++].st_name = s - mod->core_kallsyms.strtab; - s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name], - KSYM_NAME_LEN) + 1; - } - } - mod->core_kallsyms.num_symtab = ndst; -} -#else -static inline void layout_symtab(struct module *mod, struct load_info *info) -{ -} - -static void add_kallsyms(struct module *mod, const struct load_info *info) -{ -} -#endif /* CONFIG_KALLSYMS */ - -#if IS_ENABLED(CONFIG_KALLSYMS) && IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) -static void init_build_id(struct module *mod, const struct load_info *info) -{ - const Elf_Shdr *sechdr; - unsigned int i; - - for (i = 0; i < info->hdr->e_shnum; i++) { - sechdr = &info->sechdrs[i]; - if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE && - !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id, - sechdr->sh_size)) - break; - } -} -#else -static void init_build_id(struct module *mod, const struct load_info *info) -{ -} -#endif - static void dynamic_debug_setup(struct module *mod, struct _ddebug *debug, unsigned int num) { if (!debug) @@ -3799,287 +3561,6 @@ static inline int within(unsigned long addr, void *start, unsigned long size) return ((void *)addr >= start && (void *)addr < start + size); } -#ifdef CONFIG_KALLSYMS -/* - * This ignores the intensely annoying "mapping symbols" found - * in ARM ELF files: $a, $t and $d. - */ -static inline int is_arm_mapping_symbol(const char *str) -{ - if (str[0] == '.' && str[1] == 'L') - return true; - return str[0] == '$' && strchr("axtd", str[1]) - && (str[2] == '\0' || str[2] == '.'); -} - -static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) -{ - return kallsyms->strtab + kallsyms->symtab[symnum].st_name; -} - -/* - * Given a module and address, find the corresponding symbol and return its name - * while providing its size and offset if needed. - */ -static const char *find_kallsyms_symbol(struct module *mod, - unsigned long addr, - unsigned long *size, - unsigned long *offset) -{ - unsigned int i, best = 0; - unsigned long nextval, bestval; - struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); - - /* At worse, next value is at end of module */ - if (within_module_init(addr, mod)) - nextval = (unsigned long)mod->init_layout.base+mod->init_layout.text_size; - else - nextval = (unsigned long)mod->core_layout.base+mod->core_layout.text_size; - - bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); - - /* - * Scan for closest preceding symbol, and next symbol. (ELF - * starts real symbols at 1). - */ - for (i = 1; i < kallsyms->num_symtab; i++) { - const Elf_Sym *sym = &kallsyms->symtab[i]; - unsigned long thisval = kallsyms_symbol_value(sym); - - if (sym->st_shndx == SHN_UNDEF) - continue; - - /* - * We ignore unnamed symbols: they're uninformative - * and inserted at a whim. - */ - if (*kallsyms_symbol_name(kallsyms, i) == '\0' - || is_arm_mapping_symbol(kallsyms_symbol_name(kallsyms, i))) - continue; - - if (thisval <= addr && thisval > bestval) { - best = i; - bestval = thisval; - } - if (thisval > addr && thisval < nextval) - nextval = thisval; - } - - if (!best) - return NULL; - - if (size) - *size = nextval - bestval; - if (offset) - *offset = addr - bestval; - - return kallsyms_symbol_name(kallsyms, best); -} - -void * __weak dereference_module_function_descriptor(struct module *mod, - void *ptr) -{ - return ptr; -} - -/* - * For kallsyms to ask for address resolution. NULL means not found. Careful - * not to lock to avoid deadlock on oopses, simply disable preemption. - */ -const char *module_address_lookup(unsigned long addr, - unsigned long *size, - unsigned long *offset, - char **modname, - const unsigned char **modbuildid, - char *namebuf) -{ - const char *ret = NULL; - struct module *mod; - - preempt_disable(); - mod = __module_address(addr); - if (mod) { - if (modname) - *modname = mod->name; - if (modbuildid) { -#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) - *modbuildid = mod->build_id; -#else - *modbuildid = NULL; -#endif - } - - ret = find_kallsyms_symbol(mod, addr, size, offset); - } - /* Make a copy in here where it's safe */ - if (ret) { - strncpy(namebuf, ret, KSYM_NAME_LEN - 1); - ret = namebuf; - } - preempt_enable(); - - return ret; -} - -int lookup_module_symbol_name(unsigned long addr, char *symname) -{ - struct module *mod; - - preempt_disable(); - list_for_each_entry_rcu(mod, &modules, list) { - if (mod->state == MODULE_STATE_UNFORMED) - continue; - if (within_module(addr, mod)) { - const char *sym; - - sym = find_kallsyms_symbol(mod, addr, NULL, NULL); - if (!sym) - goto out; - - strlcpy(symname, sym, KSYM_NAME_LEN); - preempt_enable(); - return 0; - } - } -out: - preempt_enable(); - return -ERANGE; -} - -int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, - unsigned long *offset, char *modname, char *name) -{ - struct module *mod; - - preempt_disable(); - list_for_each_entry_rcu(mod, &modules, list) { - if (mod->state == MODULE_STATE_UNFORMED) - continue; - if (within_module(addr, mod)) { - const char *sym; - - sym = find_kallsyms_symbol(mod, addr, size, offset); - if (!sym) - goto out; - if (modname) - strlcpy(modname, mod->name, MODULE_NAME_LEN); - if (name) - strlcpy(name, sym, KSYM_NAME_LEN); - preempt_enable(); - return 0; - } - } -out: - preempt_enable(); - return -ERANGE; -} - -int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, - char *name, char *module_name, int *exported) -{ - struct module *mod; - - preempt_disable(); - list_for_each_entry_rcu(mod, &modules, list) { - struct mod_kallsyms *kallsyms; - - if (mod->state == MODULE_STATE_UNFORMED) - continue; - kallsyms = rcu_dereference_sched(mod->kallsyms); - if (symnum < kallsyms->num_symtab) { - const Elf_Sym *sym = &kallsyms->symtab[symnum]; - - *value = kallsyms_symbol_value(sym); - *type = kallsyms->typetab[symnum]; - strlcpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN); - strlcpy(module_name, mod->name, MODULE_NAME_LEN); - *exported = is_exported(name, *value, mod); - preempt_enable(); - return 0; - } - symnum -= kallsyms->num_symtab; - } - preempt_enable(); - return -ERANGE; -} - -/* Given a module and name of symbol, find and return the symbol's value */ -static unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) -{ - unsigned int i; - struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); - - for (i = 0; i < kallsyms->num_symtab; i++) { - const Elf_Sym *sym = &kallsyms->symtab[i]; - - if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 && - sym->st_shndx != SHN_UNDEF) - return kallsyms_symbol_value(sym); - } - return 0; -} - -/* Look for this name: can be of form module:name. */ -unsigned long module_kallsyms_lookup_name(const char *name) -{ - struct module *mod; - char *colon; - unsigned long ret = 0; - - /* Don't lock: we're in enough trouble already. */ - preempt_disable(); - if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { - if ((mod = find_module_all(name, colon - name, false)) != NULL) - ret = find_kallsyms_symbol_value(mod, colon+1); - } else { - list_for_each_entry_rcu(mod, &modules, list) { - if (mod->state == MODULE_STATE_UNFORMED) - continue; - if ((ret = find_kallsyms_symbol_value(mod, name)) != 0) - break; - } - } - preempt_enable(); - return ret; -} - -#ifdef CONFIG_LIVEPATCH -int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, - struct module *, unsigned long), - void *data) -{ - struct module *mod; - unsigned int i; - int ret = 0; - - mutex_lock(&module_mutex); - list_for_each_entry(mod, &modules, list) { - /* We hold module_mutex: no need for rcu_dereference_sched */ - struct mod_kallsyms *kallsyms = mod->kallsyms; - - if (mod->state == MODULE_STATE_UNFORMED) - continue; - for (i = 0; i < kallsyms->num_symtab; i++) { - const Elf_Sym *sym = &kallsyms->symtab[i]; - - if (sym->st_shndx == SHN_UNDEF) - continue; - - ret = fn(data, kallsyms_symbol_name(kallsyms, i), - mod, kallsyms_symbol_value(sym)); - if (ret != 0) - goto out; - - cond_resched(); - } - } -out: - mutex_unlock(&module_mutex); - return ret; -} -#endif /* CONFIG_LIVEPATCH */ -#endif /* CONFIG_KALLSYMS */ - static void cfi_init(struct module *mod) { #ifdef CONFIG_CFI_CLANG
No functional change. This patch migrates kallsyms code out of core module code kernel/module/kallsyms.c Signed-off-by: Aaron Tomlin <atomlin@redhat.com> --- kernel/module/Makefile | 1 + kernel/module/internal.h | 29 +++ kernel/module/kallsyms.c | 506 +++++++++++++++++++++++++++++++++++++ kernel/module/main.c | 531 +-------------------------------------- 4 files changed, 542 insertions(+), 525 deletions(-) create mode 100644 kernel/module/kallsyms.c