When a module is being loaded directly from disk (no compression, etc), pass the file descriptor to the new finit_module() syscall. If finit_module is exported by glibc, use it. Otherwise, manually make the syscall on architectures where it is known to exist. Also update testsuite to expect the call, and fix callers of mmap() to use NULL instead of 0. Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx> --- configure.ac | 1 + libkmod/libkmod-file.c | 16 +++++++++++++++- libkmod/libkmod-index.c | 4 ++-- libkmod/libkmod-module.c | 29 +++++++++++++++++++++++++++++ libkmod/libkmod-private.h | 2 ++ testsuite/init_module.c | 24 ++++++++++++++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 0f86c25..faab6bd 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,7 @@ PKG_PROG_PKG_CONFIG ##################################################################### AC_CHECK_FUNCS_ONCE(__xstat) +AC_CHECK_FUNCS_ONCE(finit_module) # dietlibc doesn't have st.st_mtim struct member AC_CHECK_MEMBERS([struct stat.st_mtim], [], [], [#include <sys/stat.h>]) diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c index ced20a8..76585f5 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; @@ -254,9 +255,11 @@ static int load_reg(struct kmod_file *file) return -errno; file->size = st.st_size; - file->memory = mmap(0, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0); + file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, + file->fd, 0); if (file->memory == MAP_FAILED) return -errno; + file->direct = 1; return 0; } @@ -300,6 +303,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 +357,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-index.c b/libkmod/libkmod-index.c index 516240e..d386f00 100644 --- a/libkmod/libkmod-index.c +++ b/libkmod/libkmod-index.c @@ -801,9 +801,9 @@ struct index_mm *index_mm_open(struct kmod_ctx *ctx, const char *filename, if ((size_t) st.st_size < sizeof(hdr)) goto fail_nommap; - if ((idx->mm = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) + if ((idx->mm = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { - ERR(ctx, "mmap(0, %"PRIu64", PROT_READ, %d, MAP_PRIVATE, 0): %m\n", + ERR(ctx, "mmap(NULL, %"PRIu64", PROT_READ, %d, MAP_PRIVATE, 0): %m\n", st.st_size, fd); goto fail_nommap; } diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c index b1d40b1..31ad5fc 100644 --- a/libkmod/libkmod-module.c +++ b/libkmod/libkmod-module.c @@ -762,6 +762,26 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, } extern long init_module(const void *mem, unsigned long len, const char *args); +#ifdef HAVE_FINIT_MODULE +extern int finit_module(int fd, const char *uargs, int flags); +#else +# include <sys/syscall.h> +# 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 +# else +# define __NR_finit_module -1 +# endif +# endif +static inline int finit_module(int fd, const char *uargs, int flags) +{ + return syscall(__NR_finit_module, fd, uargs, flags); +} +#endif /** * kmod_module_insert_module: @@ -803,6 +823,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 +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); 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..ca9f84c 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(NULL, 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