Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| lk_usermodehelper [2019/02/13 18:30] – [Files] rpjday | lk_usermodehelper [2019/02/14 15:18] (current) – [init/do_mounts_initrd.c] rpjday | ||
|---|---|---|---|
| Line 10: | Line 10: | ||
| * [[https:// | * [[https:// | ||
| * [[https:// | * [[https:// | ||
| + | |||
| + | ===== include/ | ||
| + | |||
| + | ==== Types of execs ==== | ||
| + | |||
| + | < | ||
| + | #define UMH_NO_WAIT | ||
| + | #define UMH_WAIT_EXEC | ||
| + | #define UMH_WAIT_PROC | ||
| + | #define UMH_KILLABLE | ||
| + | </ | ||
| + | |||
| + | ==== struct subprocess_info ==== | ||
| + | |||
| + | < | ||
| + | struct subprocess_info { | ||
| + | struct work_struct work; | ||
| + | struct completion *complete; | ||
| + | const char *path; | ||
| + | char **argv; | ||
| + | char **envp; | ||
| + | struct file *file; | ||
| + | int wait; | ||
| + | int retval; | ||
| + | pid_t pid; | ||
| + | int (*init)(struct subprocess_info *info, struct cred *new); | ||
| + | void (*cleanup)(struct subprocess_info *info); | ||
| + | void *data; | ||
| + | } __randomize_layout; | ||
| + | </ | ||
| + | |||
| + | ==== Standard calls ==== | ||
| + | Common combination: | ||
| + | |||
| + | < | ||
| + | extern struct subprocess_info * | ||
| + | call_usermodehelper_setup(const char *path, char **argv, char **envp, | ||
| + | gfp_t gfp_mask, | ||
| + | int (*init)(struct subprocess_info *info, struct cred *new), | ||
| + | void (*cleanup)(struct subprocess_info *), void *data); | ||
| + | | ||
| + | extern int | ||
| + | call_usermodehelper_exec(struct subprocess_info *info, int wait); | ||
| + | </ | ||
| + | |||
| + | or combination of the above: | ||
| + | |||
| + | < | ||
| + | extern int | ||
| + | call_usermodehelper(const char *path, char **argv, char **envp, int wait); | ||
| + | </ | ||
| + | |||
| + | ===== kernel/ | ||
| + | |||
| + | ==== call_usermodehelper_setup() ==== | ||
| + | |||
| + | < | ||
| + | struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv, | ||
| + | char **envp, gfp_t gfp_mask, | ||
| + | int (*init)(struct subprocess_info *info, struct cred *new), | ||
| + | void (*cleanup)(struct subprocess_info *info), | ||
| + | void *data) | ||
| + | { | ||
| + | struct subprocess_info *sub_info; | ||
| + | sub_info = kzalloc(sizeof(struct subprocess_info), | ||
| + | if (!sub_info) | ||
| + | goto out; | ||
| + | |||
| + | INIT_WORK(& | ||
| + | |||
| + | #ifdef CONFIG_STATIC_USERMODEHELPER | ||
| + | sub_info-> | ||
| + | #else | ||
| + | sub_info-> | ||
| + | #endif | ||
| + | sub_info-> | ||
| + | sub_info-> | ||
| + | |||
| + | sub_info-> | ||
| + | sub_info-> | ||
| + | sub_info-> | ||
| + | out: | ||
| + | return sub_info; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== call_usermodehelper_exec() ==== | ||
| + | |||
| + | < | ||
| + | int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) | ||
| + | { | ||
| + | DECLARE_COMPLETION_ONSTACK(done); | ||
| + | int retval = 0; | ||
| + | |||
| + | if (!sub_info-> | ||
| + | call_usermodehelper_freeinfo(sub_info); | ||
| + | return -EINVAL; | ||
| + | } | ||
| + | helper_lock(); | ||
| + | if (usermodehelper_disabled) { | ||
| + | retval = -EBUSY; | ||
| + | goto out; | ||
| + | } | ||
| + | |||
| + | /* | ||
| + | * If there is no binary for us to call, then just return and get out of | ||
| + | * here. This allows us to set STATIC_USERMODEHELPER_PATH to "" | ||
| + | * disable all call_usermodehelper() calls. | ||
| + | */ | ||
| + | if (strlen(sub_info-> | ||
| + | goto out; | ||
| + | |||
| + | /* | ||
| + | * Set the completion pointer only if there is a waiter. | ||
| + | * This makes it possible to use umh_complete to free | ||
| + | * the data structure in case of UMH_NO_WAIT. | ||
| + | */ | ||
| + | sub_info-> | ||
| + | sub_info-> | ||
| + | |||
| + | queue_work(system_unbound_wq, | ||
| + | if (wait == UMH_NO_WAIT) / | ||
| + | goto unlock; | ||
| + | |||
| + | if (wait & UMH_KILLABLE) { | ||
| + | retval = wait_for_completion_killable(& | ||
| + | if (!retval) | ||
| + | goto wait_done; | ||
| + | |||
| + | /* umh_complete() will see NULL and free sub_info */ | ||
| + | if (xchg(& | ||
| + | goto unlock; | ||
| + | /* fallthrough, | ||
| + | } | ||
| + | |||
| + | wait_for_completion(& | ||
| + | wait_done: | ||
| + | retval = sub_info-> | ||
| + | out: | ||
| + | call_usermodehelper_freeinfo(sub_info); | ||
| + | unlock: | ||
| + | helper_unlock(); | ||
| + | return retval; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== call_usermodehelper() ==== | ||
| + | |||
| + | Combination of the above two: | ||
| + | |||
| + | < | ||
| + | int call_usermodehelper(const char *path, char **argv, char **envp, int wait) | ||
| + | { | ||
| + | struct subprocess_info *info; | ||
| + | gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; | ||
| + | |||
| + | info = call_usermodehelper_setup(path, | ||
| + | NULL, NULL, NULL); | ||
| + | if (info == NULL) | ||
| + | return -ENOMEM; | ||
| + | |||
| + | return call_usermodehelper_exec(info, | ||
| + | } | ||
| + | </ | ||
| + | ===== STATIC_USERMODEHELPER ===== | ||
| + | |||
| + | ==== security/ | ||
| + | |||
| + | < | ||
| + | config STATIC_USERMODEHELPER | ||
| + | bool "Force all usermode helper calls through a single binary" | ||
| + | help | ||
| + | By default, the kernel can call many different userspace | ||
| + | binary programs through the " | ||
| + | interface. | ||
| + | either in the kernel code itself, or as a kernel configuration | ||
| + | option. | ||
| + | runtime, or can be modified after the kernel has started up. | ||
| + | To provide an additional layer of security, route all of these | ||
| + | calls through a single executable that can not have its name | ||
| + | changed. | ||
| + | |||
| + | Note, it is up to this single binary to then call the relevant | ||
| + | " | ||
| + | passed to it. If desired, this program can filter and pick | ||
| + | and choose what real programs are called. | ||
| + | |||
| + | If you wish for all usermode helper programs are to be | ||
| + | disabled, choose this option and then set | ||
| + | STATIC_USERMODEHELPER_PATH to an empty string. | ||
| + | |||
| + | config STATIC_USERMODEHELPER_PATH | ||
| + | string "Path to the static usermode helper binary" | ||
| + | depends on STATIC_USERMODEHELPER | ||
| + | default "/ | ||
| + | help | ||
| + | The binary called by the kernel when any usermode helper | ||
| + | program is wish to be run. The " | ||
| + | be in the first argument passed to this program on the command | ||
| + | line. | ||
| + | |||
| + | If you wish for all usermode helper programs to be disabled, | ||
| + | specify an empty string here (i.e. "" | ||
| + | </ | ||
| + | |||
| + | ==== kernel/ | ||
| + | |||
| + | < | ||
| + | #ifdef CONFIG_STATIC_USERMODEHELPER | ||
| + | sub_info-> | ||
| + | #else | ||
| + | sub_info-> | ||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ===== Examples ===== | ||
| + | |||
| + | ==== kernel/ | ||
| + | |||
| + | < | ||
| + | static int call_modprobe(char *module_name, | ||
| + | { | ||
| + | struct subprocess_info *info; | ||
| + | static char *envp[] = { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | NULL | ||
| + | }; | ||
| + | |||
| + | char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL); | ||
| + | if (!argv) | ||
| + | goto out; | ||
| + | |||
| + | module_name = kstrdup(module_name, | ||
| + | if (!module_name) | ||
| + | goto free_argv; | ||
| + | |||
| + | argv[0] = modprobe_path; | ||
| + | argv[1] = " | ||
| + | argv[2] = " | ||
| + | argv[3] = module_name; | ||
| + | argv[4] = NULL; | ||
| + | |||
| + | info = call_usermodehelper_setup(modprobe_path, | ||
| + | NULL, free_modprobe_argv, | ||
| + | if (!info) | ||
| + | goto free_module_name; | ||
| + | |||
| + | return call_usermodehelper_exec(info, | ||
| + | |||
| + | free_module_name: | ||
| + | kfree(module_name); | ||
| + | free_argv: | ||
| + | kfree(argv); | ||
| + | out: | ||
| + | return -ENOMEM; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== drivers/ | ||
| + | |||
| + | < | ||
| + | static char v86d_path[PATH_MAX] = "/ | ||
| + | |||
| + | ... | ||
| + | |||
| + | static int uvesafb_helper_start(void) | ||
| + | { | ||
| + | char *envp[] = { | ||
| + | " | ||
| + | " | ||
| + | NULL, | ||
| + | }; | ||
| + | |||
| + | char *argv[] = { | ||
| + | v86d_path, | ||
| + | NULL, | ||
| + | }; | ||
| + | |||
| + | return call_usermodehelper(v86d_path, | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== drivers/ | ||
| + | |||
| + | < | ||
| + | static int wf_critical_overtemp(void) | ||
| + | { | ||
| + | static char const critical_overtemp_path[] = "/ | ||
| + | char *argv[] = { (char *)critical_overtemp_path, | ||
| + | static char *envp[] = { " | ||
| + | " | ||
| + | " | ||
| + | NULL }; | ||
| + | |||
| + | return call_usermodehelper(critical_overtemp_path, | ||
| + | argv, envp, UMH_WAIT_EXEC); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== init/ | ||
| + | |||
| + | < | ||
| + | static void __init handle_initrd(void) | ||
| + | { | ||
| + | struct subprocess_info *info; | ||
| + | static char *argv[] = { " | ||
| + | extern char *envp_init[]; | ||
| + | int error; | ||
| + | |||
| + | real_root_dev = new_encode_dev(ROOT_DEV); | ||
| + | create_dev("/ | ||
| + | /* mount initrd on rootfs' | ||
| + | mount_block_root("/ | ||
| + | ksys_mkdir("/ | ||
| + | ksys_chdir("/ | ||
| + | |||
| + | /* | ||
| + | * In case that a resume from disk is carried out by linuxrc or one of | ||
| + | * its children, we need to tell the freezer not to wait for us. | ||
| + | */ | ||
| + | current-> | ||
| + | |||
| + | info = call_usermodehelper_setup("/ | ||
| + | | ||
| + | if (!info) | ||
| + | return; | ||
| + | call_usermodehelper_exec(info, | ||
| + | | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | ==== kernel/ | ||
| + | |||
| + | < | ||
| + | char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/ | ||
| + | static const char reboot_cmd[] = "/ | ||
| + | |||
| + | static int run_cmd(const char *cmd) | ||
| + | { | ||
| + | char **argv; | ||
| + | static char *envp[] = { | ||
| + | " | ||
| + | " | ||
| + | NULL | ||
| + | }; | ||
| + | int ret; | ||
| + | argv = argv_split(GFP_KERNEL, | ||
| + | if (argv) { | ||
| + | ret = call_usermodehelper(argv[0], | ||
| + | argv_free(argv); | ||
| + | } else { | ||
| + | ret = -ENOMEM; | ||
| + | } | ||
| + | |||
| + | return ret; | ||
| + | } | ||
| + | </ | ||