Merge tag 'modules-for-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux

Pull modules updates from Jessica Yu:
 "Summary of modules changes for the 4.19 merge window:

   - Fix modules kallsyms for livepatch. Livepatch modules can have
     SHN_UNDEF symbols in their module symbol tables for later symbol
     resolution, but kallsyms shouldn't be returning these symbols

   - Some code cleanups and minor reshuffling in load_module() were done
     to log the module name when module signature verification fails"

* tag 'modules-for-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux:
  kernel/module: Use kmemdup to replace kmalloc+memcpy
  ARM: module: fix modsign build error
  modsign: log module name in the event of an error
  module: replace VMLINUX_SYMBOL_STR() with __stringify() or string literal
  module: print sensible error code
  module: setup load info before module_sig_check()
  module: make it clear when we're handling the module copy in info->hdr
  module: exclude SHN_UNDEF symbols from kallsyms api
This commit is contained in:
Linus Torvalds
2018-08-17 10:51:22 -07:00
5 changed files with 100 additions and 85 deletions

View File

@@ -34,6 +34,7 @@ struct mod_arch_specific {
#endif #endif
}; };
struct module;
u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val); u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val);
/* /*

View File

@@ -266,7 +266,7 @@ extern int modules_disabled; /* for sysctl */
/* Get/put a kernel symbol (calls must be symmetric) */ /* Get/put a kernel symbol (calls must be symmetric) */
void *__symbol_get(const char *symbol); void *__symbol_get(const char *symbol);
void *__symbol_get_gpl(const char *symbol); void *__symbol_get_gpl(const char *symbol);
#define symbol_get(x) ((typeof(&x))(__symbol_get(VMLINUX_SYMBOL_STR(x)))) #define symbol_get(x) ((typeof(&x))(__symbol_get(__stringify(x))))
/* modules using other modules: kdb wants to see this. */ /* modules using other modules: kdb wants to see this. */
struct module_use { struct module_use {
@@ -575,7 +575,7 @@ extern void __noreturn __module_put_and_exit(struct module *mod,
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
int module_refcount(struct module *mod); int module_refcount(struct module *mod);
void __symbol_put(const char *symbol); void __symbol_put(const char *symbol);
#define symbol_put(x) __symbol_put(VMLINUX_SYMBOL_STR(x)) #define symbol_put(x) __symbol_put(__stringify(x))
void symbol_put_addr(void *addr); void symbol_put_addr(void *addr);
/* Sometimes we know we already have a refcount, and it's easier not /* Sometimes we know we already have a refcount, and it's easier not

View File

@@ -9,4 +9,27 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
extern int mod_verify_sig(const void *mod, unsigned long *_modlen); #include <linux/elf.h>
#include <asm/module.h>
struct load_info {
const char *name;
/* pointer to module in temporary copy, freed at end of load_module() */
struct module *mod;
Elf_Ehdr *hdr;
unsigned long len;
Elf_Shdr *sechdrs;
char *secstrings, *strtab;
unsigned long symoffs, stroffs;
struct _ddebug *debug;
unsigned int num_debug;
bool sig_ok;
#ifdef CONFIG_KALLSYMS
unsigned long mod_kallsyms_init_off;
#endif
struct {
unsigned int sym, str, mod, vers, info, pcpu;
} index;
};
extern int mod_verify_sig(const void *mod, struct load_info *info);

View File

@@ -307,24 +307,6 @@ int unregister_module_notifier(struct notifier_block *nb)
} }
EXPORT_SYMBOL(unregister_module_notifier); EXPORT_SYMBOL(unregister_module_notifier);
struct load_info {
const char *name;
Elf_Ehdr *hdr;
unsigned long len;
Elf_Shdr *sechdrs;
char *secstrings, *strtab;
unsigned long symoffs, stroffs;
struct _ddebug *debug;
unsigned int num_debug;
bool sig_ok;
#ifdef CONFIG_KALLSYMS
unsigned long mod_kallsyms_init_off;
#endif
struct {
unsigned int sym, str, mod, vers, info, pcpu;
} index;
};
/* /*
* We require a truly strong try_module_get(): 0 means success. * We require a truly strong try_module_get(): 0 means success.
* Otherwise an error is returned due to ongoing or failed * Otherwise an error is returned due to ongoing or failed
@@ -1339,14 +1321,12 @@ static inline int check_modstruct_version(const struct load_info *info,
* locking is necessary -- use preempt_disable() to placate lockdep. * locking is necessary -- use preempt_disable() to placate lockdep.
*/ */
preempt_disable(); preempt_disable();
if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL, if (!find_symbol("module_layout", NULL, &crc, true, false)) {
&crc, true, false)) {
preempt_enable(); preempt_enable();
BUG(); BUG();
} }
preempt_enable(); preempt_enable();
return check_version(info, VMLINUX_SYMBOL_STR(module_layout), return check_version(info, "module_layout", mod, crc);
mod, crc);
} }
/* First part is kernel version, which we ignore if module has crcs. */ /* First part is kernel version, which we ignore if module has crcs. */
@@ -2059,21 +2039,19 @@ static int copy_module_elf(struct module *mod, struct load_info *info)
/* Elf section header table */ /* Elf section header table */
size = sizeof(*info->sechdrs) * info->hdr->e_shnum; size = sizeof(*info->sechdrs) * info->hdr->e_shnum;
mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL); mod->klp_info->sechdrs = kmemdup(info->sechdrs, size, GFP_KERNEL);
if (mod->klp_info->sechdrs == NULL) { if (mod->klp_info->sechdrs == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto free_info; goto free_info;
} }
memcpy(mod->klp_info->sechdrs, info->sechdrs, size);
/* Elf section name string table */ /* Elf section name string table */
size = info->sechdrs[info->hdr->e_shstrndx].sh_size; size = info->sechdrs[info->hdr->e_shstrndx].sh_size;
mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL); mod->klp_info->secstrings = kmemdup(info->secstrings, size, GFP_KERNEL);
if (mod->klp_info->secstrings == NULL) { if (mod->klp_info->secstrings == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto free_sechdrs; goto free_sechdrs;
} }
memcpy(mod->klp_info->secstrings, info->secstrings, size);
/* Elf symbol section index */ /* Elf symbol section index */
symndx = info->index.sym; symndx = info->index.sym;
@@ -2282,9 +2260,9 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
break; break;
pr_warn("%s: Unknown symbol %s (err %li)\n",
mod->name, name, PTR_ERR(ksym));
ret = PTR_ERR(ksym) ?: -ENOENT; ret = PTR_ERR(ksym) ?: -ENOENT;
pr_warn("%s: Unknown symbol %s (err %d)\n",
mod->name, name, ret);
break; break;
default: default:
@@ -2486,7 +2464,11 @@ static char *get_modinfo(struct load_info *info, const char *tag)
Elf_Shdr *infosec = &info->sechdrs[info->index.info]; Elf_Shdr *infosec = &info->sechdrs[info->index.info];
unsigned long size = infosec->sh_size; unsigned long size = infosec->sh_size;
for (p = (char *)infosec->sh_addr; p; p = next_string(p, &size)) { /*
* get_modinfo() calls made before rewrite_section_headers()
* must use sh_offset, as sh_addr isn't set!
*/
for (p = (char *)info->hdr + infosec->sh_offset; p; p = next_string(p, &size)) {
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
return p + taglen + 1; return p + taglen + 1;
} }
@@ -2774,7 +2756,7 @@ static int module_sig_check(struct load_info *info, int flags)
memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
/* We truncate the module to discard the signature */ /* We truncate the module to discard the signature */
info->len -= markerlen; info->len -= markerlen;
err = mod_verify_sig(mod, &info->len); err = mod_verify_sig(mod, info);
} }
if (!err) { if (!err) {
@@ -2926,17 +2908,7 @@ static int rewrite_section_headers(struct load_info *info, int flags)
} }
/* Track but don't keep modinfo and version sections. */ /* Track but don't keep modinfo and version sections. */
if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
info->index.vers = 0; /* Pretend no __versions section! */
else
info->index.vers = find_sec(info, "__versions");
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC; info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
info->index.info = find_sec(info, ".modinfo");
if (!info->index.info)
info->name = "(missing .modinfo section)";
else
info->name = get_modinfo(info, "name");
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC; info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
return 0; return 0;
@@ -2947,23 +2919,24 @@ static int rewrite_section_headers(struct load_info *info, int flags)
* search for module section index etc), and do some basic section * search for module section index etc), and do some basic section
* verification. * verification.
* *
* Return the temporary module pointer (we'll replace it with the final * Set info->mod to the temporary copy of the module in info->hdr. The final one
* one when we move the module sections around). * will be allocated in move_module().
*/ */
static struct module *setup_load_info(struct load_info *info, int flags) static int setup_load_info(struct load_info *info, int flags)
{ {
unsigned int i; unsigned int i;
int err;
struct module *mod;
/* Set up the convenience variables */ /* Set up the convenience variables */
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff; info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
info->secstrings = (void *)info->hdr info->secstrings = (void *)info->hdr
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset; + info->sechdrs[info->hdr->e_shstrndx].sh_offset;
err = rewrite_section_headers(info, flags); /* Try to find a name early so we can log errors with a module name */
if (err) info->index.info = find_sec(info, ".modinfo");
return ERR_PTR(err); if (!info->index.info)
info->name = "(missing .modinfo section)";
else
info->name = get_modinfo(info, "name");
/* Find internal symbols and strings. */ /* Find internal symbols and strings. */
for (i = 1; i < info->hdr->e_shnum; i++) { for (i = 1; i < info->hdr->e_shnum; i++) {
@@ -2976,34 +2949,35 @@ static struct module *setup_load_info(struct load_info *info, int flags)
} }
} }
if (info->index.sym == 0) {
pr_warn("%s: module has no symbols (stripped?)\n", info->name);
return -ENOEXEC;
}
info->index.mod = find_sec(info, ".gnu.linkonce.this_module"); info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
if (!info->index.mod) { if (!info->index.mod) {
pr_warn("%s: No module found in object\n", pr_warn("%s: No module found in object\n",
info->name ?: "(missing .modinfo name field)"); info->name ?: "(missing .modinfo name field)");
return ERR_PTR(-ENOEXEC); return -ENOEXEC;
} }
/* This is temporary: point mod into copy of data. */ /* This is temporary: point mod into copy of data. */
mod = (void *)info->sechdrs[info->index.mod].sh_addr; info->mod = (void *)info->hdr + info->sechdrs[info->index.mod].sh_offset;
/* /*
* If we didn't load the .modinfo 'name' field, fall back to * If we didn't load the .modinfo 'name' field earlier, fall back to
* on-disk struct mod 'name' field. * on-disk struct mod 'name' field.
*/ */
if (!info->name) if (!info->name)
info->name = mod->name; info->name = info->mod->name;
if (info->index.sym == 0) { if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
pr_warn("%s: module has no symbols (stripped?)\n", info->name); info->index.vers = 0; /* Pretend no __versions section! */
return ERR_PTR(-ENOEXEC); else
} info->index.vers = find_sec(info, "__versions");
info->index.pcpu = find_pcpusec(info); info->index.pcpu = find_pcpusec(info);
/* Check module struct version now, before we try to use module. */ return 0;
if (!check_modstruct_version(info, mod))
return ERR_PTR(-ENOEXEC);
return mod;
} }
static int check_modinfo(struct module *mod, struct load_info *info, int flags) static int check_modinfo(struct module *mod, struct load_info *info, int flags)
@@ -3298,25 +3272,17 @@ core_param(module_blacklist, module_blacklist, charp, 0400);
static struct module *layout_and_allocate(struct load_info *info, int flags) static struct module *layout_and_allocate(struct load_info *info, int flags)
{ {
/* Module within temporary copy. */
struct module *mod; struct module *mod;
unsigned int ndx; unsigned int ndx;
int err; int err;
mod = setup_load_info(info, flags); err = check_modinfo(info->mod, info, flags);
if (IS_ERR(mod))
return mod;
if (blacklisted(info->name))
return ERR_PTR(-EPERM);
err = check_modinfo(mod, info, flags);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
/* Allow arches to frob section contents and sizes. */ /* Allow arches to frob section contents and sizes. */
err = module_frob_arch_sections(info->hdr, info->sechdrs, err = module_frob_arch_sections(info->hdr, info->sechdrs,
info->secstrings, mod); info->secstrings, info->mod);
if (err < 0) if (err < 0)
return ERR_PTR(err); return ERR_PTR(err);
@@ -3335,11 +3301,11 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
/* Determine total sizes, and put offsets in sh_entsize. For now /* Determine total sizes, and put offsets in sh_entsize. For now
this is done generically; there doesn't appear to be any this is done generically; there doesn't appear to be any
special cases for the architectures. */ special cases for the architectures. */
layout_sections(mod, info); layout_sections(info->mod, info);
layout_symtab(mod, info); layout_symtab(info->mod, info);
/* Allocate and move to the final place */ /* Allocate and move to the final place */
err = move_module(mod, info); err = move_module(info->mod, info);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
@@ -3657,17 +3623,36 @@ static int load_module(struct load_info *info, const char __user *uargs,
int flags) int flags)
{ {
struct module *mod; struct module *mod;
long err; long err = 0;
char *after_dashes; char *after_dashes;
err = elf_header_check(info);
if (err)
goto free_copy;
err = setup_load_info(info, flags);
if (err)
goto free_copy;
if (blacklisted(info->name)) {
err = -EPERM;
goto free_copy;
}
err = module_sig_check(info, flags); err = module_sig_check(info, flags);
if (err) if (err)
goto free_copy; goto free_copy;
err = elf_header_check(info); err = rewrite_section_headers(info, flags);
if (err) if (err)
goto free_copy; goto free_copy;
/* Check module struct version now, before we try to use module. */
if (!check_modstruct_version(info, info->mod)) {
err = -ENOEXEC;
goto free_copy;
}
/* Figure out module layout, and allocate all the memory. */ /* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info, flags); mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) { if (IS_ERR(mod)) {
@@ -4067,7 +4052,7 @@ static unsigned long mod_find_symname(struct module *mod, const char *name)
for (i = 0; i < kallsyms->num_symtab; i++) for (i = 0; i < kallsyms->num_symtab; i++)
if (strcmp(name, symname(kallsyms, i)) == 0 && if (strcmp(name, symname(kallsyms, i)) == 0 &&
kallsyms->symtab[i].st_info != 'U') kallsyms->symtab[i].st_shndx != SHN_UNDEF)
return kallsyms->symtab[i].st_value; return kallsyms->symtab[i].st_value;
return 0; return 0;
} }
@@ -4113,6 +4098,10 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
if (mod->state == MODULE_STATE_UNFORMED) if (mod->state == MODULE_STATE_UNFORMED)
continue; continue;
for (i = 0; i < kallsyms->num_symtab; i++) { for (i = 0; i < kallsyms->num_symtab; i++) {
if (kallsyms->symtab[i].st_shndx == SHN_UNDEF)
continue;
ret = fn(data, symname(kallsyms, i), ret = fn(data, symname(kallsyms, i),
mod, kallsyms->symtab[i].st_value); mod, kallsyms->symtab[i].st_value);
if (ret != 0) if (ret != 0)

View File

@@ -45,10 +45,10 @@ struct module_signature {
/* /*
* Verify the signature on a module. * Verify the signature on a module.
*/ */
int mod_verify_sig(const void *mod, unsigned long *_modlen) int mod_verify_sig(const void *mod, struct load_info *info)
{ {
struct module_signature ms; struct module_signature ms;
size_t modlen = *_modlen, sig_len; size_t sig_len, modlen = info->len;
pr_devel("==>%s(,%zu)\n", __func__, modlen); pr_devel("==>%s(,%zu)\n", __func__, modlen);
@@ -62,10 +62,11 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
if (sig_len >= modlen) if (sig_len >= modlen)
return -EBADMSG; return -EBADMSG;
modlen -= sig_len; modlen -= sig_len;
*_modlen = modlen; info->len = modlen;
if (ms.id_type != PKEY_ID_PKCS7) { if (ms.id_type != PKEY_ID_PKCS7) {
pr_err("Module is not signed with expected PKCS#7 message\n"); pr_err("%s: Module is not signed with expected PKCS#7 message\n",
info->name);
return -ENOPKG; return -ENOPKG;
} }
@@ -76,7 +77,8 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
ms.__pad[0] != 0 || ms.__pad[0] != 0 ||
ms.__pad[1] != 0 || ms.__pad[1] != 0 ||
ms.__pad[2] != 0) { ms.__pad[2] != 0) {
pr_err("PKCS#7 signature info has unexpected non-zero params\n"); pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
info->name);
return -EBADMSG; return -EBADMSG;
} }