Skip to content

Commit 449325b

Browse files
Alexei Starovoitovdavem330
authored andcommitted
umh: introduce fork_usermode_blob() helper
Introduce helper: int fork_usermode_blob(void *data, size_t len, struct umh_info *info); struct umh_info { struct file *pipe_to_umh; struct file *pipe_from_umh; pid_t pid; }; that GPLed kernel modules (signed or unsigned) can use it to execute part of its own data as swappable user mode process. The kernel will do: - allocate a unique file in tmpfs - populate that file with [data, data + len] bytes - user-mode-helper code will do_execve that file and, before the process starts, the kernel will create two unix pipes for bidirectional communication between kernel module and umh - close tmpfs file, effectively deleting it - the fork_usermode_blob will return zero on success and populate 'struct umh_info' with two unix pipes and the pid of the user process As the first step in the development of the bpfilter project the fork_usermode_blob() helper is introduced to allow user mode code to be invoked from a kernel module. The idea is that user mode code plus normal kernel module code are built as part of the kernel build and installed as traditional kernel module into distro specified location, such that from a distribution point of view, there is no difference between regular kernel modules and kernel modules + umh code. Such modules can be signed, modprobed, rmmod, etc. The use of this new helper by a kernel module doesn't make it any special from kernel and user space tooling point of view. Such approach enables kernel to delegate functionality traditionally done by the kernel modules into the user space processes (either root or !root) and reduces security attack surface of the new code. The buggy umh code would crash the user process, but not the kernel. Another advantage is that umh code of the kernel module can be debugged and tested out of user space (e.g. opening the possibility to run clang sanitizers, fuzzers or user space test suites on the umh code). In case of the bpfilter project such architecture allows complex control plane to be done in the user space while bpf based data plane stays in the kernel. Since umh can crash, can be oom-ed by the kernel, killed by the admin, the kernel module that uses them (like bpfilter) needs to manage life time of umh on its own via two unix pipes and the pid of umh. The exit code of such kernel module should kill the umh it started, so that rmmod of the kernel module will cleanup the corresponding umh. Just like if the kernel module does kmalloc() it should kfree() it in the exit code. Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1fe8c06 commit 449325b

File tree

4 files changed

+164
-12
lines changed

4 files changed

+164
-12
lines changed

