1*884c5e68SEric W. Biederman // SPDX-License-Identifier: GPL-2.0-only 2*884c5e68SEric W. Biederman /* 3*884c5e68SEric W. Biederman * umd - User mode driver support 4*884c5e68SEric W. Biederman */ 5*884c5e68SEric W. Biederman #include <linux/shmem_fs.h> 6*884c5e68SEric W. Biederman #include <linux/pipe_fs_i.h> 7*884c5e68SEric W. Biederman #include <linux/usermode_driver.h> 8*884c5e68SEric W. Biederman 9*884c5e68SEric W. Biederman static LIST_HEAD(umh_list); 10*884c5e68SEric W. Biederman static DEFINE_MUTEX(umh_list_lock); 11*884c5e68SEric W. Biederman 12*884c5e68SEric W. Biederman static int umd_setup(struct subprocess_info *info, struct cred *new) 13*884c5e68SEric W. Biederman { 14*884c5e68SEric W. Biederman struct umh_info *umh_info = info->data; 15*884c5e68SEric W. Biederman struct file *from_umh[2]; 16*884c5e68SEric W. Biederman struct file *to_umh[2]; 17*884c5e68SEric W. Biederman int err; 18*884c5e68SEric W. Biederman 19*884c5e68SEric W. Biederman /* create pipe to send data to umh */ 20*884c5e68SEric W. Biederman err = create_pipe_files(to_umh, 0); 21*884c5e68SEric W. Biederman if (err) 22*884c5e68SEric W. Biederman return err; 23*884c5e68SEric W. Biederman err = replace_fd(0, to_umh[0], 0); 24*884c5e68SEric W. Biederman fput(to_umh[0]); 25*884c5e68SEric W. Biederman if (err < 0) { 26*884c5e68SEric W. Biederman fput(to_umh[1]); 27*884c5e68SEric W. Biederman return err; 28*884c5e68SEric W. Biederman } 29*884c5e68SEric W. Biederman 30*884c5e68SEric W. Biederman /* create pipe to receive data from umh */ 31*884c5e68SEric W. Biederman err = create_pipe_files(from_umh, 0); 32*884c5e68SEric W. Biederman if (err) { 33*884c5e68SEric W. Biederman fput(to_umh[1]); 34*884c5e68SEric W. Biederman replace_fd(0, NULL, 0); 35*884c5e68SEric W. Biederman return err; 36*884c5e68SEric W. Biederman } 37*884c5e68SEric W. Biederman err = replace_fd(1, from_umh[1], 0); 38*884c5e68SEric W. Biederman fput(from_umh[1]); 39*884c5e68SEric W. Biederman if (err < 0) { 40*884c5e68SEric W. Biederman fput(to_umh[1]); 41*884c5e68SEric W. Biederman replace_fd(0, NULL, 0); 42*884c5e68SEric W. Biederman fput(from_umh[0]); 43*884c5e68SEric W. Biederman return err; 44*884c5e68SEric W. Biederman } 45*884c5e68SEric W. Biederman 46*884c5e68SEric W. Biederman umh_info->pipe_to_umh = to_umh[1]; 47*884c5e68SEric W. Biederman umh_info->pipe_from_umh = from_umh[0]; 48*884c5e68SEric W. Biederman umh_info->pid = task_pid_nr(current); 49*884c5e68SEric W. Biederman current->flags |= PF_UMH; 50*884c5e68SEric W. Biederman return 0; 51*884c5e68SEric W. Biederman } 52*884c5e68SEric W. Biederman 53*884c5e68SEric W. Biederman static void umd_cleanup(struct subprocess_info *info) 54*884c5e68SEric W. Biederman { 55*884c5e68SEric W. Biederman struct umh_info *umh_info = info->data; 56*884c5e68SEric W. Biederman 57*884c5e68SEric W. Biederman /* cleanup if umh_setup() was successful but exec failed */ 58*884c5e68SEric W. Biederman if (info->retval) { 59*884c5e68SEric W. Biederman fput(umh_info->pipe_to_umh); 60*884c5e68SEric W. Biederman fput(umh_info->pipe_from_umh); 61*884c5e68SEric W. Biederman } 62*884c5e68SEric W. Biederman } 63*884c5e68SEric W. Biederman 64*884c5e68SEric W. Biederman /** 65*884c5e68SEric W. Biederman * fork_usermode_blob - fork a blob of bytes as a usermode process 66*884c5e68SEric W. Biederman * @data: a blob of bytes that can be do_execv-ed as a file 67*884c5e68SEric W. Biederman * @len: length of the blob 68*884c5e68SEric W. Biederman * @info: information about usermode process (shouldn't be NULL) 69*884c5e68SEric W. Biederman * 70*884c5e68SEric W. Biederman * If info->cmdline is set it will be used as command line for the 71*884c5e68SEric W. Biederman * user process, else "usermodehelper" is used. 72*884c5e68SEric W. Biederman * 73*884c5e68SEric W. Biederman * Returns either negative error or zero which indicates success 74*884c5e68SEric W. Biederman * in executing a blob of bytes as a usermode process. In such 75*884c5e68SEric W. Biederman * case 'struct umh_info *info' is populated with two pipes 76*884c5e68SEric W. Biederman * and a pid of the process. The caller is responsible for health 77*884c5e68SEric W. Biederman * check of the user process, killing it via pid, and closing the 78*884c5e68SEric W. Biederman * pipes when user process is no longer needed. 79*884c5e68SEric W. Biederman */ 80*884c5e68SEric W. Biederman int fork_usermode_blob(void *data, size_t len, struct umh_info *info) 81*884c5e68SEric W. Biederman { 82*884c5e68SEric W. Biederman const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper"; 83*884c5e68SEric W. Biederman struct subprocess_info *sub_info; 84*884c5e68SEric W. Biederman char **argv = NULL; 85*884c5e68SEric W. Biederman struct file *file; 86*884c5e68SEric W. Biederman ssize_t written; 87*884c5e68SEric W. Biederman loff_t pos = 0; 88*884c5e68SEric W. Biederman int err; 89*884c5e68SEric W. Biederman 90*884c5e68SEric W. Biederman file = shmem_kernel_file_setup("", len, 0); 91*884c5e68SEric W. Biederman if (IS_ERR(file)) 92*884c5e68SEric W. Biederman return PTR_ERR(file); 93*884c5e68SEric W. Biederman 94*884c5e68SEric W. Biederman written = kernel_write(file, data, len, &pos); 95*884c5e68SEric W. Biederman if (written != len) { 96*884c5e68SEric W. Biederman err = written; 97*884c5e68SEric W. Biederman if (err >= 0) 98*884c5e68SEric W. Biederman err = -ENOMEM; 99*884c5e68SEric W. Biederman goto out; 100*884c5e68SEric W. Biederman } 101*884c5e68SEric W. Biederman 102*884c5e68SEric W. Biederman err = -ENOMEM; 103*884c5e68SEric W. Biederman argv = argv_split(GFP_KERNEL, cmdline, NULL); 104*884c5e68SEric W. Biederman if (!argv) 105*884c5e68SEric W. Biederman goto out; 106*884c5e68SEric W. Biederman 107*884c5e68SEric W. Biederman sub_info = call_usermodehelper_setup("none", argv, NULL, GFP_KERNEL, 108*884c5e68SEric W. Biederman umd_setup, umd_cleanup, info); 109*884c5e68SEric W. Biederman if (!sub_info) 110*884c5e68SEric W. Biederman goto out; 111*884c5e68SEric W. Biederman 112*884c5e68SEric W. Biederman sub_info->file = file; 113*884c5e68SEric W. Biederman err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC); 114*884c5e68SEric W. Biederman if (!err) { 115*884c5e68SEric W. Biederman mutex_lock(&umh_list_lock); 116*884c5e68SEric W. Biederman list_add(&info->list, &umh_list); 117*884c5e68SEric W. Biederman mutex_unlock(&umh_list_lock); 118*884c5e68SEric W. Biederman } 119*884c5e68SEric W. Biederman out: 120*884c5e68SEric W. Biederman if (argv) 121*884c5e68SEric W. Biederman argv_free(argv); 122*884c5e68SEric W. Biederman fput(file); 123*884c5e68SEric W. Biederman return err; 124*884c5e68SEric W. Biederman } 125*884c5e68SEric W. Biederman EXPORT_SYMBOL_GPL(fork_usermode_blob); 126*884c5e68SEric W. Biederman 127*884c5e68SEric W. Biederman void __exit_umh(struct task_struct *tsk) 128*884c5e68SEric W. Biederman { 129*884c5e68SEric W. Biederman struct umh_info *info; 130*884c5e68SEric W. Biederman pid_t pid = tsk->pid; 131*884c5e68SEric W. Biederman 132*884c5e68SEric W. Biederman mutex_lock(&umh_list_lock); 133*884c5e68SEric W. Biederman list_for_each_entry(info, &umh_list, list) { 134*884c5e68SEric W. Biederman if (info->pid == pid) { 135*884c5e68SEric W. Biederman list_del(&info->list); 136*884c5e68SEric W. Biederman mutex_unlock(&umh_list_lock); 137*884c5e68SEric W. Biederman goto out; 138*884c5e68SEric W. Biederman } 139*884c5e68SEric W. Biederman } 140*884c5e68SEric W. Biederman mutex_unlock(&umh_list_lock); 141*884c5e68SEric W. Biederman return; 142*884c5e68SEric W. Biederman out: 143*884c5e68SEric W. Biederman if (info->cleanup) 144*884c5e68SEric W. Biederman info->cleanup(info); 145*884c5e68SEric W. Biederman } 146*884c5e68SEric W. Biederman 147