modules: fix longstanding /proc/kallsyms vs module insertion race.
For CONFIG_KALLSYMS, we keep two symbol tables and two string tables. There's one full copy, marked SHF_ALLOC and laid out at the end of the module's init section. There's also a cut-down version that only contains core symbols and strings, and lives in the module's core section. After module init (and before we free the module memory), we switch the mod->symtab, mod->num_symtab and mod->strtab to point to the core versions. We do this under the module_mutex. However, kallsyms doesn't take the module_mutex: it uses preempt_disable() and rcu tricks to walk through the modules, because it's used in the oops path. It's also used in /proc/kallsyms. There's nothing atomic about the change of these variables, so we can get the old (larger!) num_symtab and the new symtab pointer; in fact this is what I saw when trying to reproduce. By grouping these variables together, we can use a carefully-dereferenced pointer to ensure we always get one or the other (the free of the module init section is already done in an RCU callback, so that's safe). We allocate the init one at the end of the module init section, and keep the core one inside the struct module itself (it could also have been allocated at the end of the module core, but that's probably overkill). CRs-Fixed: 982779 Change-Id: I519f081967785e44a6ea33b16b1da64b14979963 Reported-by: Weilong Chen <chenweilong@huawei.com> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=111541 Cc: stable@kernel.org Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Git-commit: 8244062ef1e54502ef55f54cced659913f244c3e Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [salvares@codeaurora.org: resolved context conflicts in module.c] Signed-off-by: Sanrio Alvares <salvares@codeaurora.org>
This commit is contained in:
parent
6890367336
commit
606471708a
2 changed files with 77 additions and 49 deletions
|
@ -302,6 +302,12 @@ struct mod_tree_node {
|
||||||
struct latch_tree_node node;
|
struct latch_tree_node node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mod_kallsyms {
|
||||||
|
Elf_Sym *symtab;
|
||||||
|
unsigned int num_symtab;
|
||||||
|
char *strtab;
|
||||||
|
};
|
||||||
|
|
||||||
struct module {
|
struct module {
|
||||||
enum module_state state;
|
enum module_state state;
|
||||||
|
|
||||||
|
@ -411,14 +417,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;
|
||||||
|
|
109
kernel/module.c
109
kernel/module.c
|
@ -327,6 +327,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;
|
||||||
|
@ -2494,8 +2497,20 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
||||||
info->index.str) | INIT_OFFSET_MASK;
|
info->index.str) | INIT_OFFSET_MASK;
|
||||||
mod->init_size = debug_align(mod->init_size);
|
mod->init_size = debug_align(mod->init_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_size = ALIGN(mod->init_size,
|
||||||
|
__alignof__(struct mod_kallsyms));
|
||||||
|
info->mod_kallsyms_init_off = mod->init_size;
|
||||||
|
mod->init_size += sizeof(struct mod_kallsyms);
|
||||||
|
mod->init_size = debug_align(mod->init_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;
|
||||||
|
@ -2504,28 +2519,33 @@ 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->module_init + 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->module_core + info->symoffs;
|
/* Now populate the cut down core kallsyms for after init. */
|
||||||
mod->core_strtab = s = mod->module_core + info->stroffs;
|
mod->core_kallsyms.symtab = dst = mod->module_core + info->symoffs;
|
||||||
src = mod->symtab;
|
mod->core_kallsyms.strtab = s = mod->module_core + info->stroffs;
|
||||||
for (ndst = i = 0; i < mod->num_symtab; i++) {
|
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)) {
|
||||||
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)
|
||||||
|
@ -3274,9 +3294,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);
|
||||||
unset_module_init_ro_nx(mod);
|
unset_module_init_ro_nx(mod);
|
||||||
|
@ -3646,9 +3665,9 @@ 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 module *mod, unsigned int symnum)
|
static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum)
|
||||||
{
|
{
|
||||||
return mod->strtab + mod->symtab[symnum].st_name;
|
return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *get_ksymbol(struct module *mod,
|
static const char *get_ksymbol(struct module *mod,
|
||||||
|
@ -3658,6 +3677,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))
|
||||||
|
@ -3667,32 +3687,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 (*symname(mod, i) == '\0'
|
if (*symname(kallsyms, i) == '\0'
|
||||||
|| is_arm_mapping_symbol(symname(mod, i)))
|
|| is_arm_mapping_symbol(symname(kallsyms, i)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mod->symtab[i].st_value <= addr
|
if (kallsyms->symtab[i].st_value <= addr
|
||||||
&& mod->symtab[i].st_value > mod->symtab[best].st_value)
|
&& 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)
|
||||||
nextval = mod->symtab[i].st_value;
|
nextval = kallsyms->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 symname(mod, best);
|
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
|
||||||
|
@ -3782,18 +3802,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, symname(mod, symnum), KSYM_NAME_LEN);
|
*type = kallsyms->symtab[symnum].st_info;
|
||||||
|
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;
|
||||||
|
@ -3802,11 +3825,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, symname(mod, i)) == 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3845,11 +3869,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, symname(mod, i),
|
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue