Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e95a5f54 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bpfilter'

Alexei Starovoitov says:

====================
bpfilter

v2->v3:
- followed Luis's suggestion and significantly simplied first patch
  with shmem_kernel_file_setup+kernel_write. Added kdoc for new helper
- fixed typos and race to access pipes with mutex
- tested with bpfilter being 'builtin'. CONFIG_BPFILTER_UMH=y|m both work.
  Interesting to see a usermode executable being embedded inside vmlinux.
- it doesn't hurt to enable bpfilter in .config.
  ip_setsockopt commands sent to usermode via pipes and -ENOPROTOOPT is
  returned from userspace, so kernel falls back to original iptables code

v1->v2:
this patch set is almost a full rewrite of the earlier umh modules approach
The v1 of patches and follow up discussion was covered by LWN:
https://lwn.net/Articles/749108/



I believe the v2 addresses all issues brought up by Andy and others.
Mainly there are zero changes to kernel/module.c
Instead of teaching module loading logic to recognize special
umh module, let normal kernel modules execute part of its own
.init.rodata as a new user space process (Andy's idea)
Patch 1 introduces this new helper:
int fork_usermode_blob(void *data, size_t len, struct umh_info *info);
Input:
  data + len == executable file
Output:
  struct umh_info {
       struct file *pipe_to_umh;
       struct file *pipe_from_umh;
       pid_t pid;
  };

Advantages vs v1:
- the embedded user mode executable is stored as .init.rodata inside
  normal kernel module. These pages are freed when .ko finishes loading
- the elf file is copied into tmpfs file. The user mode process is swappable.
- the communication between user mode process and 'parent' kernel module
  is done via two unix pipes, hence protocol is not exposed to
  user space
- impossible to launch umh on its own (that was the main issue of v1)
  and impossible to be man-in-the-middle due to pipes
- bpfilter.ko consists of tiny kernel part that passes the data
  between kernel and umh via pipes and much bigger umh part that
  doing all the work
- 'lsmod' shows bpfilter.ko as usual.
  'rmmod bpfilter' removes kernel module and kills corresponding umh
- signed bpfilter.ko covers the whole image including umh code

Few issues:
- the user can still attach to the process and debug it with
  'gdb /proc/pid/exe pid', but 'gdb -p pid' doesn't work.
  (a bit worse comparing to v1)
- tinyconfig will notice a small increase in .text
  +766 | TEXT | 7c8b94806bec umh: introduce fork_usermode_blob() helper
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1fe8c06c d2ba09c1
Loading
Loading
Loading
Loading
+29 −9
Original line number Diff line number Diff line
@@ -1706,14 +1706,13 @@ static int exec_binprm(struct linux_binprm *bprm)
/*
 * sys_execve() executes a new program.
 */
static int do_execveat_common(int fd, struct filename *filename,
static int __do_execve_file(int fd, struct filename *filename,
			    struct user_arg_ptr argv,
			    struct user_arg_ptr envp,
			      int flags)
			    int flags, struct file *file)
{
	char *pathbuf = NULL;
	struct linux_binprm *bprm;
	struct file *file;
	struct files_struct *displaced;
	int retval;

@@ -1752,6 +1751,7 @@ static int do_execveat_common(int fd, struct filename *filename,
	check_unsafe_exec(bprm);
	current->in_execve = 1;

	if (!file)
		file = do_open_execat(fd, filename, flags);
	retval = PTR_ERR(file);
	if (IS_ERR(file))
@@ -1760,7 +1760,9 @@ static int do_execveat_common(int fd, struct filename *filename,
	sched_exec();

	bprm->file = file;
	if (fd == AT_FDCWD || filename->name[0] == '/') {
	if (!filename) {
		bprm->filename = "none";
	} else if (fd == AT_FDCWD || filename->name[0] == '/') {
		bprm->filename = filename->name;
	} else {
		if (filename->name[0] == '\0')
@@ -1826,6 +1828,7 @@ static int do_execveat_common(int fd, struct filename *filename,
	task_numa_free(current);
	free_bprm(bprm);
	kfree(pathbuf);
	if (filename)
		putname(filename);
	if (displaced)
		put_files_struct(displaced);
@@ -1849,10 +1852,27 @@ static int do_execveat_common(int fd, struct filename *filename,
	if (displaced)
		reset_files_struct(displaced);
out_ret:
	if (filename)
		putname(filename);
	return retval;
}

static int do_execveat_common(int fd, struct filename *filename,
			      struct user_arg_ptr argv,
			      struct user_arg_ptr envp,
			      int flags)
{
	return __do_execve_file(fd, filename, argv, envp, flags, NULL);
}

int do_execve_file(struct file *file, void *__argv, void *__envp)
{
	struct user_arg_ptr argv = { .ptr.native = __argv };
	struct user_arg_ptr envp = { .ptr.native = __envp };

	return __do_execve_file(AT_FDCWD, NULL, argv, envp, 0, file);
}

int do_execve(struct filename *filename,
	const char __user *const __user *__argv,
	const char __user *const __user *__envp)
+1 −0
Original line number Diff line number Diff line
@@ -150,5 +150,6 @@ extern int do_execveat(int, struct filename *,
		       const char __user * const __user *,
		       const char __user * const __user *,
		       int);
int do_execve_file(struct file *file, void *__argv, void *__envp);

#endif /* _LINUX_BINFMTS_H */
+15 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_BPFILTER_H
#define _LINUX_BPFILTER_H

#include <uapi/linux/bpfilter.h>

struct sock;
int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char *optval,
			    unsigned int optlen);
int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char *optval,
			    int *optlen);
extern int (*bpfilter_process_sockopt)(struct sock *sk, int optname,
				       char __user *optval,
				       unsigned int optlen, bool is_set);
#endif
+12 −0
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ struct subprocess_info {
	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;
@@ -38,6 +40,16 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp,
			  int (*init)(struct subprocess_info *info, struct cred *new),
			  void (*cleanup)(struct subprocess_info *), void *data);

struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
			  int (*init)(struct subprocess_info *info, struct cred *new),
			  void (*cleanup)(struct subprocess_info *), void *data);
struct umh_info {
	struct file *pipe_to_umh;
	struct file *pipe_from_umh;
	pid_t pid;
};
int fork_usermode_blob(void *data, size_t len, struct umh_info *info);

extern int
call_usermodehelper_exec(struct subprocess_info *info, int wait);

+21 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _UAPI_LINUX_BPFILTER_H
#define _UAPI_LINUX_BPFILTER_H

#include <linux/if.h>

enum {
	BPFILTER_IPT_SO_SET_REPLACE = 64,
	BPFILTER_IPT_SO_SET_ADD_COUNTERS = 65,
	BPFILTER_IPT_SET_MAX,
};

enum {
	BPFILTER_IPT_SO_GET_INFO = 64,
	BPFILTER_IPT_SO_GET_ENTRIES = 65,
	BPFILTER_IPT_SO_GET_REVISION_MATCH = 66,
	BPFILTER_IPT_SO_GET_REVISION_TARGET = 67,
	BPFILTER_IPT_GET_MAX,
};

#endif /* _UAPI_LINUX_BPFILTER_H */
Loading