Skip to content

Commit 610dde5

Browse files
rustyrussellgregkh
authored andcommitted
modules: fix longstanding /proc/kallsyms vs module insertion race.
commit 8244062ef1e54502ef55f54cced659913f244c3e upstream. 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). [ Rebased for 4.4-stable and older, because the following changes aren't in the older trees: - e0224418516b4d8a6c2160574bac18447c354ef0: adds arg to is_core_symbol - 7523e4dc5057e157212b4741abd6256e03404cf1: module_init/module_core/init_size/core_size become init_layout.base/core_layout.base/init_layout.size/core_layout.size. ] 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 74b2c72 commit 610dde5

2 files changed

Lines changed: 78 additions & 51 deletions

File tree

include/linux/module.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ struct mod_tree_node {
302302
struct latch_tree_node node;
303303
};
304304

305+
struct mod_kallsyms {
306+
Elf_Sym *symtab;
307+
unsigned int num_symtab;
308+
char *strtab;
309+
};
310+
305311
struct module {
306312
enum module_state state;
307313

@@ -411,14 +417,9 @@ struct module {
411417
#endif
412418

413419
#ifdef CONFIG_KALLSYMS
414-
/*
415-
* We keep the symbol and string tables for kallsyms.
416-
* The core_* fields below are temporary, loader-only (they
417-
* could really be discarded after module init).
418-
*/
419-
Elf_Sym *symtab, *core_symtab;
420-
unsigned int num_symtab, core_num_syms;
421-
char *strtab, *core_strtab;
420+
/* Protected by RCU and/or module_mutex: use rcu_dereference() */
421+
struct mod_kallsyms *kallsyms;
422+
struct mod_kallsyms core_kallsyms;
422423

423424
/* Section attributes */
424425
struct module_sect_attrs *sect_attrs;

kernel/module.c

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ struct load_info {
327327
struct _ddebug *debug;
328328
unsigned int num_debug;
329329
bool sig_ok;
330+
#ifdef CONFIG_KALLSYMS
331+
unsigned long mod_kallsyms_init_off;
332+
#endif
330333
struct {
331334
unsigned int sym, str, mod, vers, info, pcpu;
332335
} index;
@@ -2492,10 +2495,21 @@ static void layout_symtab(struct module *mod, struct load_info *info)
24922495
strsect->sh_flags |= SHF_ALLOC;
24932496
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
24942497
info->index.str) | INIT_OFFSET_MASK;
2495-
mod->init_size = debug_align(mod->init_size);
24962498
pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
2499+
2500+
/* We'll tack temporary mod_kallsyms on the end. */
2501+
mod->init_size = ALIGN(mod->init_size,
2502+
__alignof__(struct mod_kallsyms));
2503+
info->mod_kallsyms_init_off = mod->init_size;
2504+
mod->init_size += sizeof(struct mod_kallsyms);
2505+
mod->init_size = debug_align(mod->init_size);
24972506
}
24982507

2508+
/*
2509+
* We use the full symtab and strtab which layout_symtab arranged to
2510+
* be appended to the init section. Later we switch to the cut-down
2511+
* core-only ones.
2512+
*/
24992513
static void add_kallsyms(struct module *mod, const struct load_info *info)
25002514
{
25012515
unsigned int i, ndst;
@@ -2504,28 +2518,33 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
25042518
char *s;
25052519
Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
25062520

2507-
mod->symtab = (void *)symsec->sh_addr;
2508-
mod->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
2521+
/* Set up to point into init section. */
2522+
mod->kallsyms = mod->module_init + info->mod_kallsyms_init_off;
2523+
2524+
mod->kallsyms->symtab = (void *)symsec->sh_addr;
2525+
mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
25092526
/* Make sure we get permanent strtab: don't use info->strtab. */
2510-
mod->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
2527+
mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
25112528

25122529
/* Set types up while we still have access to sections. */
2513-
for (i = 0; i < mod->num_symtab; i++)
2514-
mod->symtab[i].st_info = elf_type(&mod->symtab[i], info);
2515-
2516-
mod->core_symtab = dst = mod->module_core + info->symoffs;
2517-
mod->core_strtab = s = mod->module_core + info->stroffs;
2518-
src = mod->symtab;
2519-
for (ndst = i = 0; i < mod->num_symtab; i++) {
2530+
for (i = 0; i < mod->kallsyms->num_symtab; i++)
2531+
mod->kallsyms->symtab[i].st_info
2532+
= elf_type(&mod->kallsyms->symtab[i], info);
2533+
2534+
/* Now populate the cut down core kallsyms for after init. */
2535+
mod->core_kallsyms.symtab = dst = mod->module_core + info->symoffs;
2536+
mod->core_kallsyms.strtab = s = mod->module_core + info->stroffs;
2537+
src = mod->kallsyms->symtab;
2538+
for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) {
25202539
if (i == 0 ||
25212540
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
25222541
dst[ndst] = src[i];
2523-
dst[ndst++].st_name = s - mod->core_strtab;
2524-
s += strlcpy(s, &mod->strtab[src[i].st_name],
2542+
dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
2543+
s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name],
25252544
KSYM_NAME_LEN) + 1;
25262545
}
25272546
}
2528-
mod->core_num_syms = ndst;
2547+
mod->core_kallsyms.num_symtab = ndst;
25292548
}
25302549
#else
25312550
static inline void layout_symtab(struct module *mod, struct load_info *info)
@@ -3274,9 +3293,8 @@ static noinline int do_init_module(struct module *mod)
32743293
module_put(mod);
32753294
trim_init_extable(mod);
32763295
#ifdef CONFIG_KALLSYMS
3277-
mod->num_symtab = mod->core_num_syms;
3278-
mod->symtab = mod->core_symtab;
3279-
mod->strtab = mod->core_strtab;
3296+
/* Switch to core kallsyms now init is done: kallsyms may be walking! */
3297+
rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
32803298
#endif
32813299
mod_tree_remove_init(mod);
32823300
unset_module_init_ro_nx(mod);
@@ -3646,9 +3664,9 @@ static inline int is_arm_mapping_symbol(const char *str)
36463664
&& (str[2] == '\0' || str[2] == '.');
36473665
}
36483666

