mirror of
https://github.com/LineageOS/android_kernel_samsung_msm8226.git
synced 2026-09-28 00:00:22 +02:00
CHROMIUM: module: add syscall to load module from fd
Instead of (or in addition to) kernel module signing, being able to reason about the origin of a kernel module would be valuable in situations where an OS already trusts a specific file system, file, etc, due to things like security labels or an existing root of trust to a partition through things like dm-verity. This introduces a new syscall (currently only on x86), similar to init_module, that has only two arguments. The first argument is used as a file descriptor to the module and the second argument is a pointer to the NULL terminated string of module arguments. Signed-off-by: Kees Cook <keescook@chromium.org> [accepted into Rusty's modules-wip tree for linux-next: http://git.kernel.org/?p=linux/kernel/git/rusty/linux.git;a=shortlog;h=refs/heads/modules-wip] BUG=chromium-os:34134 TEST=parrot build, manual testing Change-Id: I51122d61d0085ed40d2ac9b542646b28d066d5e3 Reviewed-on: https://gerrit.chromium.org/gerrit/34302 Tested-by: Kees Cook <keescook@chromium.org> Reviewed-by: Olof Johansson <olofj@chromium.org> Commit-Ready: Kees Cook <keescook@chromium.org>
This commit is contained in:
committed by
Francescodario Cuzzocrea
parent
a79911a8c6
commit
58bb6cdba7
@@ -356,4 +356,5 @@
|
||||
347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv
|
||||
348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev
|
||||
355 i386 getrandom sys_getrandom
|
||||
350 i386 finit_module sys_finit_module
|
||||
356 i386 memfd_create sys_memfd_create
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
310 64 process_vm_readv sys_process_vm_readv
|
||||
311 64 process_vm_writev sys_process_vm_writev
|
||||
318 common getrandom sys_getrandom
|
||||
313 common finit_module sys_finit_module
|
||||
319 common memfd_create sys_memfd_create
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
|
||||
@@ -859,6 +859,7 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
|
||||
unsigned long riovcnt,
|
||||
unsigned long flags);
|
||||
|
||||
asmlinkage long sys_finit_module(int fd, const char __user *uargs);
|
||||
asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
|
||||
const char __user *uargs);
|
||||
|
||||
|
||||
+152
-72
@@ -21,6 +21,7 @@
|
||||
#include <linux/ftrace_event.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -2616,54 +2617,102 @@ static inline void kmemleak_load_module(const struct module *mod,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
|
||||
static int check_info(struct load_info *info)
|
||||
{
|
||||
if (info->len < sizeof(*(info->hdr)))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
|
||||
|| info->hdr->e_type != ET_REL
|
||||
|| !elf_check_arch(info->hdr)
|
||||
|| info->hdr->e_shentsize != sizeof(Elf_Shdr))
|
||||
return -ENOEXEC;
|
||||
|
||||
if (info->hdr->e_shoff >= info->len
|
||||
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
|
||||
info->len - info->hdr->e_shoff))
|
||||
return -ENOEXEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets info->hdr and info->len. */
|
||||
static int copy_and_check(struct load_info *info,
|
||||
const void __user *umod, unsigned long len,
|
||||
const char __user *uargs)
|
||||
int copy_module_from_user(const void __user *umod, unsigned long len,
|
||||
struct load_info *info)
|
||||
{
|
||||
int err;
|
||||
Elf_Ehdr *hdr;
|
||||
|
||||
if (len < sizeof(*hdr))
|
||||
info->len = len;
|
||||
if (info->len < sizeof(*(info->hdr)))
|
||||
return -ENOEXEC;
|
||||
|
||||
/* Suck in entire file: we'll want most of it. */
|
||||
if ((hdr = vmalloc(len)) == NULL)
|
||||
info->hdr = vmalloc(info->len);
|
||||
if (!info->hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(hdr, umod, len) != 0) {
|
||||
err = -EFAULT;
|
||||
err = copy_from_user(info->hdr, umod, info->len);
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
/* Sanity checks against insmoding binaries or wrong arch,
|
||||
weird elf version */
|
||||
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
||||
|| hdr->e_type != ET_REL
|
||||
|| !elf_check_arch(hdr)
|
||||
|| hdr->e_shentsize != sizeof(Elf_Shdr)) {
|
||||
err = -ENOEXEC;
|
||||
err = check_info(info);
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TIMA_LKMAUTH
|
||||
if (lkmauth(hdr, len) != 0) {
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
#endif
|
||||
|
||||
info->hdr = hdr;
|
||||
info->len = len;
|
||||
return 0;
|
||||
return err;
|
||||
|
||||
free_hdr:
|
||||
vfree(hdr);
|
||||
vfree(info->hdr);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Sets info->hdr and info->len. */
|
||||
int copy_module_from_fd(int fd, struct load_info *info)
|
||||
{
|
||||
struct file *file;
|
||||
int err;
|
||||
struct kstat stat;
|
||||
unsigned long size;
|
||||
off_t pos;
|
||||
ssize_t bytes = 0;
|
||||
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
return -ENOEXEC;
|
||||
|
||||
err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
size = stat.size;
|
||||
info->hdr = vmalloc(size);
|
||||
if (!info->hdr) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while (pos < size) {
|
||||
bytes = kernel_read(file, pos, (char *)(info->hdr) + pos,
|
||||
size - pos);
|
||||
if (bytes < 0) {
|
||||
vfree(info->hdr);
|
||||
err = bytes;
|
||||
goto out;
|
||||
}
|
||||
if (bytes == 0)
|
||||
break;
|
||||
pos += bytes;
|
||||
}
|
||||
info->len = pos;
|
||||
|
||||
err = check_info(info);
|
||||
if (err)
|
||||
vfree(info->hdr);
|
||||
|
||||
out:
|
||||
fput(file);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -3088,26 +3137,17 @@ static int post_relocation(struct module *mod, const struct load_info *info)
|
||||
return module_finalize(info->hdr, info->sechdrs, mod);
|
||||
}
|
||||
|
||||
static int do_init_module(struct module *mod);
|
||||
|
||||
/* Allocate and load the module: note that size of section 0 is always
|
||||
zero, and we rely on this for optional sections. */
|
||||
static struct module *load_module(void __user *umod,
|
||||
unsigned long len,
|
||||
const char __user *uargs)
|
||||
static int load_module(struct load_info *info, const char __user *uargs)
|
||||
{
|
||||
struct load_info info = { NULL, };
|
||||
struct module *mod;
|
||||
long err;
|
||||
|
||||
pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
|
||||
umod, len, uargs);
|
||||
|
||||
/* Copy in the blobs from userspace, check they are vaguely sane. */
|
||||
err = copy_and_check(&info, umod, len, uargs);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* Figure out module layout, and allocate all the memory. */
|
||||
mod = layout_and_allocate(&info);
|
||||
mod = layout_and_allocate(info);
|
||||
if (IS_ERR(mod)) {
|
||||
err = PTR_ERR(mod);
|
||||
goto free_copy;
|
||||
@@ -3120,25 +3160,25 @@ static struct module *load_module(void __user *umod,
|
||||
|
||||
/* Now we've got everything in the final locations, we can
|
||||
* find optional sections. */
|
||||
find_module_sections(mod, &info);
|
||||
find_module_sections(mod, info);
|
||||
|
||||
err = check_module_license_and_versions(mod);
|
||||
if (err)
|
||||
goto free_unload;
|
||||
|
||||
/* Set up MODINFO_ATTR fields */
|
||||
setup_modinfo(mod, &info);
|
||||
setup_modinfo(mod, info);
|
||||
|
||||
/* Fix up syms, so that st_value is a pointer to location. */
|
||||
err = simplify_symbols(mod, &info);
|
||||
err = simplify_symbols(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = apply_relocations(mod, &info);
|
||||
err = apply_relocations(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
err = post_relocation(mod, &info);
|
||||
err = post_relocation(mod, info);
|
||||
if (err < 0)
|
||||
goto free_modinfo;
|
||||
|
||||
@@ -3168,7 +3208,7 @@ static struct module *load_module(void __user *umod,
|
||||
}
|
||||
|
||||
/* This has to be done once we're sure module name is unique. */
|
||||
dynamic_debug_setup(info.debug, info.num_debug);
|
||||
dynamic_debug_setup(info->debug, info->num_debug);
|
||||
|
||||
/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
|
||||
ftrace_module_init(mod);
|
||||
@@ -3178,7 +3218,7 @@ static struct module *load_module(void __user *umod,
|
||||
if (err < 0)
|
||||
goto ddebug;
|
||||
|
||||
module_bug_finalize(info.hdr, info.sechdrs, mod);
|
||||
module_bug_finalize(info->hdr, info->sechdrs, mod);
|
||||
list_add_rcu(&mod->list, &modules);
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
@@ -3189,16 +3229,17 @@ static struct module *load_module(void __user *umod,
|
||||
goto unlink;
|
||||
|
||||
/* Link in to syfs. */
|
||||
err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);
|
||||
err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
/* Get rid of temporary copy. */
|
||||
free_copy(&info);
|
||||
free_copy(info);
|
||||
|
||||
/* Done! */
|
||||
trace_module_load(mod);
|
||||
return mod;
|
||||
|
||||
return do_init_module(mod);
|
||||
|
||||
unlink:
|
||||
mutex_lock(&module_mutex);
|
||||
@@ -3207,7 +3248,7 @@ static struct module *load_module(void __user *umod,
|
||||
module_bug_cleanup(mod);
|
||||
|
||||
ddebug:
|
||||
dynamic_debug_remove(info.debug);
|
||||
dynamic_debug_remove(info->debug);
|
||||
unlock:
|
||||
mutex_unlock(&module_mutex);
|
||||
synchronize_sched();
|
||||
@@ -3219,10 +3260,10 @@ static struct module *load_module(void __user *umod,
|
||||
free_unload:
|
||||
module_unload_free(mod);
|
||||
free_module:
|
||||
module_deallocate(mod, &info);
|
||||
module_deallocate(mod, info);
|
||||
free_copy:
|
||||
free_copy(&info);
|
||||
return ERR_PTR(err);
|
||||
free_copy(info);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Call module constructors. */
|
||||
@@ -3363,21 +3404,10 @@ void tima_mod_page_change_access(struct module *mod)
|
||||
#endif/*CONFIG_TIMA_LKMAUTH_CODE_PROT*/
|
||||
|
||||
/* This is where the real work happens */
|
||||
SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
unsigned long, len, const char __user *, uargs)
|
||||
static int do_init_module(struct module *mod)
|
||||
{
|
||||
struct module *mod;
|
||||
int ret = 0;
|
||||
|
||||
/* Must have permission */
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
/* Do all the hard work */
|
||||
mod = load_module(umod, len, uargs);
|
||||
if (IS_ERR(mod))
|
||||
return PTR_ERR(mod);
|
||||
|
||||
blocking_notifier_call_chain(&module_notify_list,
|
||||
MODULE_STATE_COMING, mod);
|
||||
#ifdef CONFIG_TIMA_LKMAUTH_CODE_PROT
|
||||
@@ -3450,6 +3480,56 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_module_permission(void)
|
||||
{
|
||||
/* Must have permission */
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(finit_module, int, fd, const char __user *, uargs)
|
||||
{
|
||||
int err;
|
||||
struct load_info info = { };
|
||||
|
||||
err = init_module_permission();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("finit_module: fd=%d, uargs=%p\n", fd, uargs);
|
||||
|
||||
if (fd < 0)
|
||||
return -ENOEXEC;
|
||||
|
||||
err = copy_module_from_fd(fd, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return load_module(&info, uargs);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
unsigned long, len, const char __user *, uargs)
|
||||
{
|
||||
int err;
|
||||
struct load_info info = { };
|
||||
|
||||
err = init_module_permission();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
|
||||
umod, len, uargs);
|
||||
|
||||
err = copy_module_from_user(umod, len, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return load_module(&info, uargs);
|
||||
}
|
||||
|
||||
static inline int within(unsigned long addr, void *start, unsigned long size)
|
||||
{
|
||||
return ((void *)addr >= start && (void *)addr < start + size);
|
||||
|
||||
@@ -25,6 +25,7 @@ cond_syscall(sys_swapoff);
|
||||
cond_syscall(sys_kexec_load);
|
||||
cond_syscall(compat_sys_kexec_load);
|
||||
cond_syscall(sys_init_module);
|
||||
cond_syscall(sys_finit_module);
|
||||
cond_syscall(sys_delete_module);
|
||||
cond_syscall(sys_socketpair);
|
||||
cond_syscall(sys_bind);
|
||||
|
||||
Reference in New Issue
Block a user