]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/blobdiff - kernel/module.c
module: add syscall to load module from fd
[mirror_ubuntu-disco-kernel.git] / kernel / module.c
index 6e48c3a43599b6f5d5ce074ec2fbbf8778680ee4..6d2c4e4ca1f5b4eed87643c7df763c9ea85fc902 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/ftrace_event.h>
 #include <linux/init.h>
 #include <linux/kallsyms.h>
+#include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/sysfs.h>
 #include <linux/kernel.h>
@@ -2425,18 +2426,17 @@ static inline void kmemleak_load_module(const struct module *mod,
 #endif
 
 #ifdef CONFIG_MODULE_SIG
-static int module_sig_check(struct load_info *info,
-                           const void *mod, unsigned long *_len)
+static int module_sig_check(struct load_info *info)
 {
        int err = -ENOKEY;
-       unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
-       unsigned long len = *_len;
+       const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
+       const void *mod = info->hdr;
 
-       if (len > markerlen &&
-           memcmp(mod + len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
+       if (info->len > markerlen &&
+           memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
                /* We truncate the module to discard the signature */
-               *_len -= markerlen;
-               err = mod_verify_sig(mod, _len);
+               info->len -= markerlen;
+               err = mod_verify_sig(mod, &info->len);
        }
 
        if (!err) {
@@ -2454,59 +2454,97 @@ static int module_sig_check(struct load_info *info,
        return err;
 }
 #else /* !CONFIG_MODULE_SIG */
-static int module_sig_check(struct load_info *info,
-                           void *mod, unsigned long *len)
+static int module_sig_check(struct load_info *info)
 {
        return 0;
 }
 #endif /* !CONFIG_MODULE_SIG */
 
-/* Sets info->hdr, info->len and info->sig_ok. */
-static int copy_and_check(struct load_info *info,
-                         const void __user *umod, unsigned long len,
-                         const char __user *uargs)
+/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
+static int elf_header_check(struct load_info *info)
 {
-       int err;
-       Elf_Ehdr *hdr;
+       if (info->len < sizeof(*(info->hdr)))
+               return -ENOEXEC;
+
+       if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
+           || info->hdr->e_type != ET_REL
+           || !elf_check_arch(info->hdr)
+           || info->hdr->e_shentsize != sizeof(Elf_Shdr))
+               return -ENOEXEC;
+
+       if (info->hdr->e_shoff >= info->len
+           || (info->hdr->e_shnum * sizeof(Elf_Shdr) >
+               info->len - info->hdr->e_shoff))
+               return -ENOEXEC;
 
-       if (len < sizeof(*hdr))
+       return 0;
+}
+
+/* Sets info->hdr and info->len. */
+static int copy_module_from_user(const void __user *umod, unsigned long len,
+                                 struct load_info *info)
+{
+       info->len = len;
+       if (info->len < sizeof(*(info->hdr)))
                return -ENOEXEC;
 
        /* Suck in entire file: we'll want most of it. */
-       if ((hdr = vmalloc(len)) == NULL)
+       info->hdr = vmalloc(info->len);
+       if (!info->hdr)
                return -ENOMEM;
 
-       if (copy_from_user(hdr, umod, len) != 0) {
-               err = -EFAULT;
-               goto free_hdr;
+       if (copy_from_user(info->hdr, umod, info->len) != 0) {
+               vfree(info->hdr);
+               return -EFAULT;
        }
 
-       err = module_sig_check(info, hdr, &len);
+       return 0;
+}
+
+/* Sets info->hdr and info->len. */
+static int copy_module_from_fd(int fd, struct load_info *info)
+{
+       struct file *file;
+       int err;
+       struct kstat stat;
+       loff_t pos;
+       ssize_t bytes = 0;
+
+       file = fget(fd);
+       if (!file)
+               return -ENOEXEC;
+
+       err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
        if (err)
-               goto free_hdr;
+               goto out;
 
-       /* Sanity checks against insmoding binaries or wrong arch,
-          weird elf version */
-       if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
-           || hdr->e_type != ET_REL
-           || !elf_check_arch(hdr)
-           || hdr->e_shentsize != sizeof(Elf_Shdr)) {
-               err = -ENOEXEC;
-               goto free_hdr;
+       if (stat.size > INT_MAX) {
+               err = -EFBIG;
+               goto out;
        }
-
-       if (hdr->e_shoff >= len ||
-           hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) {
-               err = -ENOEXEC;
-               goto free_hdr;
+       info->hdr = vmalloc(stat.size);
+       if (!info->hdr) {
+               err = -ENOMEM;
+               goto out;
        }
 
-       info->hdr = hdr;
-       info->len = len;
-       return 0;
+       pos = 0;
+       while (pos < stat.size) {
+               bytes = kernel_read(file, pos, (char *)(info->hdr) + pos,
+                                   stat.size - pos);
+               if (bytes < 0) {
+                       vfree(info->hdr);
+                       err = bytes;
+                       goto out;
+               }
+               if (bytes == 0)
+                       break;
+               pos += bytes;
+       }
+       info->len = pos;
 
-free_hdr:
-       vfree(hdr);
+out:
+       fput(file);
        return err;
 }
 
@@ -2945,33 +2983,123 @@ static bool finished_loading(const char *name)
        return ret;
 }
 
+/* Call module constructors. */
+static void do_mod_ctors(struct module *mod)
+{
+#ifdef CONFIG_CONSTRUCTORS
+       unsigned long i;
+
+       for (i = 0; i < mod->num_ctors; i++)
+               mod->ctors[i]();
+#endif
+}
+
+/* This is where the real work happens */
+static int do_init_module(struct module *mod)
+{
+       int ret = 0;
+
+       blocking_notifier_call_chain(&module_notify_list,
+                       MODULE_STATE_COMING, mod);
+
+       /* Set RO and NX regions for core */
+       set_section_ro_nx(mod->module_core,
+                               mod->core_text_size,
+                               mod->core_ro_size,
+                               mod->core_size);
+
+       /* Set RO and NX regions for init */
+       set_section_ro_nx(mod->module_init,
+                               mod->init_text_size,
+                               mod->init_ro_size,
+                               mod->init_size);
+
+       do_mod_ctors(mod);
+       /* Start the module */
+       if (mod->init != NULL)
+               ret = do_one_initcall(mod->init);
+       if (ret < 0) {
+               /* Init routine failed: abort.  Try to protect us from
+                   buggy refcounters. */
+               mod->state = MODULE_STATE_GOING;
+               synchronize_sched();
+               module_put(mod);
+               blocking_notifier_call_chain(&module_notify_list,
+                                            MODULE_STATE_GOING, mod);
+               free_module(mod);
+               wake_up_all(&module_wq);
+               return ret;
+       }
+       if (ret > 0) {
+               printk(KERN_WARNING
+"%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n"
+"%s: loading module anyway...\n",
+                      __func__, mod->name, ret,
+                      __func__);
+               dump_stack();
+       }
+
+       /* Now it's a first class citizen! */
+       mod->state = MODULE_STATE_LIVE;
+       blocking_notifier_call_chain(&module_notify_list,
+                                    MODULE_STATE_LIVE, mod);
+
+       /* We need to finish all async code before the module init sequence is done */
+       async_synchronize_full();
+
+       mutex_lock(&module_mutex);
+       /* Drop initial reference. */
+       module_put(mod);
+       trim_init_extable(mod);
+#ifdef CONFIG_KALLSYMS
+       mod->num_symtab = mod->core_num_syms;
+       mod->symtab = mod->core_symtab;
+       mod->strtab = mod->core_strtab;
+#endif
+       unset_module_init_ro_nx(mod);
+       module_free(mod, mod->module_init);
+       mod->module_init = NULL;
+       mod->init_size = 0;
+       mod->init_ro_size = 0;
+       mod->init_text_size = 0;
+       mutex_unlock(&module_mutex);
+       wake_up_all(&module_wq);
+
+       return 0;
+}
+
+static int may_init_module(void)
+{
+       if (!capable(CAP_SYS_MODULE) || modules_disabled)
+               return -EPERM;
+
+       return 0;
+}
+
 /* Allocate and load the module: note that size of section 0 is always
    zero, and we rely on this for optional sections. */
-static struct module *load_module(void __user *umod,
-                                 unsigned long len,
-                                 const char __user *uargs)
+static int load_module(struct load_info *info, const char __user *uargs)
 {
-       struct load_info info = { NULL, };
        struct module *mod, *old;
        long err;
 
-       pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
-              umod, len, uargs);
+       err = module_sig_check(info);
+       if (err)
+               goto free_copy;
 
-       /* Copy in the blobs from userspace, check they are vaguely sane. */
-       err = copy_and_check(&info, umod, len, uargs);
+       err = elf_header_check(info);
        if (err)
-               return ERR_PTR(err);
+               goto free_copy;
 
        /* Figure out module layout, and allocate all the memory. */
-       mod = layout_and_allocate(&info);
+       mod = layout_and_allocate(info);
        if (IS_ERR(mod)) {
                err = PTR_ERR(mod);
                goto free_copy;
        }
 
 #ifdef CONFIG_MODULE_SIG
-       mod->sig_ok = info.sig_ok;
+       mod->sig_ok = info->sig_ok;
        if (!mod->sig_ok)
                add_taint_module(mod, TAINT_FORCED_MODULE);
 #endif
@@ -2983,25 +3111,25 @@ static struct module *load_module(void __user *umod,
 
        /* Now we've got everything in the final locations, we can
         * find optional sections. */
-       find_module_sections(mod, &info);
+       find_module_sections(mod, info);
 
        err = check_module_license_and_versions(mod);
        if (err)
                goto free_unload;
 
        /* Set up MODINFO_ATTR fields */
-       setup_modinfo(mod, &info);
+       setup_modinfo(mod, info);
 
        /* Fix up syms, so that st_value is a pointer to location. */
-       err = simplify_symbols(mod, &info);
+       err = simplify_symbols(mod, info);
        if (err < 0)
                goto free_modinfo;
 
-       err = apply_relocations(mod, &info);
+       err = apply_relocations(mod, info);
        if (err < 0)
                goto free_modinfo;
 
-       err = post_relocation(mod, &info);
+       err = post_relocation(mod, info);
        if (err < 0)
                goto free_modinfo;
 
@@ -3041,14 +3169,14 @@ again:
        }
 
        /* This has to be done once we're sure module name is unique. */
-       dynamic_debug_setup(info.debug, info.num_debug);
+       dynamic_debug_setup(info->debug, info->num_debug);
 
        /* Find duplicate symbols */
        err = verify_export_symbols(mod);
        if (err < 0)
                goto ddebug;
 
-       module_bug_finalize(info.hdr, info.sechdrs, mod);
+       module_bug_finalize(info->hdr, info->sechdrs, mod);
        list_add_rcu(&mod->list, &modules);
        mutex_unlock(&module_mutex);
 
@@ -3059,16 +3187,17 @@ again:
                goto unlink;
 
        /* Link in to syfs. */
-       err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);
+       err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
        if (err < 0)
                goto unlink;
 
        /* Get rid of temporary copy. */
-       free_copy(&info);
+       free_copy(info);
 
        /* Done! */
        trace_module_load(mod);
-       return mod;
+
+       return do_init_module(mod);
 
  unlink:
        mutex_lock(&module_mutex);
@@ -3077,7 +3206,7 @@ again:
        module_bug_cleanup(mod);
        wake_up_all(&module_wq);
  ddebug:
-       dynamic_debug_remove(info.debug);
+       dynamic_debug_remove(info->debug);
  unlock:
        mutex_unlock(&module_mutex);
        synchronize_sched();
@@ -3089,106 +3218,48 @@ again:
  free_unload:
        module_unload_free(mod);
  free_module:
-       module_deallocate(mod, &info);
+       module_deallocate(mod, info);
  free_copy:
-       free_copy(&info);
-       return ERR_PTR(err);
-}
-
-/* Call module constructors. */
-static void do_mod_ctors(struct module *mod)
-{
-#ifdef CONFIG_CONSTRUCTORS
-       unsigned long i;
-
-       for (i = 0; i < mod->num_ctors; i++)
-               mod->ctors[i]();
-#endif
+       free_copy(info);
+       return err;
 }
 
-/* This is where the real work happens */
 SYSCALL_DEFINE3(init_module, void __user *, umod,
                unsigned long, len, const char __user *, uargs)
 {
-       struct module *mod;
-       int ret = 0;
-
-       /* Must have permission */
-       if (!capable(CAP_SYS_MODULE) || modules_disabled)
-               return -EPERM;
+       int err;
+       struct load_info info = { };
 
-       /* Do all the hard work */
-       mod = load_module(umod, len, uargs);
-       if (IS_ERR(mod))
-               return PTR_ERR(mod);
+       err = may_init_module();
+       if (err)
+               return err;
 
-       blocking_notifier_call_chain(&module_notify_list,
-                       MODULE_STATE_COMING, mod);
+       pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
+              umod, len, uargs);
 
-       /* Set RO and NX regions for core */
-       set_section_ro_nx(mod->module_core,
-                               mod->core_text_size,
-                               mod->core_ro_size,
-                               mod->core_size);
+       err = copy_module_from_user(umod, len, &info);
+       if (err)
+               return err;
 
-       /* Set RO and NX regions for init */
-       set_section_ro_nx(mod->module_init,
-                               mod->init_text_size,
-                               mod->init_ro_size,
-                               mod->init_size);
+       return load_module(&info, uargs);
+}
 
-       do_mod_ctors(mod);
-       /* Start the module */
-       if (mod->init != NULL)
-               ret = do_one_initcall(mod->init);
-       if (ret < 0) {
-               /* Init routine failed: abort.  Try to protect us from
-                   buggy refcounters. */
-               mod->state = MODULE_STATE_GOING;
-               synchronize_sched();
-               module_put(mod);
-               blocking_notifier_call_chain(&module_notify_list,
-                                            MODULE_STATE_GOING, mod);
-               free_module(mod);
-               wake_up_all(&module_wq);
-               return ret;
-       }
-       if (ret > 0) {
-               printk(KERN_WARNING
-"%s: '%s'->init suspiciously returned %d, it should follow 0/-E convention\n"
-"%s: loading module anyway...\n",
-                      __func__, mod->name, ret,
-                      __func__);
-               dump_stack();
-       }
+SYSCALL_DEFINE2(finit_module, int, fd, const char __user *, uargs)
+{
+       int err;
+       struct load_info info = { };
 
-       /* Now it's a first class citizen! */
-       mod->state = MODULE_STATE_LIVE;
-       blocking_notifier_call_chain(&module_notify_list,
-                                    MODULE_STATE_LIVE, mod);
+       err = may_init_module();
+       if (err)
+               return err;
 
-       /* We need to finish all async code before the module init sequence is done */
-       async_synchronize_full();
+       pr_debug("finit_module: fd=%d, uargs=%p\n", fd, uargs);
 
-       mutex_lock(&module_mutex);
-       /* Drop initial reference. */
-       module_put(mod);
-       trim_init_extable(mod);
-#ifdef CONFIG_KALLSYMS
-       mod->num_symtab = mod->core_num_syms;
-       mod->symtab = mod->core_symtab;
-       mod->strtab = mod->core_strtab;
-#endif
-       unset_module_init_ro_nx(mod);
-       module_free(mod, mod->module_init);
-       mod->module_init = NULL;
-       mod->init_size = 0;
-       mod->init_ro_size = 0;
-       mod->init_text_size = 0;
-       mutex_unlock(&module_mutex);
-       wake_up_all(&module_wq);
+       err = copy_module_from_fd(fd, &info);
+       if (err)
+               return err;
 
-       return 0;
+       return load_module(&info, uargs);
 }
 
 static inline int within(unsigned long addr, void *start, unsigned long size)