3649-
static const char *symname(struct module *mod, unsigned int symnum)
3667+
static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum)
36503668
{
3651-
return mod->strtab + mod->symtab[symnum].st_name;
3669+
return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
36523670
}
36533671

36543672
static const char *get_ksymbol(struct module *mod,
@@ -3658,6 +3676,7 @@ static const char *get_ksymbol(struct module *mod,
36583676
{
36593677
unsigned int i, best = 0;
36603678
unsigned long nextval;
3679+
struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
36613680

36623681
/* At worse, next value is at end of module */
36633682
if (within_module_init(addr, mod))
@@ -3667,32 +3686,32 @@ static const char *get_ksymbol(struct module *mod,
36673686

36683687
/* Scan for closest preceding symbol, and next symbol. (ELF
36693688
starts real symbols at 1). */
3670-
for (i = 1; i < mod->num_symtab; i++) {
3671-
if (mod->symtab[i].st_shndx == SHN_UNDEF)
3689+
for (i = 1; i < kallsyms->num_symtab; i++) {
3690+
if (kallsyms->symtab[i].st_shndx == SHN_UNDEF)
36723691
continue;
36733692

36743693
/* We ignore unnamed symbols: they're uninformative
36753694
* and inserted at a whim. */
3676-
if (*symname(mod, i) == '\0'
3677-
|| is_arm_mapping_symbol(symname(mod, i)))
3695+
if (*symname(kallsyms, i) == '\0'
3696+
|| is_arm_mapping_symbol(symname(kallsyms, i)))
36783697
continue;
36793698

3680-
if (mod->symtab[i].st_value <= addr
3681-
&& mod->symtab[i].st_value > mod->symtab[best].st_value)
3699+
if (kallsyms->symtab[i].st_value <= addr
3700+
&& kallsyms->symtab[i].st_value > kallsyms->symtab[best].st_value)
36823701
best = i;
3683-
if (mod->symtab[i].st_value > addr
3684-
&& mod->symtab[i].st_value < nextval)
3685-
nextval = mod->symtab[i].st_value;
3702+
if (kallsyms->symtab[i].st_value > addr
3703+
&& kallsyms->symtab[i].st_value < nextval)
3704+
nextval = kallsyms->symtab[i].st_value;
36863705
}
36873706

36883707
if (!best)
36893708
return NULL;
36903709

36913710
if (size)
3692-
*size = nextval - mod->symtab[best].st_value;
3711+
*size = nextval - kallsyms->symtab[best].st_value;
36933712
if (offset)
3694-
*offset = addr - mod->symtab[best].st_value;
3695-
return symname(mod, best);
3713+
*offset = addr - kallsyms->symtab[best].st_value;
3714+
return symname(kallsyms, best);
36963715
}
36973716

36983717
/* For kallsyms to ask for address resolution. NULL means not found. Careful
@@ -3782,18 +3801,21 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
37823801

37833802
preempt_disable();
37843803
list_for_each_entry_rcu(mod, &modules, list) {
3804+
struct mod_kallsyms *kallsyms;
3805+
37853806
if (mod->state == MODULE_STATE_UNFORMED)
37863807
continue;
3787-
if (symnum < mod->num_symtab) {
3788-
*value = mod->symtab[symnum].st_value;
3789-
*type = mod->symtab[symnum].st_info;
3790-
strlcpy(name, symname(mod, symnum), KSYM_NAME_LEN);
3808+
kallsyms = rcu_dereference_sched(mod->kallsyms);
3809+
if (symnum < kallsyms->num_symtab) {
3810+
*value = kallsyms->symtab[symnum].st_value;
3811+
*type = kallsyms->symtab[symnum].st_info;
3812+
strlcpy(name, symname(kallsyms, symnum), KSYM_NAME_LEN);
37913813
strlcpy(module_name, mod->name, MODULE_NAME_LEN);
37923814
*exported = is_exported(name, *value, mod);
37933815
preempt_enable();
37943816
return 0;
37953817
}
3796-
symnum -= mod->num_symtab;
3818+
symnum -= kallsyms->num_symtab;
37973819
}
37983820
preempt_enable();
37993821
return -ERANGE;
@@ -3802,11 +3824,12 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
38023824
static unsigned long mod_find_symname(struct module *mod, const char *name)
38033825
{
38043826
unsigned int i;
3827+
struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
38053828

3806-
for (i = 0; i < mod->num_symtab; i++)
3807-
if (strcmp(name, symname(mod, i)) == 0 &&
3808-
mod->symtab[i].st_info != 'U')
3809-
return mod->symtab[i].st_value;
3829+
for (i = 0; i < kallsyms->num_symtab; i++)
3830+
if (strcmp(name, symname(kallsyms, i)) == 0 &&
3831+
kallsyms->symtab[i].st_info != 'U')
3832+
return kallsyms->symtab[i].st_value;
38103833
return 0;
38113834
}
38123835

@@ -3845,11 +3868,14 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
38453868
module_assert_mutex();
38463869

38473870
list_for_each_entry(mod, &modules, list) {
3871+
/* We hold module_mutex: no need for rcu_dereference_sched */
3872+
struct mod_kallsyms *kallsyms = mod->kallsyms;
3873+
38483874
if (mod->state == MODULE_STATE_UNFORMED)
38493875
continue;
3850-
for (i = 0; i < mod->num_symtab; i++) {
3851-
ret = fn(data, symname(mod, i),
3852-
mod, mod->symtab[i].st_value);
3876+
for (i = 0; i < kallsyms->num_symtab; i++) {
3877+
ret = fn(data, symname(kallsyms, i),
3878+
mod, kallsyms->symtab[i].st_value);
38533879
if (ret != 0)
38543880
return ret;
38553881
}

0 commit comments

Comments
 (0)