* [PATCH] kmod: add support for passing compressed modules directly into kernel
@ 2021-12-10 5:43 Dmitry Torokhov
0 siblings, 0 replies; only message in thread
From: Dmitry Torokhov @ 2021-12-10 5:43 UTC (permalink / raw)
To: Luis Chamberlain, Jessica Yu; +Cc: linux-modules
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.
---
Note: this is a companion piece to the kernel patch that can be found
at https://lore.kernel.org/lkml/YbLnAeaKW4HdtRWL@google.com/
libkmod/libkmod-file.c | 41 +++++++++++++++++++-------------------
libkmod/libkmod-internal.h | 9 ++++++++-
libkmod/libkmod-module.c | 41 +++++++++++++++++++++++++++++++++++++-
shared/missing.h | 4 ++++
4 files changed, 73 insertions(+), 22 deletions(-)
diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
index b6a8cc9..9f1b99e 100644
--- a/libkmod/libkmod-file.c
+++ b/libkmod/libkmod-file.c
@@ -48,17 +48,11 @@ struct file_ops {
};
struct kmod_file {
-#ifdef ENABLE_ZSTD
- bool zstd_used;
-#endif
-#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;
@@ -179,7 +173,7 @@ static int load_zstd(struct kmod_file *file)
ZSTD_freeDStream(dstr);
free((void *)zst_inb.src);
- file->zstd_used = true;
+ file->compression = KMOD_COMPRESSION_ZSTD;
file->memory = zst_outb.dst;
file->size = zst_outb.pos;
return 0;
@@ -193,7 +187,7 @@ out:
static void unload_zstd(struct kmod_file *file)
{
- if (!file->zstd_used)
+ if (file->compression != KMOD_COMPRESSION_ZSTD)
return;
free(file->memory);
}
@@ -272,7 +266,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;
@@ -302,7 +296,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);
}
@@ -314,15 +308,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;
@@ -353,22 +352,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};
@@ -403,7 +404,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;
}
@@ -447,7 +449,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;
@@ -501,9 +502,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 c22644a..a8405b3 100644
--- a/libkmod/libkmod-internal.h
+++ b/libkmod/libkmod-internal.h
@@ -48,6 +48,13 @@ static _always_inline_ _printf_format_(2, 3) void
# endif
#endif
+enum kmod_compression {
+ KMOD_COMPRESSION_NONE,
+ KMOD_COMPRESSION_GZIP,
+ KMOD_COMPRESSION_XZ,
+ KMOD_COMPRESSION_ZSTD,
+};
+
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)));
@@ -155,7 +162,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 6e0ff1a..34cb7f0 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -802,6 +802,39 @@ 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;
+ else if (streq(buf, "zstd\n"))
+ return KMOD_COMPRESSION_ZSTD;
+
+fail:
+ return KMOD_COMPRESSION_NONE;
+}
+
extern long init_module(const void *mem, unsigned long len, const char *args);
/**
@@ -829,6 +862,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;
@@ -847,7 +881,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)
@@ -855,6 +891,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
--
Dmitry
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2021-12-10 5:43 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-12-10 5:43 [PATCH] kmod: add support for passing compressed modules directly into kernel Dmitry Torokhov
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.