mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
Merge 763978ca67 ("Merge branch 'modules-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux") into android-mainline
Steps on the way to 5.17-rc1 Resolves conflicts with: kernel/module-internal.h Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I9cb3e542987a146f77cafca4fc9dbba476a78bc0
This commit is contained in:
@@ -10712,6 +10712,7 @@ F: samples/kmemleak/kmemleak-test.c
|
||||
KMOD KERNEL MODULE LOADER - USERMODE HELPER
|
||||
M: Luis Chamberlain <mcgrof@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
L: linux-modules@vger.kernel.org
|
||||
S: Maintained
|
||||
F: include/linux/kmod.h
|
||||
F: kernel/kmod.c
|
||||
@@ -13001,9 +13002,10 @@ F: drivers/media/dvb-frontends/mn88473*
|
||||
|
||||
MODULE SUPPORT
|
||||
M: Luis Chamberlain <mcgrof@kernel.org>
|
||||
M: Jessica Yu <jeyu@kernel.org>
|
||||
L: linux-modules@vger.kernel.org
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux.git modules-next
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git modules-next
|
||||
F: include/linux/module.h
|
||||
F: kernel/module.c
|
||||
|
||||
|
||||
2
fs/unicode/.gitignore
vendored
2
fs/unicode/.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/mkutf8data
|
||||
/utf8data.h
|
||||
/utf8data.c
|
||||
|
||||
@@ -290,7 +290,8 @@ extern typeof(name) __mod_##type##__##name##_device_table \
|
||||
* files require multiple MODULE_FIRMWARE() specifiers */
|
||||
#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware)
|
||||
|
||||
#define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns)
|
||||
#define _MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns)
|
||||
#define MODULE_IMPORT_NS(ns) _MODULE_IMPORT_NS(ns)
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
/* Flags for sys_finit_module: */
|
||||
#define MODULE_INIT_IGNORE_MODVERSIONS 1
|
||||
#define MODULE_INIT_IGNORE_VERMAGIC 2
|
||||
#define MODULE_INIT_COMPRESSED_FILE 4
|
||||
|
||||
#endif /* _UAPI_LINUX_MODULE_H */
|
||||
|
||||
13
init/Kconfig
13
init/Kconfig
@@ -2316,6 +2316,19 @@ config MODULE_COMPRESS_ZSTD
|
||||
|
||||
endchoice
|
||||
|
||||
config MODULE_DECOMPRESS
|
||||
bool "Support in-kernel module decompression"
|
||||
depends on MODULE_COMPRESS_GZIP || MODULE_COMPRESS_XZ
|
||||
select ZLIB_INFLATE if MODULE_COMPRESS_GZIP
|
||||
select XZ_DEC if MODULE_COMPRESS_XZ
|
||||
help
|
||||
|
||||
Support for decompressing kernel modules by the kernel itself
|
||||
instead of relying on userspace to perform this task. Useful when
|
||||
load pinning security policy is enabled.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
|
||||
bool "Allow loading of modules with missing namespace imports"
|
||||
help
|
||||
|
||||
@@ -67,6 +67,7 @@ obj-y += up.o
|
||||
endif
|
||||
obj-$(CONFIG_UID16) += uid16.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
obj-$(CONFIG_MODULE_DECOMPRESS) += module_decompress.o
|
||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
||||
obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o
|
||||
obj-$(CONFIG_MODULE_SIG_PROTECT) += gki_module.o
|
||||
|
||||
@@ -22,6 +22,11 @@ struct load_info {
|
||||
bool sig_ok;
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
unsigned long mod_kallsyms_init_off;
|
||||
#endif
|
||||
#ifdef CONFIG_MODULE_DECOMPRESS
|
||||
struct page **pages;
|
||||
unsigned int max_pages;
|
||||
unsigned int used_pages;
|
||||
#endif
|
||||
struct {
|
||||
unsigned int sym, str, mod, vers, info, pcpu;
|
||||
@@ -30,6 +35,20 @@ struct load_info {
|
||||
|
||||
extern int mod_verify_sig(const void *mod, struct load_info *info);
|
||||
|
||||
#ifdef CONFIG_MODULE_DECOMPRESS
|
||||
int module_decompress(struct load_info *info, const void *buf, size_t size);
|
||||
void module_decompress_cleanup(struct load_info *info);
|
||||
#else
|
||||
static inline int module_decompress(struct load_info *info,
|
||||
const void *buf, size_t size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline void module_decompress_cleanup(struct load_info *info)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MODULE_SIG_PROTECT
|
||||
extern bool gki_is_module_exported_symbol(const char *name);
|
||||
extern bool gki_is_module_protected_symbol(const char *name);
|
||||
|
||||
@@ -963,7 +963,6 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop the machine so refcounts can't move and disable module. */
|
||||
ret = try_stop_module(mod, flags, &forced);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
@@ -2913,12 +2912,13 @@ static int module_sig_check(struct load_info *info, int flags)
|
||||
const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
|
||||
const char *reason;
|
||||
const void *mod = info->hdr;
|
||||
|
||||
bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
|
||||
MODULE_INIT_IGNORE_VERMAGIC);
|
||||
/*
|
||||
* Require flags == 0, as a module with version information
|
||||
* removed is no longer the module that was signed
|
||||
* Do not allow mangled modules as a module with version information
|
||||
* removed is no longer the module that was signed.
|
||||
*/
|
||||
if (flags == 0 &&
|
||||
if (!mangled_module &&
|
||||
info->len > markerlen &&
|
||||
memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
|
||||
/* We truncate the module to discard the signature */
|
||||
@@ -3211,9 +3211,12 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void free_copy(struct load_info *info)
|
||||
static void free_copy(struct load_info *info, int flags)
|
||||
{
|
||||
vfree(info->hdr);
|
||||
if (flags & MODULE_INIT_COMPRESSED_FILE)
|
||||
module_decompress_cleanup(info);
|
||||
else
|
||||
vfree(info->hdr);
|
||||
}
|
||||
|
||||
static int rewrite_section_headers(struct load_info *info, int flags)
|
||||
@@ -4178,7 +4181,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
}
|
||||
|
||||
/* Get rid of temporary copy. */
|
||||
free_copy(info);
|
||||
free_copy(info, flags);
|
||||
|
||||
/* Done! */
|
||||
trace_module_load(mod);
|
||||
@@ -4227,7 +4230,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
|
||||
module_deallocate(mod, info);
|
||||
free_copy:
|
||||
free_copy(info);
|
||||
free_copy(info, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4254,7 +4257,8 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
|
||||
{
|
||||
struct load_info info = { };
|
||||
void *hdr = NULL;
|
||||
void *buf = NULL;
|
||||
int len;
|
||||
int err;
|
||||
|
||||
err = may_init_module();
|
||||
@@ -4264,15 +4268,24 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
|
||||
pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags);
|
||||
|
||||
if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS
|
||||
|MODULE_INIT_IGNORE_VERMAGIC))
|
||||
|MODULE_INIT_IGNORE_VERMAGIC
|
||||
|MODULE_INIT_COMPRESSED_FILE))
|
||||
return -EINVAL;
|
||||
|
||||
err = kernel_read_file_from_fd(fd, 0, &hdr, INT_MAX, NULL,
|
||||
len = kernel_read_file_from_fd(fd, 0, &buf, INT_MAX, NULL,
|
||||
READING_MODULE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
info.hdr = hdr;
|
||||
info.len = err;
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
if (flags & MODULE_INIT_COMPRESSED_FILE) {
|
||||
err = module_decompress(&info, buf, len);
|
||||
vfree(buf); /* compressed data is no longer needed */
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
info.hdr = buf;
|
||||
info.len = len;
|
||||
}
|
||||
|
||||
return load_module(&info, uargs, flags);
|
||||
}
|
||||
|
||||
271
kernel/module_decompress.c
Normal file
271
kernel/module_decompress.c
Normal file
@@ -0,0 +1,271 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright 2021 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "module-internal.h"
|
||||
|
||||
static int module_extend_max_pages(struct load_info *info, unsigned int extent)
|
||||
{
|
||||
struct page **new_pages;
|
||||
|
||||
new_pages = kvmalloc_array(info->max_pages + extent,
|
||||
sizeof(info->pages), GFP_KERNEL);
|
||||
if (!new_pages)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(new_pages, info->pages, info->max_pages * sizeof(info->pages));
|
||||
kvfree(info->pages);
|
||||
info->pages = new_pages;
|
||||
info->max_pages += extent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct page *module_get_next_page(struct load_info *info)
|
||||
{
|
||||
struct page *page;
|
||||
int error;
|
||||
|
||||
if (info->max_pages == info->used_pages) {
|
||||
error = module_extend_max_pages(info, info->used_pages);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
|
||||
if (!page)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
info->pages[info->used_pages++] = page;
|
||||
return page;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULE_COMPRESS_GZIP
|
||||
#include <linux/zlib.h>
|
||||
#define MODULE_COMPRESSION gzip
|
||||
#define MODULE_DECOMPRESS_FN module_gzip_decompress
|
||||
|
||||
/*
|
||||
* Calculate length of the header which consists of signature, header
|
||||
* flags, time stamp and operating system ID (10 bytes total), plus
|
||||
* an optional filename.
|
||||
*/
|
||||
static size_t module_gzip_header_len(const u8 *buf, size_t size)
|
||||
{
|
||||
const u8 signature[] = { 0x1f, 0x8b, 0x08 };
|
||||
size_t len = 10;
|
||||
|
||||
if (size < len || memcmp(buf, signature, sizeof(signature)))
|
||||
return 0;
|
||||
|
||||
if (buf[3] & 0x08) {
|
||||
do {
|
||||
/*
|
||||
* If we can't find the end of the file name we must
|
||||
* be dealing with a corrupted file.
|
||||
*/
|
||||
if (len == size)
|
||||
return 0;
|
||||
} while (buf[len++] != '\0');
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t module_gzip_decompress(struct load_info *info,
|
||||
const void *buf, size_t size)
|
||||
{
|
||||
struct z_stream_s s = { 0 };
|
||||
size_t new_size = 0;
|
||||
size_t gzip_hdr_len;
|
||||
ssize_t retval;
|
||||
int rc;
|
||||
|
||||
gzip_hdr_len = module_gzip_header_len(buf, size);
|
||||
if (!gzip_hdr_len) {
|
||||
pr_err("not a gzip compressed module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s.next_in = buf + gzip_hdr_len;
|
||||
s.avail_in = size - gzip_hdr_len;
|
||||
|
||||
s.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
|
||||
if (!s.workspace)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = zlib_inflateInit2(&s, -MAX_WBITS);
|
||||
if (rc != Z_OK) {
|
||||
pr_err("failed to initialize decompressor: %d\n", rc);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
struct page *page = module_get_next_page(info);
|
||||
if (!page) {
|
||||
retval = -ENOMEM;
|
||||
goto out_inflate_end;
|
||||
}
|
||||
|
||||
s.next_out = kmap(page);
|
||||
s.avail_out = PAGE_SIZE;
|
||||
rc = zlib_inflate(&s, 0);
|
||||
kunmap(page);
|
||||
|
||||
new_size += PAGE_SIZE - s.avail_out;
|
||||
} while (rc == Z_OK);
|
||||
|
||||
if (rc != Z_STREAM_END) {
|
||||
pr_err("decompression failed with status %d\n", rc);
|
||||
retval = -EINVAL;
|
||||
goto out_inflate_end;
|
||||
}
|
||||
|
||||
retval = new_size;
|
||||
|
||||
out_inflate_end:
|
||||
zlib_inflateEnd(&s);
|
||||
out:
|
||||
kfree(s.workspace);
|
||||
return retval;
|
||||
}
|
||||
#elif CONFIG_MODULE_COMPRESS_XZ
|
||||
#include <linux/xz.h>
|
||||
#define MODULE_COMPRESSION xz
|
||||
#define MODULE_DECOMPRESS_FN module_xz_decompress
|
||||
|
||||
static ssize_t module_xz_decompress(struct load_info *info,
|
||||
const void *buf, size_t size)
|
||||
{
|
||||
static const u8 signature[] = { 0xfd, '7', 'z', 'X', 'Z', 0 };
|
||||
struct xz_dec *xz_dec;
|
||||
struct xz_buf xz_buf;
|
||||
enum xz_ret xz_ret;
|
||||
size_t new_size = 0;
|
||||
ssize_t retval;
|
||||
|
||||
if (size < sizeof(signature) ||
|
||||
memcmp(buf, signature, sizeof(signature))) {
|
||||
pr_err("not an xz compressed module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1);
|
||||
if (!xz_dec)
|
||||
return -ENOMEM;
|
||||
|
||||
xz_buf.in_size = size;
|
||||
xz_buf.in = buf;
|
||||
xz_buf.in_pos = 0;
|
||||
|
||||
do {
|
||||
struct page *page = module_get_next_page(info);
|
||||
if (!page) {
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
xz_buf.out = kmap(page);
|
||||
xz_buf.out_pos = 0;
|
||||
xz_buf.out_size = PAGE_SIZE;
|
||||
xz_ret = xz_dec_run(xz_dec, &xz_buf);
|
||||
kunmap(page);
|
||||
|
||||
new_size += xz_buf.out_pos;
|
||||
} while (xz_buf.out_pos == PAGE_SIZE && xz_ret == XZ_OK);
|
||||
|
||||
if (xz_ret != XZ_STREAM_END) {
|
||||
pr_err("decompression failed with status %d\n", xz_ret);
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = new_size;
|
||||
|
||||
out:
|
||||
xz_dec_end(xz_dec);
|
||||
return retval;
|
||||
}
|
||||
#else
|
||||
#error "Unexpected configuration for CONFIG_MODULE_DECOMPRESS"
|
||||
#endif
|
||||
|
||||
int module_decompress(struct load_info *info, const void *buf, size_t size)
|
||||
{
|
||||
unsigned int n_pages;
|
||||
ssize_t data_size;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Start with number of pages twice as big as needed for
|
||||
* compressed data.
|
||||
*/
|
||||
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
|
||||
error = module_extend_max_pages(info, n_pages);
|
||||
|
||||
data_size = MODULE_DECOMPRESS_FN(info, buf, size);
|
||||
if (data_size < 0) {
|
||||
error = data_size;
|
||||
goto err;
|
||||
}
|
||||
|
||||
info->hdr = vmap(info->pages, info->used_pages, VM_MAP, PAGE_KERNEL);
|
||||
if (!info->hdr) {
|
||||
error = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
info->len = data_size;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
module_decompress_cleanup(info);
|
||||
return error;
|
||||
}
|
||||
|
||||
void module_decompress_cleanup(struct load_info *info)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (info->hdr)
|
||||
vunmap(info->hdr);
|
||||
|
||||
for (i = 0; i < info->used_pages; i++)
|
||||
__free_page(info->pages[i]);
|
||||
|
||||
kvfree(info->pages);
|
||||
|
||||
info->pages = NULL;
|
||||
info->max_pages = info->used_pages = 0;
|
||||
}
|
||||
|
||||
static ssize_t compression_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%s\n", __stringify(MODULE_COMPRESSION));
|
||||
}
|
||||
static struct kobj_attribute module_compression_attr = __ATTR_RO(compression);
|
||||
|
||||
static int __init module_decompress_sysfs_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = sysfs_create_file(&module_kset->kobj,
|
||||
&module_compression_attr.attr);
|
||||
if (error)
|
||||
pr_warn("Failed to create 'compression' attribute");
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(module_decompress_sysfs_init);
|
||||
Reference in New Issue
Block a user