fs/exec.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,14 +1706,13 @@ static int exec_binprm(struct linux_binprm *bprm)
17061706
/*
17071707
* sys_execve() executes a new program.
17081708
*/
1709-
static int do_execveat_common(int fd, struct filename *filename,
1710-
struct user_arg_ptr argv,
1711-
struct user_arg_ptr envp,
1712-
int flags)
1709+
static int __do_execve_file(int fd, struct filename *filename,
1710+
struct user_arg_ptr argv,
1711+
struct user_arg_ptr envp,
1712+
int flags, struct file *file)
17131713
{
17141714
char *pathbuf = NULL;
17151715
struct linux_binprm *bprm;
1716-
struct file *file;
17171716
struct files_struct *displaced;
17181717
int retval;
17191718

@@ -1752,15 +1751,18 @@ static int do_execveat_common(int fd, struct filename *filename,
17521751
check_unsafe_exec(bprm);
17531752
current->in_execve = 1;
17541753

1755-
file = do_open_execat(fd, filename, flags);
1754+
if (!file)
1755+
file = do_open_execat(fd, filename, flags);
17561756
retval = PTR_ERR(file);
17571757
if (IS_ERR(file))
17581758
goto out_unmark;
17591759

17601760
sched_exec();
17611761

17621762
bprm->file = file;
1763-
if (fd == AT_FDCWD || filename->name[0] == '/') {
1763+
if (!filename) {
1764+
bprm->filename = "none";
1765+
} else if (fd == AT_FDCWD || filename->name[0] == '/') {
17641766
bprm->filename = filename->name;
17651767
} else {
17661768
if (filename->name[0] == '\0')
@@ -1826,7 +1828,8 @@ static int do_execveat_common(int fd, struct filename *filename,
18261828
task_numa_free(current);
18271829
free_bprm(bprm);
18281830
kfree(pathbuf);
1829-
putname(filename);
1831+
if (filename)
1832+
putname(filename);
18301833
if (displaced)
18311834
put_files_struct(displaced);
18321835
return retval;
@@ -1849,10 +1852,27 @@ static int do_execveat_common(int fd, struct filename *filename,
18491852
if (displaced)
18501853
reset_files_struct(displaced);
18511854
out_ret:
1852-
putname(filename);
1855+
if (filename)
1856+
putname(filename);
18531857
return retval;
18541858
}
18551859

1860+
static int do_execveat_common(int fd, struct filename *filename,
1861+
struct user_arg_ptr argv,
1862+
struct user_arg_ptr envp,
1863+
int flags)
1864+
{
1865+
return __do_execve_file(fd, filename, argv, envp, flags, NULL);
1866+
}
1867+
1868+
int do_execve_file(struct file *file, void *__argv, void *__envp)
1869+
{
1870+
struct user_arg_ptr argv = { .ptr.native = __argv };
1871+
struct user_arg_ptr envp = { .ptr.native = __envp };
1872+
1873+
return __do_execve_file(AT_FDCWD, NULL, argv, envp, 0, file);
1874+
}
1875+
18561876
int do_execve(struct filename *filename,
18571877
const char __user *const __user *__argv,
18581878
const char __user *const __user *__envp)

include/linux/binfmts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,6 @@ extern int do_execveat(int, struct filename *,
150150
const char __user * const __user *,
151151
const char __user * const __user *,
152152
int);
153+
int do_execve_file(struct file *file, void *__argv, void *__envp);
153154

154155
#endif /* _LINUX_BINFMTS_H */

include/linux/umh.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ struct subprocess_info {
2222
const char *path;
2323
char **argv;
2424
char **envp;
25+
struct file *file;
2526
int wait;
2627
int retval;
28+
pid_t pid;
2729
int (*init)(struct subprocess_info *info, struct cred *new);
2830
void (*cleanup)(struct subprocess_info *info);
2931
void *data;
@@ -38,6 +40,16 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp,
3840
int (*init)(struct subprocess_info *info, struct cred *new),
3941
void (*cleanup)(struct subprocess_info *), void *data);
4042

43+
struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
44+
int (*init)(struct subprocess_info *info, struct cred *new),
45+
void (*cleanup)(struct subprocess_info *), void *data);
46+
struct umh_info {
47+
struct file *pipe_to_umh;
48+
struct file *pipe_from_umh;
49+
pid_t pid;
50+
};
51+
int fork_usermode_blob(void *data, size_t len, struct umh_info *info);
52+
4153
extern int
4254
call_usermodehelper_exec(struct subprocess_info *info, int wait);
4355

kernel/umh.c

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <linux/ptrace.h>
2626
#include <linux/async.h>
2727
#include <linux/uaccess.h>
28+
#include <linux/shmem_fs.h>
29+
#include <linux/pipe_fs_i.h>
2830

2931
#include <trace/events/module.h>
3032

@@ -97,9 +99,13 @@ static int call_usermodehelper_exec_async(void *data)
9799

98100
commit_creds(new);
99101

100-
retval = do_execve(getname_kernel(sub_info->path),
101-
(const char __user *const __user *)sub_info->argv,
102-
(const char __user *const __user *)sub_info->envp);
102+
if (sub_info->file)
103+
retval = do_execve_file(sub_info->file,
104+
sub_info->argv, sub_info->envp);
105+
else
106+
retval = do_execve(getname_kernel(sub_info->path),
107+
(const char __user *const __user *)sub_info->argv,
108+
(const char __user *const __user *)sub_info->envp);
103109
out:
104110
sub_info->retval = retval;
105111
/*
@@ -185,6 +191,8 @@ static void call_usermodehelper_exec_work(struct work_struct *work)
185191
if (pid < 0) {
186192
sub_info->retval = pid;
187193
umh_complete(sub_info);
194+
} else {
195+
sub_info->pid = pid;
188196
}
189197
}
190198
}
@@ -393,6 +401,117 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
393401
}
394402
EXPORT_SYMBOL(call_usermodehelper_setup);
395403

404+
struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
405+
int (*init)(struct subprocess_info *info, struct cred *new),
406+
void (*cleanup)(struct subprocess_info *info), void *data)
407+
{
408+
struct subprocess_info *sub_info;
409+
410+
sub_info = kzalloc(sizeof(struct subprocess_info), GFP_KERNEL);
411+
if (!sub_info)
412+
return NULL;
413+
414+
INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
415+
sub_info->path = "none";
416+
sub_info->file = file;
417+
sub_info->init = init;
418+
sub_info->cleanup = cleanup;
419+
sub_info->data = data;
420+
return sub_info;
421+
}
422+
423+
static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
424+
{
425+
struct umh_info *umh_info = info->data;
426+
struct file *from_umh[2];
427+
struct file *to_umh[2];
428+
int err;
429+
430+
/* create pipe to send data to umh */
431+
err = create_pipe_files(to_umh, 0);
432+
if (err)
433+
return err;
434+
err = replace_fd(0, to_umh[0], 0);
435+
fput(to_umh[0]);
436+
if (err < 0) {
437+
fput(to_umh[1]);
438+
return err;
439+
}
440+
441+
/* create pipe to receive data from umh */
442+
err = create_pipe_files(from_umh, 0);
443+
if (err) {
444+
fput(to_umh[1]);
445+
replace_fd(0, NULL, 0);
446+
return err;
447+
}
448+
err = replace_fd(1, from_umh[1], 0);
449+
fput(from_umh[1]);
450+
if (err < 0) {
451+
fput(to_umh[1]);
452+
replace_fd(0, NULL, 0);
453+
fput(from_umh[0]);
454+
return err;
455+
}
456+
457+
umh_info->pipe_to_umh = to_umh[1];
458+
umh_info->pipe_from_umh = from_umh[0];
459+
return 0;
460+
}
461+
462+
static void umh_save_pid(struct subprocess_info *info)
463+
{
464+
struct umh_info *umh_info = info->data;
465+
466+
umh_info->pid = info->pid;
467+
}
468+
469+
/**
470+
* fork_usermode_blob - fork a blob of bytes as a usermode process
471+
* @data: a blob of bytes that can be do_execv-ed as a file
472+
* @len: length of the blob
473+
* @info: information about usermode process (shouldn't be NULL)
474+
*
475+
* Returns either negative error or zero which indicates success
476+
* in executing a blob of bytes as a usermode process. In such
477+
* case 'struct umh_info *info' is populated with two pipes
478+
* and a pid of the process. The caller is responsible for health
479+
* check of the user process, killing it via pid, and closing the
480+
* pipes when user process is no longer needed.
481+
*/
482+
int fork_usermode_blob(void *data, size_t len, struct umh_info *info)
483+
{
484+
struct subprocess_info *sub_info;
485+
struct file *file;
486+
ssize_t written;
487+
loff_t pos = 0;
488+
int err;
489+
490+
file = shmem_kernel_file_setup("", len, 0);
491+
if (IS_ERR(file))
492+
return PTR_ERR(file);
493+
494+
written = kernel_write(file, data, len, &pos);
495+
if (written != len) {
496+
err = written;
497+
if (err >= 0)
498+
err = -ENOMEM;
499+
goto out;
500+
}
501+
502+
err = -ENOMEM;
503+
sub_info = call_usermodehelper_setup_file(file, umh_pipe_setup,
504+
umh_save_pid, info);
505+
if (!sub_info)
506+
goto out;
507+
508+
err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
509+
out:
510+
fput(file);
511+
return err;
512+
}
513+
EXPORT_SYMBOL_GPL(fork_usermode_blob);
514+
396515
/**
397516
* call_usermodehelper_exec - start a usermode application
398517
* @sub_info: information about the subprocessa

0 commit comments

Comments
 (0)