modpost: fix broken sym->namespace for external module builds
Currently, external module builds produce tons of false-positives: WARNING: module <mod> uses symbol <sym> from namespace <ns>, but does not import it. Here, the <ns> part shows a random string. When you build external modules, the symbol info of vmlinux and in-kernel modules are read from $(objtree)/Module.symvers, but read_dump() is buggy in multiple ways: [1] When the modpost is run for vmlinux and in-kernel modules, sym_extract_namespace() allocates memory for the namespace. On the other hand, read_dump() does not, then sym->namespace will point to somewhere in the line buffer of get_next_line(). The data in the buffer will be replaced soon, and sym->namespace will end up with pointing to unrelated data. As a result, check_exports() will show random strings in the warning messages. [2] When there is no namespace, sym_extract_namespace() returns NULL. On the other hand, read_dump() sets namespace to an empty string "". (but, it will be later replaced with unrelated data due to bug [1].) The check_exports() shows a warning unless exp->namespace is NULL, so every symbol read from read_dump() emits the warning, which is mostly false positive. To address [1], sym_add_exported() calls strdup() for s->namespace. The namespace from sym_extract_namespace() must be freed to avoid memory leak. For [2], I changed the if-conditional in check_exports(). This commit also fixes sym_add_exported() to set s->namespace correctly when the symbol is preloaded. Reviewed-by: Matthias Maennich <maennich@google.com> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Jessica Yu <jeyu@kernel.org>
This commit is contained in:
parent
bf70b0503a
commit
389eb3f5f4
|
@ -166,7 +166,7 @@ struct symbol {
|
||||||
struct module *module;
|
struct module *module;
|
||||||
unsigned int crc;
|
unsigned int crc;
|
||||||
int crc_valid;
|
int crc_valid;
|
||||||
const char *namespace;
|
char *namespace;
|
||||||
unsigned int weak:1;
|
unsigned int weak:1;
|
||||||
unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */
|
unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */
|
||||||
unsigned int kernel:1; /* 1 if symbol is from kernel
|
unsigned int kernel:1; /* 1 if symbol is from kernel
|
||||||
|
@ -348,7 +348,7 @@ static enum export export_from_sec(struct elf_info *elf, unsigned int sec)
|
||||||
return export_unknown;
|
return export_unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *sym_extract_namespace(const char **symname)
|
static char *sym_extract_namespace(const char **symname)
|
||||||
{
|
{
|
||||||
char *namespace = NULL;
|
char *namespace = NULL;
|
||||||
char *ns_separator;
|
char *ns_separator;
|
||||||
|
@ -373,7 +373,6 @@ static struct symbol *sym_add_exported(const char *name, const char *namespace,
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
s = new_symbol(name, mod, export);
|
s = new_symbol(name, mod, export);
|
||||||
s->namespace = namespace;
|
|
||||||
} else {
|
} else {
|
||||||
if (!s->preloaded) {
|
if (!s->preloaded) {
|
||||||
warn("%s: '%s' exported twice. Previous export was in %s%s\n",
|
warn("%s: '%s' exported twice. Previous export was in %s%s\n",
|
||||||
|
@ -384,6 +383,8 @@ static struct symbol *sym_add_exported(const char *name, const char *namespace,
|
||||||
s->module = mod;
|
s->module = mod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(s->namespace);
|
||||||
|
s->namespace = namespace ? strdup(namespace) : NULL;
|
||||||
s->preloaded = 0;
|
s->preloaded = 0;
|
||||||
s->vmlinux = is_vmlinux(mod->name);
|
s->vmlinux = is_vmlinux(mod->name);
|
||||||
s->kernel = 0;
|
s->kernel = 0;
|
||||||
|
@ -670,7 +671,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
|
||||||
unsigned int crc;
|
unsigned int crc;
|
||||||
enum export export;
|
enum export export;
|
||||||
bool is_crc = false;
|
bool is_crc = false;
|
||||||
const char *name, *namespace;
|
const char *name;
|
||||||
|
char *namespace;
|
||||||
|
|
||||||
if ((!is_vmlinux(mod->name) || mod->is_dot_o) &&
|
if ((!is_vmlinux(mod->name) || mod->is_dot_o) &&
|
||||||
strstarts(symname, "__ksymtab"))
|
strstarts(symname, "__ksymtab"))
|
||||||
|
@ -745,6 +747,7 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
|
||||||
name = symname + strlen("__ksymtab_");
|
name = symname + strlen("__ksymtab_");
|
||||||
namespace = sym_extract_namespace(&name);
|
namespace = sym_extract_namespace(&name);
|
||||||
sym_add_exported(name, namespace, mod, export);
|
sym_add_exported(name, namespace, mod, export);
|
||||||
|
free(namespace);
|
||||||
}
|
}
|
||||||
if (strcmp(symname, "init_module") == 0)
|
if (strcmp(symname, "init_module") == 0)
|
||||||
mod->has_init = 1;
|
mod->has_init = 1;
|
||||||
|
@ -2193,7 +2196,7 @@ static int check_exports(struct module *mod)
|
||||||
else
|
else
|
||||||
basename = mod->name;
|
basename = mod->name;
|
||||||
|
|
||||||
if (exp->namespace) {
|
if (exp->namespace && exp->namespace[0]) {
|
||||||
add_namespace(&mod->required_namespaces,
|
add_namespace(&mod->required_namespaces,
|
||||||
exp->namespace);
|
exp->namespace);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue