When possible, load modules via finit_module() syscall.

This uses a bit of a hack to call the new finit_module() syscall, but I'm
not sure what a better solution should be. If ENOSYS is seen for it, it
just continues and attempts init_module() as before.

Signed-off-by: Kees Cook <[email protected]>
---
 libkmod/libkmod-file.c    |   13 +++++++++++++
 libkmod/libkmod-module.c  |   32 ++++++++++++++++++++++++++++++++
 libkmod/libkmod-private.h |    2 ++
 testsuite/init_module.c   |   24 ++++++++++++++++++++++++
 4 files changed, 71 insertions(+)

diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
index ced20a8..4a3bdee 100644
--- a/libkmod/libkmod-file.c
+++ b/libkmod/libkmod-file.c
@@ -52,6 +52,7 @@ struct kmod_file {
        gzFile gzf;
 #endif
        int fd;
+       int 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 = 1;
        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 = 0;
        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;
 }
 
+int 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)
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index b1d40b1..feb8b8c 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -31,6 +31,7 @@
 #include <limits.h>
 #include <dirent.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/wait.h>
@@ -762,6 +763,28 @@ 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
+# if defined(__x86_64__)
+#  define __NR_finit_module 313
+# elif defined(__i386__)
+#  define __NR_finit_module 350
+# elif defined(__arm__)
+#  define __NR_finit_module 379
+# endif
+#endif
+
+#ifdef __NR_finit_module
+static inline int finit_module(int fd, const char *uargs, int flags)
+{
+   return syscall(__NR_finit_module, fd, uargs, flags);
+}
+#else
+static inline int finit_module(int fd, const char *uargs, int flags)
+{
+   errno = ENOSYS;
+   return -1;
+}
+#endif
 
 /**
  * kmod_module_insert_module:
@@ -803,6 +826,14 @@ KMOD_EXPORT int kmod_module_insert_module(struct 
kmod_module *mod,
                return err;
        }
 
+       if (kmod_file_get_direct(file)) {
+               err = finit_module(kmod_file_get_fd(file), args,
+                                  flags & (KMOD_INSERT_FORCE_VERMAGIC |
+                                           KMOD_INSERT_FORCE_MODVERSION));
+               if (err == 0 || errno != ENOSYS)
+                       goto init_finished;
+       }
+
        size = kmod_file_get_size(file);
        mem = kmod_file_get_contents(file);
 
@@ -829,6 +860,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);
diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h
index b472c62..c765003 100644
--- a/libkmod/libkmod-private.h
+++ b/libkmod/libkmod-private.h
@@ -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)));
+int 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 */
diff --git a/testsuite/init_module.c b/testsuite/init_module.c
index c4d7efb..d1bca9d 100644
--- a/testsuite/init_module.c
+++ b/testsuite/init_module.c
@@ -28,6 +28,7 @@
 #include <stddef.h>
 #include <string.h>
 #include <stdio.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -274,6 +275,29 @@ long init_module(void *mem, unsigned long len, const char 
*args)
        return err;
 }
 
+TS_EXPORT int finit_module(const int fd, const char *args, const int flags);
+
+int finit_module(const int fd, const char *args, const int flags)
+{
+       int err;
+       void *mem;
+       unsigned long len;
+       struct stat st;
+
+       if (fstat(fd, &st) < 0)
+               return -1;
+
+       len = st.st_size;
+       mem = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (mem == MAP_FAILED)
+               return -1;
+
+       err = init_module(mem, len, args);
+       munmap(mem, len);
+
+       return err;
+}
+
 /* the test is going away anyway, but lets keep valgrind happy */
 void free_resources(void) __attribute__((destructor));
 void free_resources(void)
-- 
1.7.9.5


-- 
Kees Cook
Chrome OS Security
--
To unsubscribe from this list: send the line "unsubscribe linux-modules" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to