Commit 2178cbc6 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux

Pull module fixes from Rusty Russell:
 "Fix for async_probe module param added in 4.3 (clearly not widely used
  yet), and a much more interesting kallsyms race which has been around
  approximately forever.  This fix is more invasive, and will require
  some care in backporting, but I hated all the bandaids I could think
  of, so...

  There are some more coming, which are only for breakages introduced
  this cycle (livepatch), but wanted these in now"

* tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  modules: fix longstanding /proc/kallsyms vs module insertion race.
  module: wrapper for symbol name.
  modules: fix modparam async_probe request
parents 7cf91adc 8244062e
...@@ -324,6 +324,12 @@ struct module_layout { ...@@ -324,6 +324,12 @@ struct module_layout {
#define __module_layout_align #define __module_layout_align
#endif #endif
struct mod_kallsyms {
Elf_Sym *symtab;
unsigned int num_symtab;
char *strtab;
};
struct module { struct module {
enum module_state state; enum module_state state;
...@@ -405,14 +411,9 @@ struct module { ...@@ -405,14 +411,9 @@ struct module {
#endif #endif
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
/* /* Protected by RCU and/or module_mutex: use rcu_dereference() */
* We keep the symbol and string tables for kallsyms. struct mod_kallsyms *kallsyms;
* The core_* fields below are temporary, loader-only (they struct mod_kallsyms core_kallsyms;
* could really be discarded after module init).
*/
Elf_Sym *symtab, *core_symtab;
unsigned int num_symtab, core_num_syms;
char *strtab, *core_strtab;
/* Section attributes */ /* Section attributes */
struct module_sect_attrs *sect_attrs; struct module_sect_attrs *sect_attrs;
......
...@@ -303,6 +303,9 @@ struct load_info { ...@@ -303,6 +303,9 @@ struct load_info {
struct _ddebug *debug; struct _ddebug *debug;
unsigned int num_debug; unsigned int num_debug;
bool sig_ok; bool sig_ok;
#ifdef CONFIG_KALLSYMS
unsigned long mod_kallsyms_init_off;
#endif
struct { struct {
unsigned int sym, str, mod, vers, info, pcpu; unsigned int sym, str, mod, vers, info, pcpu;
} index; } index;
...@@ -2480,10 +2483,21 @@ static void layout_symtab(struct module *mod, struct load_info *info) ...@@ -2480,10 +2483,21 @@ static void layout_symtab(struct module *mod, struct load_info *info)
strsect->sh_flags |= SHF_ALLOC; strsect->sh_flags |= SHF_ALLOC;
strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect,
info->index.str) | INIT_OFFSET_MASK; info->index.str) | INIT_OFFSET_MASK;
mod->init_layout.size = debug_align(mod->init_layout.size);
pr_debug("\t%s\n", info->secstrings + strsect->sh_name); 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);
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) static void add_kallsyms(struct module *mod, const struct load_info *info)
{ {
unsigned int i, ndst; unsigned int i, ndst;
...@@ -2492,29 +2506,34 @@ static void add_kallsyms(struct module *mod, const struct load_info *info) ...@@ -2492,29 +2506,34 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
char *s; char *s;
Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
mod->symtab = (void *)symsec->sh_addr; /* Set up to point into init section. */
mod->num_symtab = symsec->sh_size / sizeof(Elf_Sym); 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. */ /* Make sure we get permanent strtab: don't use info->strtab. */
mod->strtab = (void *)info->sechdrs[info->index.str].sh_addr; mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
/* Set types up while we still have access to sections. */ /* Set types up while we still have access to sections. */
for (i = 0; i < mod->num_symtab; i++) for (i = 0; i < mod->kallsyms->num_symtab; i++)
mod->symtab[i].st_info = elf_type(&mod->symtab[i], info); mod->kallsyms->symtab[i].st_info
= elf_type(&mod->kallsyms->symtab[i], info);
mod->core_symtab = dst = mod->core_layout.base + info->symoffs;
mod->core_strtab = s = mod->core_layout.base + info->stroffs; /* Now populate the cut down core kallsyms for after init. */
src = mod->symtab; mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs;
for (ndst = i = 0; i < mod->num_symtab; i++) { mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs;
src = mod->kallsyms->symtab;
for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) {
if (i == 0 || if (i == 0 ||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
info->index.pcpu)) { info->index.pcpu)) {
dst[ndst] = src[i]; dst[ndst] = src[i];
dst[ndst++].st_name = s - mod->core_strtab; dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
s += strlcpy(s, &mod->strtab[src[i].st_name], s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name],
KSYM_NAME_LEN) + 1; KSYM_NAME_LEN) + 1;
} }
} }
mod->core_num_syms = ndst; mod->core_kallsyms.num_symtab = ndst;
} }
#else #else
static inline void layout_symtab(struct module *mod, struct load_info *info) static inline void layout_symtab(struct module *mod, struct load_info *info)
...@@ -3263,9 +3282,8 @@ static noinline int do_init_module(struct module *mod) ...@@ -3263,9 +3282,8 @@ static noinline int do_init_module(struct module *mod)
module_put(mod); module_put(mod);
trim_init_extable(mod); trim_init_extable(mod);
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
mod->num_symtab = mod->core_num_syms; /* Switch to core kallsyms now init is done: kallsyms may be walking! */
mod->symtab = mod->core_symtab; rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
mod->strtab = mod->core_strtab;
#endif #endif
mod_tree_remove_init(mod); mod_tree_remove_init(mod);
disable_ro_nx(&mod->init_layout); disable_ro_nx(&mod->init_layout);
...@@ -3496,7 +3514,7 @@ static int load_module(struct load_info *info, const char __user *uargs, ...@@ -3496,7 +3514,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
/* Module is ready to execute: parsing args may do that. */ /* Module is ready to execute: parsing args may do that. */
after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-32768, 32767, NULL, -32768, 32767, mod,
unknown_module_param_cb); unknown_module_param_cb);
if (IS_ERR(after_dashes)) { if (IS_ERR(after_dashes)) {
err = PTR_ERR(after_dashes); err = PTR_ERR(after_dashes);
...@@ -3627,6 +3645,11 @@ static inline int is_arm_mapping_symbol(const char *str) ...@@ -3627,6 +3645,11 @@ static inline int is_arm_mapping_symbol(const char *str)
&& (str[2] == '\0' || str[2] == '.'); && (str[2] == '\0' || str[2] == '.');
} }
static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum)
{
return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
}
static const char *get_ksymbol(struct module *mod, static const char *get_ksymbol(struct module *mod,
unsigned long addr, unsigned long addr,
unsigned long *size, unsigned long *size,
...@@ -3634,6 +3657,7 @@ static const char *get_ksymbol(struct module *mod, ...@@ -3634,6 +3657,7 @@ static const char *get_ksymbol(struct module *mod,
{ {
unsigned int i, best = 0; unsigned int i, best = 0;
unsigned long nextval; unsigned long nextval;
struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
/* At worse, next value is at end of module */ /* At worse, next value is at end of module */
if (within_module_init(addr, mod)) if (within_module_init(addr, mod))
...@@ -3643,32 +3667,32 @@ static const char *get_ksymbol(struct module *mod, ...@@ -3643,32 +3667,32 @@ static const char *get_ksymbol(struct module *mod,
/* Scan for closest preceding symbol, and next symbol. (ELF /* Scan for closest preceding symbol, and next symbol. (ELF
starts real symbols at 1). */ starts real symbols at 1). */
for (i = 1; i < mod->num_symtab; i++) { for (i = 1; i < kallsyms->num_symtab; i++) {
if (mod->symtab[i].st_shndx == SHN_UNDEF) if (kallsyms->symtab[i].st_shndx == SHN_UNDEF)
continue; continue;
/* We ignore unnamed symbols: they're uninformative /* We ignore unnamed symbols: they're uninformative
* and inserted at a whim. */ * and inserted at a whim. */
if (mod->symtab[i].st_value <= addr if (*symname(kallsyms, i) == '\0'
&& mod->symtab[i].st_value > mod->symtab[best].st_value || is_arm_mapping_symbol(symname(kallsyms, i)))
&& *(mod->strtab + mod->symtab[i].st_name) != '\0' continue;
&& !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
if (kallsyms->symtab[i].st_value <= addr
&& kallsyms->symtab[i].st_value > kallsyms->symtab[best].st_value)
best = i; best = i;
if (mod->symtab[i].st_value > addr if (kallsyms->symtab[i].st_value > addr
&& mod->symtab[i].st_value < nextval && kallsyms->symtab[i].st_value < nextval)
&& *(mod->strtab + mod->symtab[i].st_name) != '\0' nextval = kallsyms->symtab[i].st_value;
&& !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
nextval = mod->symtab[i].st_value;
} }
if (!best) if (!best)
return NULL; return NULL;
if (size) if (size)
*size = nextval - mod->symtab[best].st_value; *size = nextval - kallsyms->symtab[best].st_value;
if (offset) if (offset)
*offset = addr - mod->symtab[best].st_value; *offset = addr - kallsyms->symtab[best].st_value;
return mod->strtab + mod->symtab[best].st_name; return symname(kallsyms, best);
} }
/* For kallsyms to ask for address resolution. NULL means not found. Careful /* For kallsyms to ask for address resolution. NULL means not found. Careful
...@@ -3758,19 +3782,21 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, ...@@ -3758,19 +3782,21 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
preempt_disable(); preempt_disable();
list_for_each_entry_rcu(mod, &modules, list) { list_for_each_entry_rcu(mod, &modules, list) {
struct mod_kallsyms *kallsyms;
if (mod->state == MODULE_STATE_UNFORMED) if (mod->state == MODULE_STATE_UNFORMED)
continue; continue;
if (symnum < mod->num_symtab) { kallsyms = rcu_dereference_sched(mod->kallsyms);
*value = mod->symtab[symnum].st_value; if (symnum < kallsyms->num_symtab) {
*type = mod->symtab[symnum].st_info; *value = kallsyms->symtab[symnum].st_value;
strlcpy(name, mod->strtab + mod->symtab[symnum].st_name, *type = kallsyms->symtab[symnum].st_info;
KSYM_NAME_LEN); strlcpy(name, symname(kallsyms, symnum), KSYM_NAME_LEN);
strlcpy(module_name, mod->name, MODULE_NAME_LEN); strlcpy(module_name, mod->name, MODULE_NAME_LEN);
*exported = is_exported(name, *value, mod); *exported = is_exported(name, *value, mod);
preempt_enable(); preempt_enable();
return 0; return 0;
} }
symnum -= mod->num_symtab; symnum -= kallsyms->num_symtab;
} }
preempt_enable(); preempt_enable();
return -ERANGE; return -ERANGE;
...@@ -3779,11 +3805,12 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, ...@@ -3779,11 +3805,12 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
static unsigned long mod_find_symname(struct module *mod, const char *name) static unsigned long mod_find_symname(struct module *mod, const char *name)
{ {
unsigned int i; unsigned int i;
struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
for (i = 0; i < mod->num_symtab; i++) for (i = 0; i < kallsyms->num_symtab; i++)
if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 && if (strcmp(name, symname(kallsyms, i)) == 0 &&
mod->symtab[i].st_info != 'U') kallsyms->symtab[i].st_info != 'U')
return mod->symtab[i].st_value; return kallsyms->symtab[i].st_value;
return 0; return 0;
} }
...@@ -3822,11 +3849,14 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, ...@@ -3822,11 +3849,14 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
module_assert_mutex(); module_assert_mutex();
list_for_each_entry(mod, &modules, list) { 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) if (mod->state == MODULE_STATE_UNFORMED)
continue; continue;
for (i = 0; i < mod->num_symtab; i++) { for (i = 0; i < kallsyms->num_symtab; i++) {
ret = fn(data, mod->strtab + mod->symtab[i].st_name, ret = fn(data, symname(kallsyms, i),
mod, mod->symtab[i].st_value); mod, kallsyms->symtab[i].st_value);
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment