aboutsummaryrefslogtreecommitdiff
path: root/kernel/module.c
diff options
context:
space:
mode:
authorThomas Gleixner <[email protected]>2018-09-15 20:50:42 +0200
committerThomas Gleixner <[email protected]>2018-09-15 20:50:42 +0200
commit9ac669fc01dbfef707ecaa6b618c0d03294cca16 (patch)
tree3207995766ab5664c60026daae5da268806a3262 /kernel/module.c
parentfbfa9260085b5b578a049a90135e5c51928c5f7f (diff)
parent67314ec7b0250290cc85eaa7a2f88a8ddb9e8547 (diff)
Merge tag 'y2038' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground into timers/core
Pull more y2038 work from Arnd Bergman: y2038: convert more syscalls Here is another set of system call changes to prepare the change over to 64-bit time_t. As before, the strategy is to change system calls that take a 'struct timespec' argument over to 'struct __kernel_timespec', which for now is defined to be the same but will get redefined to use a 64-bit time_t argument once we are ready to modify the system call tables. The major change from previous patches is that the plan is no longer to directly use the 'compat' system calls for providing compatibility with the existing 32-bit time_t based entry points. Instead, we rename the compat code to something that makes more sense on 32-bit architectures, e.g. compat_timespec becomes old_timespec32. With the renamed types in place, change over the 'stat' and 'utimes' families of system calls, sched_rr_get_interval, recvmmsg and rt_sigtimedwait. Another series for poll, select and io_pgetevents is currently being tested.
Diffstat (limited to 'kernel/module.c')
-rw-r--r--kernel/module.c177
1 files changed, 93 insertions, 84 deletions
diff --git a/kernel/module.c b/kernel/module.c
index f475f30eed8c..6746c85511fe 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -307,24 +307,6 @@ int unregister_module_notifier(struct notifier_block *nb)
}
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.
* Otherwise an error is returned due to ongoing or failed
@@ -547,12 +529,30 @@ static bool check_symbol(const struct symsearch *syms,
return true;
}
+static unsigned long kernel_symbol_value(const struct kernel_symbol *sym)
+{
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ return (unsigned long)offset_to_ptr(&sym->value_offset);
+#else
+ return sym->value;
+#endif
+}
+
+static const char *kernel_symbol_name(const struct kernel_symbol *sym)
+{
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+ return offset_to_ptr(&sym->name_offset);
+#else
+ return sym->name;
+#endif
+}
+
static int cmp_name(const void *va, const void *vb)
{
const char *a;
const struct kernel_symbol *b;
a = va; b = vb;
- return strcmp(a, b->name);
+ return strcmp(a, kernel_symbol_name(b));
}
static bool find_symbol_in_section(const struct symsearch *syms,
@@ -1339,14 +1339,12 @@ static inline int check_modstruct_version(const struct load_info *info,
* locking is necessary -- use preempt_disable() to placate lockdep.
*/
preempt_disable();
- if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL,
- &crc, true, false)) {
+ if (!find_symbol("module_layout", NULL, &crc, true, false)) {
preempt_enable();
BUG();
}
preempt_enable();
- return check_version(info, VMLINUX_SYMBOL_STR(module_layout),
- mod, crc);
+ return check_version(info, "module_layout", mod, crc);
}
/* First part is kernel version, which we ignore if module has crcs. */
@@ -2059,21 +2057,19 @@ static int copy_module_elf(struct module *mod, struct load_info *info)
/* Elf section header table */
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) {
ret = -ENOMEM;
goto free_info;
}
- memcpy(mod->klp_info->sechdrs, info->sechdrs, size);
/* Elf section name string table */
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) {
ret = -ENOMEM;
goto free_sechdrs;
}
- memcpy(mod->klp_info->secstrings, info->secstrings, size);
/* Elf symbol section index */
symndx = info->index.sym;
@@ -2192,7 +2188,7 @@ void *__symbol_get(const char *symbol)
sym = NULL;
preempt_enable();
- return sym ? (void *)sym->value : NULL;
+ return sym ? (void *)kernel_symbol_value(sym) : NULL;
}
EXPORT_SYMBOL_GPL(__symbol_get);
@@ -2222,10 +2218,12 @@ static int verify_export_symbols(struct module *mod)
for (i = 0; i < ARRAY_SIZE(arr); i++) {
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
- if (find_symbol(s->name, &owner, NULL, true, false)) {
+ if (find_symbol(kernel_symbol_name(s), &owner, NULL,
+ true, false)) {
pr_err("%s: exports duplicate symbol %s"
" (owned by %s)\n",
- mod->name, s->name, module_name(owner));
+ mod->name, kernel_symbol_name(s),
+ module_name(owner));
return -ENOEXEC;
}
}
@@ -2274,7 +2272,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
ksym = resolve_symbol_wait(mod, info, name);
/* Ok if resolved. */
if (ksym && !IS_ERR(ksym)) {
- sym[i].st_value = ksym->value;
+ sym[i].st_value = kernel_symbol_value(ksym);
break;
}
@@ -2282,9 +2280,9 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
break;
- pr_warn("%s: Unknown symbol %s (err %li)\n",
- mod->name, name, PTR_ERR(ksym));
ret = PTR_ERR(ksym) ?: -ENOENT;
+ pr_warn("%s: Unknown symbol %s (err %d)\n",
+ mod->name, name, ret);
break;
default:
@@ -2486,7 +2484,11 @@ static char *get_modinfo(struct load_info *info, const char *tag)
Elf_Shdr *infosec = &info->sechdrs[info->index.info];
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] == '=')
return p + taglen + 1;
}
@@ -2534,7 +2536,7 @@ static int is_exported(const char *name, unsigned long value,
ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
else
ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
- return ks != NULL && ks->value == value;
+ return ks != NULL && kernel_symbol_value(ks) == value;
}
/* As per nm */
@@ -2774,7 +2776,7 @@ static int module_sig_check(struct load_info *info, int flags)
memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
/* We truncate the module to discard the signature */
info->len -= markerlen;
- err = mod_verify_sig(mod, &info->len);
+ err = mod_verify_sig(mod, info);
}
if (!err) {
@@ -2876,7 +2878,7 @@ static int copy_module_from_user(const void __user *umod, unsigned long len,
if (info->len < sizeof(*(info->hdr)))
return -ENOEXEC;
- err = security_kernel_read_file(NULL, READING_MODULE);
+ err = security_kernel_load_data(LOADING_MODULE);
if (err)
return err;
@@ -2926,17 +2928,7 @@ static int rewrite_section_headers(struct load_info *info, int flags)
}
/* 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->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;
return 0;
@@ -2947,23 +2939,24 @@ static int rewrite_section_headers(struct load_info *info, int flags)
* search for module section index etc), and do some basic section
* verification.
*
- * Return the temporary module pointer (we'll replace it with the final
- * one when we move the module sections around).
+ * Set info->mod to the temporary copy of the module in info->hdr. The final one
+ * 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;
- int err;
- struct module *mod;
/* Set up the convenience variables */
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
info->secstrings = (void *)info->hdr
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset;
- err = rewrite_section_headers(info, flags);
- if (err)
- return ERR_PTR(err);
+ /* Try to find a name early so we can log errors with a module name */
+ info->index.info = find_sec(info, ".modinfo");
+ if (!info->index.info)
+ info->name = "(missing .modinfo section)";
+ else
+ info->name = get_modinfo(info, "name");
/* Find internal symbols and strings. */
for (i = 1; i < info->hdr->e_shnum; i++) {
@@ -2976,34 +2969,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");
if (!info->index.mod) {
pr_warn("%s: No module found in object\n",
info->name ?: "(missing .modinfo name field)");
- return ERR_PTR(-ENOEXEC);
+ return -ENOEXEC;
}
/* 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.
*/
if (!info->name)
- info->name = mod->name;
+ info->name = info->mod->name;
- if (info->index.sym == 0) {
- pr_warn("%s: module has no symbols (stripped?)\n", info->name);
- return ERR_PTR(-ENOEXEC);
- }
+ if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
+ info->index.vers = 0; /* Pretend no __versions section! */
+ else
+ info->index.vers = find_sec(info, "__versions");
info->index.pcpu = find_pcpusec(info);
- /* Check module struct version now, before we try to use module. */
- if (!check_modstruct_version(info, mod))
- return ERR_PTR(-ENOEXEC);
-
- return mod;
+ return 0;
}
static int check_modinfo(struct module *mod, struct load_info *info, int flags)
@@ -3298,25 +3292,17 @@ core_param(module_blacklist, module_blacklist, charp, 0400);
static struct module *layout_and_allocate(struct load_info *info, int flags)
{
- /* Module within temporary copy. */
struct module *mod;
unsigned int ndx;
int err;
- mod = setup_load_info(info, flags);
- if (IS_ERR(mod))
- return mod;
-
- if (blacklisted(info->name))
- return ERR_PTR(-EPERM);
-
- err = check_modinfo(mod, info, flags);
+ err = check_modinfo(info->mod, info, flags);
if (err)
return ERR_PTR(err);
/* Allow arches to frob section contents and sizes. */
err = module_frob_arch_sections(info->hdr, info->sechdrs,
- info->secstrings, mod);
+ info->secstrings, info->mod);
if (err < 0)
return ERR_PTR(err);
@@ -3335,11 +3321,11 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
/* Determine total sizes, and put offsets in sh_entsize. For now
this is done generically; there doesn't appear to be any
special cases for the architectures. */
- layout_sections(mod, info);
- layout_symtab(mod, info);
+ layout_sections(info->mod, info);
+ layout_symtab(info->mod, info);
/* Allocate and move to the final place */
- err = move_module(mod, info);
+ err = move_module(info->mod, info);
if (err)
return ERR_PTR(err);
@@ -3657,17 +3643,36 @@ static int load_module(struct load_info *info, const char __user *uargs,
int flags)
{
struct module *mod;
- long err;
+ long err = 0;
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);
if (err)
goto free_copy;
- err = elf_header_check(info);
+ err = rewrite_section_headers(info, flags);
if (err)
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. */
mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) {
@@ -4067,7 +4072,7 @@ static unsigned long mod_find_symname(struct module *mod, const char *name)
for (i = 0; i < kallsyms->num_symtab; i++)
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 0;
}
@@ -4113,6 +4118,10 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
if (mod->state == MODULE_STATE_UNFORMED)
continue;
for (i = 0; i < kallsyms->num_symtab; i++) {
+
+ if (kallsyms->symtab[i].st_shndx == SHN_UNDEF)
+ continue;
+
ret = fn(data, symname(kallsyms, i),
mod, kallsyms->symtab[i].st_value);
if (ret != 0)