Patch series "kmod: few code cleanups to split out umh code" The usermode helper has a provenance from the old usb code which first required a usermode helper. Eventually this was shoved into kmod.c and the kernel's modprobe calls was converted over eventually to share the same code. Over time the list of usermode helpers in the kernel has grown -- so kmod is just but one user of the API. This series is a simple logical cleanup which acknowledges the code evolution of the usermode helper and shoves the UMH API into its own dedicated file. This way users of the API can later just include umh.h instead of kmod.h. Note despite the diff state the first patch really is just a code shove, no functional changes are done there. I did use git format-patch -M to generate the patch, but in the end the split was not enough for git to consider it a rename hence the large diffstat. I've put this through 0-day and it gives me their machine compilation blessings with all tests as OK. This patch (of 4): There's a slew of usermode helper users and kmod is just one of them. Split out the usermode helper code into its own file to keep the logic and focus split up. This change provides no functional changes. Link: http://lkml.kernel.org/r/20170810180618.22457-2-mcgrof@kernel.org Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org> Cc: Kees Cook <keescook@chromium.org> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Jessica Yu <jeyu@redhat.com> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Michal Marek <mmarek@suse.com> Cc: Petr Mladek <pmladek@suse.com> Cc: Miroslav Benes <mbenes@suse.cz> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Guenter Roeck <linux@roeck-us.net> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Matt Redfearn <matt.redfearn@imgtec.com> Cc: Dan Carpenter <dan.carpenter@oracle.com> Cc: Colin Ian King <colin.king@canonical.com> Cc: Daniel Mentz <danielmentz@google.com> Cc: David Binderman <dcb314@hotmail.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Sorayukii <sorayukii69@gmail.com>
165 lines
4.5 KiB
C
165 lines
4.5 KiB
C
/*
|
|
* kmod - the kernel module loader
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/file.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/fs_struct.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/security.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/resource.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/async.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <trace/events/module.h>
|
|
|
|
extern int max_threads;
|
|
|
|
#ifdef CONFIG_MODULES
|
|
|
|
/*
|
|
modprobe_path is set via /proc/sys.
|
|
*/
|
|
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
|
|
|
|
static void free_modprobe_argv(struct subprocess_info *info)
|
|
{
|
|
kfree(info->argv[3]); /* check call_modprobe() */
|
|
kfree(info->argv);
|
|
}
|
|
|
|
static int call_modprobe(char *module_name, int wait)
|
|
{
|
|
struct subprocess_info *info;
|
|
static char *envp[] = {
|
|
"HOME=/",
|
|
"TERM=linux",
|
|
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
|
NULL
|
|
};
|
|
|
|
char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
|
|
if (!argv)
|
|
goto out;
|
|
|
|
module_name = kstrdup(module_name, GFP_KERNEL);
|
|
if (!module_name)
|
|
goto free_argv;
|
|
|
|
argv[0] = modprobe_path;
|
|
argv[1] = "-q";
|
|
argv[2] = "--";
|
|
argv[3] = module_name; /* check free_modprobe_argv() */
|
|
argv[4] = NULL;
|
|
|
|
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
|
|
NULL, free_modprobe_argv, NULL);
|
|
if (!info)
|
|
goto free_module_name;
|
|
|
|
return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
|
|
|
|
free_module_name:
|
|
kfree(module_name);
|
|
free_argv:
|
|
kfree(argv);
|
|
out:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**
|
|
* __request_module - try to load a kernel module
|
|
* @wait: wait (or not) for the operation to complete
|
|
* @fmt: printf style format string for the name of the module
|
|
* @...: arguments as specified in the format string
|
|
*
|
|
* Load a module using the user mode module loader. The function returns
|
|
* zero on success or a negative errno code or positive exit code from
|
|
* "modprobe" on failure. Note that a successful module load does not mean
|
|
* the module did not then unload and exit on an error of its own. Callers
|
|
* must check that the service they requested is now available not blindly
|
|
* invoke it.
|
|
*
|
|
* If module auto-loading support is disabled then this function
|
|
* simply returns -ENOENT.
|
|
*/
|
|
int __request_module(bool wait, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char module_name[MODULE_NAME_LEN];
|
|
unsigned int max_modprobes;
|
|
int ret;
|
|
static atomic_t kmod_concurrent = ATOMIC_INIT(0);
|
|
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
|
|
static int kmod_loop_msg;
|
|
|
|
/*
|
|
* We don't allow synchronous module loading from async. Module
|
|
* init may invoke async_synchronize_full() which will end up
|
|
* waiting for this task which already is waiting for the module
|
|
* loading to complete, leading to a deadlock.
|
|
*/
|
|
WARN_ON_ONCE(wait && current_is_async());
|
|
|
|
if (!modprobe_path[0])
|
|
return -ENOENT;
|
|
|
|
va_start(args, fmt);
|
|
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
|
|
va_end(args);
|
|
if (ret >= MODULE_NAME_LEN)
|
|
return -ENAMETOOLONG;
|
|
|
|
ret = security_kernel_module_request(module_name);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* If modprobe needs a service that is in a module, we get a recursive
|
|
* loop. Limit the number of running kmod threads to max_threads/2 or
|
|
* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method
|
|
* would be to run the parents of this process, counting how many times
|
|
* kmod was invoked. That would mean accessing the internals of the
|
|
* process tables to get the command line, proc_pid_cmdline is static
|
|
* and it is not worth changing the proc code just to handle this case.
|
|
* KAO.
|
|
*
|
|
* "trace the ppid" is simple, but will fail if someone's
|
|
* parent exits. I think this is as good as it gets. --RR
|
|
*/
|
|
max_modprobes = min(max_threads/2, MAX_KMOD_CONCURRENT);
|
|
atomic_inc(&kmod_concurrent);
|
|
if (atomic_read(&kmod_concurrent) > max_modprobes) {
|
|
/* We may be blaming an innocent here, but unlikely */
|
|
if (kmod_loop_msg < 5) {
|
|
printk(KERN_ERR
|
|
"request_module: runaway loop modprobe %s\n",
|
|
module_name);
|
|
kmod_loop_msg++;
|
|
}
|
|
atomic_dec(&kmod_concurrent);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
trace_module_request(module_name, wait, _RET_IP_);
|
|
|
|
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
|
|
|
|
atomic_dec(&kmod_concurrent);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(__request_module);
|
|
#endif /* CONFIG_MODULES */
|