This commit adds syscall hook with seccomp. Using seccomp raises SIGSYS to UML process, which is captured in the (UML) kernel, then call the syscall entry point, __kernel_vsyscall, to hook the original syscall instructions.
The SIGSYS signal is raised upon the execution from uml_reserved and high_physmem, which locates userspace memory. Signed-off-by: Hajime Tazaki <thehaj...@gmail.com> Signed-off-by: Kenichi Yasukata <kenichi.yasuk...@gmail.com> --- arch/um/include/shared/kern_util.h | 8 +++ arch/um/include/shared/os.h | 10 +++ arch/um/kernel/um_arch.c | 3 + arch/um/nommu/Makefile | 3 + arch/um/nommu/os-Linux/Makefile | 7 +++ arch/um/nommu/os-Linux/signal.c | 15 +++++ arch/um/os-Linux/Makefile | 5 ++ arch/um/os-Linux/seccomp.c | 87 +++++++++++++++++++++++++++ arch/um/os-Linux/signal.c | 6 ++ arch/x86/um/nommu/Makefile | 2 +- arch/x86/um/nommu/os-Linux/Makefile | 6 ++ arch/x86/um/nommu/os-Linux/mcontext.c | 15 +++++ arch/x86/um/shared/sysdep/mcontext.h | 3 + 13 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 arch/um/nommu/Makefile create mode 100644 arch/um/nommu/os-Linux/Makefile create mode 100644 arch/um/nommu/os-Linux/signal.c create mode 100644 arch/um/os-Linux/seccomp.c create mode 100644 arch/x86/um/nommu/os-Linux/Makefile create mode 100644 arch/x86/um/nommu/os-Linux/mcontext.c diff --git a/arch/um/include/shared/kern_util.h b/arch/um/include/shared/kern_util.h index f21dc8517538..1dd3d02c4e39 100644 --- a/arch/um/include/shared/kern_util.h +++ b/arch/um/include/shared/kern_util.h @@ -67,4 +67,12 @@ void um_idle_sleep(void); void kasan_map_memory(void *start, size_t len); +#ifdef CONFIG_MMU +static inline void arch_sigsys_handler(int sig, struct siginfo *si, void *mc) +{ +} +#else +extern void arch_sigsys_handler(int sig, struct siginfo *si, void *mc); +#endif + #endif diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index 5babad8c5f75..d29b967a009e 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -334,4 +334,14 @@ extern void um_trace_signals_off(void); /* time-travel */ extern void deliver_time_travel_irqs(void); +/* seccomp.c */ +#ifdef CONFIG_MMU +static inline int os_setup_seccomp(void) +{ + return 0; +} +#else +extern int os_setup_seccomp(void); +#endif + #endif diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 62ddb865eb91..f56f14eaefc5 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -432,6 +432,9 @@ void __init setup_arch(char **cmdline_p) add_bootloader_randomness(rng_seed, sizeof(rng_seed)); memzero_explicit(rng_seed, sizeof(rng_seed)); } + + /* install seccomp filter */ + os_setup_seccomp(); } void __init arch_cpu_finalize_init(void) diff --git a/arch/um/nommu/Makefile b/arch/um/nommu/Makefile new file mode 100644 index 000000000000..baab7c2f57c2 --- /dev/null +++ b/arch/um/nommu/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y := os-Linux/ diff --git a/arch/um/nommu/os-Linux/Makefile b/arch/um/nommu/os-Linux/Makefile new file mode 100644 index 000000000000..68833c576437 --- /dev/null +++ b/arch/um/nommu/os-Linux/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y := signal.o +USER_OBJS := $(obj-y) + +include $(srctree)/arch/um/scripts/Makefile.rules +USER_CFLAGS+=-I$(srctree)/arch/um/os-Linux diff --git a/arch/um/nommu/os-Linux/signal.c b/arch/um/nommu/os-Linux/signal.c new file mode 100644 index 000000000000..844f6c840902 --- /dev/null +++ b/arch/um/nommu/os-Linux/signal.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <signal.h> +#include <kern_util.h> +#include <os.h> +#include <sysdep/mcontext.h> +#include <sys/ucontext.h> + +void arch_sigsys_handler(int sig, struct siginfo *si, void *ptr) +{ + mcontext_t *mc = (mcontext_t *) ptr; + + /* hook syscall via SIGSYS */ + mc_set_sigsys_hook(mc); +} diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index 331564888400..6bfdfec8cbcc 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile @@ -21,4 +21,9 @@ USER_OBJS := $(user-objs-y) elf_aux.o execvp.o file.o helper.o irq.o \ main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \ tty.o umid.o util.o +ifneq ($(CONFIG_MMU),y) +obj-y += seccomp.o +USER_OBJS += seccomp.o +endif + include $(srctree)/arch/um/scripts/Makefile.rules diff --git a/arch/um/os-Linux/seccomp.c b/arch/um/os-Linux/seccomp.c new file mode 100644 index 000000000000..d1cfa6e3d632 --- /dev/null +++ b/arch/um/os-Linux/seccomp.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/syscall.h> /* For SYS_xxx definitions */ +#include <init.h> +#include <as-layout.h> +#include <os.h> +#include <linux/filter.h> +#include <linux/seccomp.h> + +int __init os_setup_seccomp(void) +{ + int err; + unsigned long __userspace_start = uml_reserved, + __userspace_end = high_physmem; + + struct sock_filter filter[] = { + /* if (IP_high > __userspace_end) allow; */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer) + 4), + BPF_JUMP(BPF_JMP + BPF_JGT + BPF_K, __userspace_end >> 32, + /*true-skip=*/0, /*false-skip=*/1), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + + /* if (IP_high == __userspace_end && IP_low >= __userspace_end) allow; */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer) + 4), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __userspace_end >> 32, + /*true-skip=*/0, /*false-skip=*/3), + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer)), + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, __userspace_end, + /*true-skip=*/0, /*false-skip=*/1), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + + /* if (IP_high < __userspace_start) allow; */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer) + 4), + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, __userspace_start >> 32, + /*true-skip=*/1, /*false-skip=*/0), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + + /* if (IP_high == __userspace_start && IP_low < __userspace_start) allow; */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer) + 4), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __userspace_start >> 32, + /*true-skip=*/0, /*false-skip=*/3), + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, + offsetof(struct seccomp_data, instruction_pointer)), + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, __userspace_start, + /*true-skip=*/1, /*false-skip=*/0), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + + /* other address; trap */ + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRAP), + }; + struct sock_fprog prog = { + .len = ARRAY_SIZE(filter), + .filter = filter, + }; + + err = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + if (err) + os_warn("PR_SET_NO_NEW_PRIVS (err=%d, ernro=%d)\n", + err, errno); + + err = syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_TSYNC, &prog); + if (err) { + os_warn("SECCOMP_SET_MODE_FILTER (err=%d, ernro=%d)\n", + err, errno); + exit(1); + } + + set_handler(SIGSYS); + + os_info("seccomp: setup filter syscalls in the range: 0x%lx-0x%lx\n", + __userspace_start, __userspace_end); + + return 0; +} + diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 9ea7269ffb77..97efdabe1e45 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -172,12 +172,18 @@ void register_pm_wake_signal(void) set_handler(SIGUSR1); } +static void sigsys_handler(int sig, struct siginfo *unused_si, mcontext_t *mc) +{ + arch_sigsys_handler(sig, unused_si, mc); +} + static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { [SIGSEGV] = sig_handler, [SIGBUS] = sig_handler, [SIGILL] = sig_handler, [SIGFPE] = sig_handler, [SIGTRAP] = sig_handler, + [SIGSYS] = sigsys_handler, [SIGIO] = sig_handler, [SIGWINCH] = sig_handler, diff --git a/arch/x86/um/nommu/Makefile b/arch/x86/um/nommu/Makefile index d72c63afffa5..ebe47d4836f4 100644 --- a/arch/x86/um/nommu/Makefile +++ b/arch/x86/um/nommu/Makefile @@ -5,4 +5,4 @@ else BITS := 64 endif -obj-y = do_syscall_$(BITS).o entry_$(BITS).o +obj-y = do_syscall_$(BITS).o entry_$(BITS).o os-Linux/ diff --git a/arch/x86/um/nommu/os-Linux/Makefile b/arch/x86/um/nommu/os-Linux/Makefile new file mode 100644 index 000000000000..4571e403a6ff --- /dev/null +++ b/arch/x86/um/nommu/os-Linux/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y = mcontext.o +USER_OBJS := mcontext.o + +include $(srctree)/arch/um/scripts/Makefile.rules diff --git a/arch/x86/um/nommu/os-Linux/mcontext.c b/arch/x86/um/nommu/os-Linux/mcontext.c new file mode 100644 index 000000000000..d05722307acb --- /dev/null +++ b/arch/x86/um/nommu/os-Linux/mcontext.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/ucontext.h> +#define __FRAME_OFFSETS +#include <asm/ptrace.h> +#include <sysdep/ptrace.h> +#include <sysdep/mcontext.h> +#include <sysdep/syscalls.h> + +void mc_set_sigsys_hook(mcontext_t *mc) +{ + mc->gregs[REG_RSP] -= sizeof(unsigned long); + *((unsigned long *) (mc->gregs[REG_RSP])) = mc->gregs[REG_RIP]; + mc->gregs[REG_RCX] = mc->gregs[REG_RIP]; + mc->gregs[REG_RIP] = (unsigned long) __kernel_vsyscall; +} diff --git a/arch/x86/um/shared/sysdep/mcontext.h b/arch/x86/um/shared/sysdep/mcontext.h index b724c54da316..3f2dc3a2995c 100644 --- a/arch/x86/um/shared/sysdep/mcontext.h +++ b/arch/x86/um/shared/sysdep/mcontext.h @@ -7,6 +7,9 @@ #define __SYS_SIGCONTEXT_X86_H extern void get_regs_from_mc(struct uml_pt_regs *, mcontext_t *); +#ifndef CONFIG_MMU +extern void mc_set_sigsys_hook(mcontext_t *mc); +#endif #ifdef __i386__ -- 2.43.0