Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
lk_usermodehelper [2019/02/13 18:29] – created rpjdaylk_usermodehelper [2019/02/14 15:18] (current) – [init/do_mounts_initrd.c] rpjday
Line 7: Line 7:
 ===== Files ===== ===== Files =====
  
-  * [[https://elixir.bootlin.com/linux/latest/source/include/linux/umh.h|umh.h]]+  * [[https://elixir.bootlin.com/linux/latest/source/include/linux/umh.h|include/linux/umh.h]] 
 +  * [[https://elixir.bootlin.com/linux/latest/source/kernel/umh.c|kernel/umh.c]] 
 +  * [[https://elixir.bootlin.com/linux/latest/source/security/Kconfig|security/Kconfig]] 
 + 
 +===== include/linux/umh.h ===== 
 + 
 +==== Types of execs ==== 
 + 
 +<code> 
 +#define UMH_NO_WAIT           /* don't wait at all */ 
 +#define UMH_WAIT_EXEC         /* wait for the exec, but not the process */ 
 +#define UMH_WAIT_PROC         /* wait for the process to complete */ 
 +#define UMH_KILLABLE    4       /* wait for EXEC/PROC killable */ 
 +</code> 
 + 
 +==== struct subprocess_info ==== 
 + 
 +<code> 
 +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; 
 +</code> 
 + 
 +==== Standard calls ==== 
 +Common combination: 
 + 
 +<code> 
 +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); 
 +</code> 
 + 
 +or combination of the above: 
 + 
 +<code> 
 +extern int 
 +call_usermodehelper(const char *path, char **argv, char **envp, int wait); 
 +</code> 
 + 
 +===== kernel/umh.c ===== 
 + 
 +==== call_usermodehelper_setup() ==== 
 + 
 +<code> 
 +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), gfp_mask); 
 + if (!sub_info) 
 + goto out; 
 + 
 + INIT_WORK(&sub_info->work, call_usermodehelper_exec_work); 
 + 
 +#ifdef CONFIG_STATIC_USERMODEHELPER 
 + sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH; 
 +#else 
 + sub_info->path = path; 
 +#endif 
 + sub_info->argv = argv; 
 + sub_info->envp = envp; 
 + 
 + sub_info->cleanup = cleanup; 
 + sub_info->init = init; 
 + sub_info->data = data; 
 +  out: 
 + return sub_info; 
 +
 +</code> 
 + 
 +==== call_usermodehelper_exec() ==== 
 + 
 +<code> 
 +int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) 
 +
 + DECLARE_COMPLETION_ONSTACK(done); 
 + int retval = 0; 
 + 
 + if (!sub_info->path) { 
 + 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 "" and 
 + * disable all call_usermodehelper() calls. 
 + */ 
 + if (strlen(sub_info->path) == 0) 
 + 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->complete = (wait == UMH_NO_WAIT) ? NULL : &done; 
 + sub_info->wait = wait; 
 + 
 + queue_work(system_unbound_wq, &sub_info->work); 
 + if (wait == UMH_NO_WAIT) /* task has freed sub_info */ 
 + goto unlock; 
 + 
 + if (wait & UMH_KILLABLE) { 
 + retval = wait_for_completion_killable(&done); 
 + if (!retval) 
 + goto wait_done; 
 + 
 + /* umh_complete() will see NULL and free sub_info */ 
 + if (xchg(&sub_info->complete, NULL)) 
 + goto unlock; 
 + /* fallthrough, umh_complete() was already called */ 
 +
 + 
 + wait_for_completion(&done); 
 +wait_done: 
 + retval = sub_info->retval; 
 +out: 
 + call_usermodehelper_freeinfo(sub_info); 
 +unlock: 
 + helper_unlock(); 
 + return retval; 
 +
 +</code> 
 + 
 +==== call_usermodehelper() ==== 
 + 
 +Combination of the above two: 
 + 
 +<code> 
 +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, argv, envp, gfp_mask, 
 + NULL, NULL, NULL); 
 + if (info == NULL) 
 + return -ENOMEM; 
 + 
 + return call_usermodehelper_exec(info, wait); 
 +
 +</code> 
 +===== STATIC_USERMODEHELPER ===== 
 + 
 +==== security/Kconfig ==== 
 + 
 +<code> 
 +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 "usermode helper" kernel 
 +          interface.  Some of these binaries are statically defined 
 +          either in the kernel code itself, or as a kernel configuration 
 +          option.  However, some of these are dynamically created at 
 +          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 
 +          "real" usermode helper binary, based on the first argument 
 +          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 "/sbin/usermode-helper" 
 +        help 
 +          The binary called by the kernel when any usermode helper 
 +          program is wish to be run.  The "real" application's name will 
 +          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. ""). 
 +</code> 
 + 
 +==== kernel/umh.c ==== 
 + 
 +<code> 
 +#ifdef CONFIG_STATIC_USERMODEHELPER 
 +        sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH; 
 +#else 
 +        sub_info->path = path; 
 +#endif 
 +</code> 
 + 
 +===== Examples ===== 
 + 
 +==== kernel/kmod.c ==== 
 + 
 +<code> 
 +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; 
 +
 +</code> 
 + 
 +==== drivers/video/fbdev/uvesafb.c ==== 
 + 
 +<code> 
 +static char v86d_path[PATH_MAX] = "/sbin/v86d"; 
 + 
 +... 
 + 
 +static int uvesafb_helper_start(void) 
 +
 +        char *envp[] = { 
 +                "HOME=/", 
 +                "PATH=/sbin:/bin", 
 +                NULL, 
 +        }; 
 + 
 +        char *argv[] = { 
 +                v86d_path, 
 +                NULL, 
 +        }; 
 + 
 +        return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC); 
 +
 +</code> 
 + 
 +==== drivers/macintosh/windfarm_core.c ==== 
 + 
 +<code> 
 +static int wf_critical_overtemp(void) 
 +
 +        static char const critical_overtemp_path[] = "/sbin/critical_overtemp"; 
 +        char *argv[] = { (char *)critical_overtemp_path, NULL }; 
 +        static char *envp[] = { "HOME=/", 
 +                                "TERM=linux", 
 +                                "PATH=/sbin:/usr/sbin:/bin:/usr/bin", 
 +                                NULL }; 
 + 
 +        return call_usermodehelper(critical_overtemp_path, 
 +                                   argv, envp, UMH_WAIT_EXEC); 
 +
 +</code> 
 + 
 +==== init/do_mounts_initrd.c ==== 
 + 
 +<code> 
 +static void __init handle_initrd(void) 
 +
 +        struct subprocess_info *info; 
 +        static char *argv[] = { "linuxrc", NULL, }; 
 +        extern char *envp_init[]; 
 +        int error; 
 + 
 +        real_root_dev = new_encode_dev(ROOT_DEV); 
 +        create_dev("/dev/root.old", Root_RAM0); 
 +        /* mount initrd on rootfs' /root */ 
 +        mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); 
 +        ksys_mkdir("/old", 0700); 
 +        ksys_chdir("/old"); 
 + 
 +        /* 
 +         * 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->flags |= PF_FREEZER_SKIP; 
 + 
 +        info = call_usermodehelper_setup("/linuxrc", argv, envp_init, 
 +                                         GFP_KERNEL, init_linuxrc, NULL, NULL); 
 +        if (!info) 
 +                return; 
 +        call_usermodehelper_exec(info, UMH_WAIT_PROC); 
 +         
 +        ... 
 +</code> 
 + 
 +==== kernel/reboot.c ==== 
 + 
 +<code> 
 +char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; 
 +static const char reboot_cmd[] = "/sbin/reboot"; 
 + 
 +static int run_cmd(const char *cmd) 
 +
 +        char **argv; 
 +        static char *envp[] = { 
 +                "HOME=/", 
 +                "PATH=/sbin:/bin:/usr/sbin:/usr/bin", 
 +                NULL 
 +        }; 
 +        int ret; 
 +        argv = argv_split(GFP_KERNEL, cmd, NULL); 
 +        if (argv) { 
 +                ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); 
 +                argv_free(argv); 
 +        } else { 
 +                ret = -ENOMEM; 
 +        } 
 + 
 +        return ret; 
 +
 +</code>
  • lk_usermodehelper.1550082555.txt.gz
  • Last modified: 2019/02/13 18:29
  • by rpjday