| From f299d5bce82e7bd9941aa13337a3e22eaf4fac51 Mon Sep 17 00:00:00 2001 |
| From: Dmitry Torokhov <dtor@chromium.org> |
| Date: Sun, 14 Nov 2021 14:25:27 -0800 |
| Subject: [PATCH] kmod: add support for passing compressed modules directly |
| into kernel |
| |
| Kernel's load pinning facilities interfere with loading compressed |
| modules, because if decompression happens in userspace and module is |
| instantiated via init_module() call there is no way for kernel to |
| ascertain that module data is coming from a trusted source. To solve |
| this issue module decompression support was added to the kernel. |
| |
| Let's use the new kernel facility, and if kernel indicates (via |
| /sys/module/compression) that that it can handle certain compression |
| scheme, then use finit_module() and pass a file descriptor to the |
| compressed module instead of init_module() with decompressed data. |
| --- |
| |
| This is a backport to kmod-25 branch. The original version can be |
| found at: |
| |
| https://lore.kernel.org/linux-modules/YbLo8wjBWDxwICf1@google.com/ |
| |
| libkmod/libkmod-file.c | 34 ++++++++++++++++++--------------- |
| libkmod/libkmod-internal.h | 8 +++++++- |
| libkmod/libkmod-module.c | 39 +++++++++++++++++++++++++++++++++++++- |
| shared/missing.h | 4 ++++ |
| 4 files changed, 68 insertions(+), 17 deletions(-) |
| |
| diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c |
| index 5eeba6a..7840c18 100644 |
| --- a/libkmod/libkmod-file.c |
| +++ b/libkmod/libkmod-file.c |
| @@ -45,14 +45,11 @@ struct file_ops { |
| }; |
| |
| struct kmod_file { |
| -#ifdef ENABLE_XZ |
| - bool xz_used; |
| -#endif |
| + enum kmod_compression compression; |
| #ifdef ENABLE_ZLIB |
| gzFile gzf; |
| #endif |
| int fd; |
| - bool direct; |
| off_t size; |
| void *memory; |
| const struct file_ops *ops; |
| @@ -131,7 +128,7 @@ static int xz_uncompress(lzma_stream *strm, struct kmod_file *file) |
| goto out; |
| } |
| } |
| - file->xz_used = true; |
| + file->compression = KMOD_COMPRESSION_XZ; |
| file->memory = p; |
| file->size = total; |
| return 0; |
| @@ -161,7 +158,7 @@ static int load_xz(struct kmod_file *file) |
| |
| static void unload_xz(struct kmod_file *file) |
| { |
| - if (!file->xz_used) |
| + if (file->compression != KMOD_COMPRESSION_XZ) |
| return; |
| free(file->memory); |
| } |
| @@ -173,15 +170,20 @@ static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0}; |
| #define READ_STEP (4 * 1024 * 1024) |
| static int load_zlib(struct kmod_file *file) |
| { |
| + int gzf_fd; |
| int err = 0; |
| off_t did = 0, total = 0; |
| _cleanup_free_ unsigned char *p = NULL; |
| |
| errno = 0; |
| - file->gzf = gzdopen(file->fd, "rb"); |
| + |
| + gzf_fd = dup(file->fd); |
| + if (gzf_fd < 0) |
| + return -errno; |
| + |
| + file->gzf = gzdopen(gzf_fd, "rb"); |
| if (file->gzf == NULL) |
| return -errno; |
| - file->fd = -1; /* now owned by gzf due gzdopen() */ |
| |
| for (;;) { |
| int r; |
| @@ -212,22 +214,24 @@ static int load_zlib(struct kmod_file *file) |
| did += r; |
| } |
| |
| + file->compression = KMOD_COMPRESSION_GZIP; |
| file->memory = p; |
| file->size = did; |
| p = NULL; |
| return 0; |
| |
| error: |
| - gzclose(file->gzf); |
| + if (file->gzf) |
| + gzclose(file->gzf); |
| return err; |
| } |
| |
| static void unload_zlib(struct kmod_file *file) |
| { |
| - if (file->gzf == NULL) |
| + if (file->compression != KMOD_COMPRESSION_GZIP) |
| return; |
| free(file->memory); |
| - gzclose(file->gzf); /* closes file->fd */ |
| + gzclose(file->gzf); |
| } |
| |
| static const char magic_zlib[] = {0x1f, 0x8b}; |
| @@ -259,7 +263,8 @@ static int load_reg(struct kmod_file *file) |
| file->fd, 0); |
| if (file->memory == MAP_FAILED) |
| return -errno; |
| - file->direct = true; |
| + |
| + file->compression = KMOD_COMPRESSION_NONE; |
| return 0; |
| } |
| |
| @@ -303,7 +308,6 @@ 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; |
| @@ -357,9 +361,9 @@ off_t kmod_file_get_size(const struct kmod_file *file) |
| return file->size; |
| } |
| |
| -bool kmod_file_get_direct(const struct kmod_file *file) |
| +enum kmod_compression kmod_file_get_compression(const struct kmod_file *file) |
| { |
| - return file->direct; |
| + return file->compression; |
| } |
| |
| int kmod_file_get_fd(const struct kmod_file *file) |
| diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h |
| index 346579c..93eefac 100644 |
| --- a/libkmod/libkmod-internal.h |
| +++ b/libkmod/libkmod-internal.h |
| @@ -46,6 +46,12 @@ static _always_inline_ _printf_format_(2, 3) void |
| # endif |
| #endif |
| |
| +enum kmod_compression { |
| + KMOD_COMPRESSION_NONE, |
| + KMOD_COMPRESSION_GZIP, |
| + KMOD_COMPRESSION_XZ, |
| +}; |
| + |
| void kmod_log(const struct kmod_ctx *ctx, |
| int priority, const char *file, int line, const char *fn, |
| const char *format, ...) __attribute__((format(printf, 6, 7))) __attribute__((nonnull(1, 3, 5))); |
| @@ -151,7 +157,7 @@ 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))); |
| +enum kmod_compression kmod_file_get_compression(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))); |
| |
| diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c |
| index 0a3ef11..ff8d438 100644 |
| --- a/libkmod/libkmod-module.c |
| +++ b/libkmod/libkmod-module.c |
| @@ -796,6 +796,37 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, |
| return err; |
| } |
| |
| +static enum kmod_compression kmod_get_supported_compression(struct kmod_module *mod) |
| +{ |
| + static const char path[] = "/sys/module/compression"; |
| + char buf[16]; |
| + int err; |
| + int fd; |
| + |
| + fd = open(path, O_RDONLY|O_CLOEXEC); |
| + if (fd < 0) { |
| + DBG(mod->ctx, "could not open '%s': %s\n", |
| + path, strerror(errno)); |
| + goto fail; |
| + } |
| + |
| + err = read_str_safe(fd, buf, sizeof(buf)); |
| + close(fd); |
| + if (err < 0) { |
| + ERR(mod->ctx, "could not read from '%s': %s\n", |
| + path, strerror(-err)); |
| + goto fail; |
| + } |
| + |
| + if (streq(buf, "gzip\n")) |
| + return KMOD_COMPRESSION_GZIP; |
| + else if (streq(buf, "xz\n")) |
| + return KMOD_COMPRESSION_XZ; |
| + |
| +fail: |
| + return KMOD_COMPRESSION_NONE; |
| +} |
| + |
| extern long init_module(const void *mem, unsigned long len, const char *args); |
| |
| /** |
| @@ -823,6 +854,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, |
| struct kmod_elf *elf; |
| const char *path; |
| const char *args = options ? options : ""; |
| + enum kmod_compression compression; |
| |
| if (mod == NULL) |
| return -ENOENT; |
| @@ -841,7 +873,9 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, |
| } |
| } |
| |
| - if (kmod_file_get_direct(mod->file)) { |
| + compression = kmod_file_get_compression(mod->file); |
| + if (compression == KMOD_COMPRESSION_NONE || |
| + compression == kmod_get_supported_compression(mod)) { |
| unsigned int kernel_flags = 0; |
| |
| if (flags & KMOD_INSERT_FORCE_VERMAGIC) |
| @@ -849,6 +883,9 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, |
| if (flags & KMOD_INSERT_FORCE_MODVERSION) |
| kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS; |
| |
| + if (compression != KMOD_COMPRESSION_NONE) |
| + kernel_flags |= MODULE_INIT_COMPRESSED_FILE; |
| + |
| err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags); |
| if (err == 0 || errno != ENOSYS) |
| goto init_finished; |
| diff --git a/shared/missing.h b/shared/missing.h |
| index 4c0d136..2629444 100644 |
| --- a/shared/missing.h |
| +++ b/shared/missing.h |
| @@ -15,6 +15,10 @@ |
| # define MODULE_INIT_IGNORE_VERMAGIC 2 |
| #endif |
| |
| +#ifndef MODULE_INIT_COMPRESSED_FILE |
| +# define MODULE_INIT_COMPRESSED_FILE 4 |
| +#endif |
| + |
| #ifndef __NR_finit_module |
| # define __NR_finit_module -1 |
| #endif |
| -- |
| 2.34.1.173.g76aa8bc2d0-goog |
| |