libkmod: add finit_module logic
authorKees Cook <keescook@chromium.org>
Mon, 18 Feb 2013 20:02:32 +0000 (12:02 -0800)
committerLucas De Marchi <lucas.de.marchi@gmail.com>
Tue, 19 Feb 2013 22:19:51 +0000 (19:19 -0300)
When a module is being loaded directly from disk (no compression, etc),
pass the file descriptor to the new finit_module() syscall. If the
finit_module syscall is exported by the kernel syscall headers, use it.
Additionally, if the kernel's module.h file is available, map kmod flags
to finit_module flags.

configure.ac
libkmod/libkmod-file.c
libkmod/libkmod-module.c
libkmod/libkmod-private.h

index 0f86c25..566b317 100644 (file)
@@ -43,6 +43,9 @@ AC_CHECK_FUNCS_ONCE(__xstat)
 # dietlibc doesn't have st.st_mtim struct member
 AC_CHECK_MEMBERS([struct stat.st_mtim], [], [], [#include <sys/stat.h>])
 
+# Check kernel headers
+AC_CHECK_HEADERS_ONCE([linux/module.h])
+
 
 #####################################################################
 # --with-
index ced20a8..219c63b 100644 (file)
@@ -52,6 +52,7 @@ struct kmod_file {
        gzFile gzf;
 #endif
        int fd;
+       bool direct;
        off_t size;
        void *memory;
        const struct file_ops *ops;
@@ -257,6 +258,7 @@ static int load_reg(struct kmod_file *file)
        file->memory = mmap(0, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
        if (file->memory == MAP_FAILED)
                return -errno;
+       file->direct = true;
        return 0;
 }
 
@@ -300,6 +302,7 @@ struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
                        magic_size_max = itr->magic_size;
        }
 
+       file->direct = false;
        if (magic_size_max > 0) {
                char *buf = alloca(magic_size_max + 1);
                ssize_t sz;
@@ -353,6 +356,16 @@ off_t kmod_file_get_size(const struct kmod_file *file)
        return file->size;
 }
 
+bool kmod_file_get_direct(const struct kmod_file *file)
+{
+       return file->direct;
+}
+
+int kmod_file_get_fd(const struct kmod_file *file)
+{
+       return file->fd;
+}
+
 void kmod_file_unref(struct kmod_file *file)
 {
        if (file->elf)
index b1d40b1..7b38e64 100644 (file)
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <sys/syscall.h>
 #include <sys/wait.h>
 #include <string.h>
 #include <fnmatch.h>
 
+#ifdef HAVE_LINUX_MODULE_H
+#include <linux/module.h>
+#endif
+
 #include "libkmod.h"
 #include "libkmod-private.h"
 
@@ -763,6 +768,14 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod,
 
 extern long init_module(const void *mem, unsigned long len, const char *args);
 
+#ifndef __NR_finit_module
+# define __NR_finit_module -1
+#endif
+static inline int finit_module(int fd, const char *uargs, int flags)
+{
+   return syscall(__NR_finit_module, fd, uargs, flags);
+}
+
 /**
  * kmod_module_insert_module:
  * @mod: kmod module
@@ -803,6 +816,21 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
                return err;
        }
 
+       if (kmod_file_get_direct(file)) {
+               unsigned int kernel_flags = 0;
+
+#ifdef HAVE_LINUX_MODULE_H
+               if (flags & KMOD_INSERT_FORCE_VERMAGIC)
+                       kernel_flags |= MODULE_INIT_IGNORE_VERMAGIC;
+               if (flags & KMOD_INSERT_FORCE_MODVERSION)
+                       kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS;
+#endif
+
+               err = finit_module(kmod_file_get_fd(file), args, kernel_flags);
+               if (err == 0 || errno != ENOSYS)
+                       goto init_finished;
+       }
+
        size = kmod_file_get_size(file);
        mem = kmod_file_get_contents(file);
 
@@ -829,6 +857,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
        }
 
        err = init_module(mem, size, args);
+init_finished:
        if (err < 0) {
                err = -errno;
                INFO(mod->ctx, "Failed to insert module '%s': %m\n", path);
index b472c62..7748b14 100644 (file)
@@ -142,6 +142,8 @@ struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filenam
 struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) __attribute__((nonnull(1)));
 void *kmod_file_get_contents(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
 off_t kmod_file_get_size(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+bool kmod_file_get_direct(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+int kmod_file_get_fd(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
 void kmod_file_unref(struct kmod_file *file) __attribute__((nonnull(1)));
 
 /* libkmod-elf.c */