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 <keescook@xxxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html