From: Andrei Vagin <avagin@gmail.com> To: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org Cc: linux-um@lists.infradead.org, criu@openvz.org, avagin@google.com, Andrei Vagin <avagin@gmail.com>, Andrew Morton <akpm@linux-foundation.org>, Andy Lutomirski <luto@kernel.org>, Anton Ivanov <anton.ivanov@cambridgegreys.com>, Christian Brauner <christian.brauner@ubuntu.com>, Dmitry Safonov <0x7f454c46@gmail.com>, Ingo Molnar <mingo@redhat.com>, Jeff Dike <jdike@addtoit.com>, Mike Rapoport <rppt@linux.ibm.com>, Michael Kerrisk <mtk.manpages@gmail.com>, Oleg Nesterov <oleg@redhat.com>, Peter Zijlstra <peterz@infradead.org>, Richard Weinberger <richard@nod.at>, Thomas Gleixner <tglx@linutronix.de> Subject: [PATCH 4/4] selftests: add tests for process_vm_exec Date: Tue, 13 Apr 2021 22:52:17 -0700 [thread overview] Message-ID: <20210414055217.543246-5-avagin@gmail.com> (raw) In-Reply-To: <20210414055217.543246-1-avagin@gmail.com> Output: $ make run_tests TAP version 13 1..4 # selftests: process_vm_exec: process_vm_exec # 1..1 # ok 1 275 ns/syscall # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 1 selftests: process_vm_exec: process_vm_exec # selftests: process_vm_exec: process_vm_exec_fault # 1..1 # ok 1 789 ns/signal # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 2 selftests: process_vm_exec: process_vm_exec_fault # selftests: process_vm_exec: ptrace_vm_exec # 1..1 # ok 1 1378 ns/syscall# Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 3 selftests: process_vm_exec: ptrace_vm_exec # selftests: process_vm_exec: process_vm_exec_syscall # 1..1 # ok 1 write works as expectd # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 4 selftests: process_vm_exec: process_vm_exec_syscall Signed-off-by: Andrei Vagin <avagin@gmail.com> --- .../selftests/process_vm_exec/Makefile | 7 ++ tools/testing/selftests/process_vm_exec/log.h | 26 ++++ .../process_vm_exec/process_vm_exec.c | 105 +++++++++++++++++ .../process_vm_exec/process_vm_exec_fault.c | 111 ++++++++++++++++++ .../process_vm_exec/process_vm_exec_syscall.c | 81 +++++++++++++ .../process_vm_exec/ptrace_vm_exec.c | 111 ++++++++++++++++++ 6 files changed, 441 insertions(+) create mode 100644 tools/testing/selftests/process_vm_exec/Makefile create mode 100644 tools/testing/selftests/process_vm_exec/log.h create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec.c create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c create mode 100644 tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c diff --git a/tools/testing/selftests/process_vm_exec/Makefile b/tools/testing/selftests/process_vm_exec/Makefile new file mode 100644 index 000000000000..bdf7fcf0fdd3 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +UNAME_M := $(shell uname -m) +TEST_GEN_PROGS_x86_64 := process_vm_exec process_vm_exec_fault ptrace_vm_exec process_vm_exec_syscall +TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) + +include ../lib.mk diff --git a/tools/testing/selftests/process_vm_exec/log.h b/tools/testing/selftests/process_vm_exec/log.h new file mode 100644 index 000000000000..ef268c2cf2b8 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/log.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SELFTEST_PROCESS_VM_EXEC_LOG_H__ +#define __SELFTEST_PROCESS_VM_EXEC_LOG_H__ + +#define pr_msg(fmt, lvl, ...) \ + ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \ + lvl, __FILE__, __LINE__, ##__VA_ARGS__) + +#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__) + +#define pr_err(fmt, ...) \ + ({ \ + ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_fail(fmt, ...) \ + ({ \ + ksft_test_result_fail(fmt "\n", ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__) + +#endif diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec.c b/tools/testing/selftests/process_vm_exec/process_vm_exec.c new file mode 100644 index 000000000000..aa4009c43e01 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <sys/prctl.h> +#include "asm/unistd.h" +#include <time.h> +#include <sys/mman.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#define TEST_SYSCALL 123 +#define TEST_SYSCALL_RET 456 +#define TEST_MARKER 789 +#define TEST_TIMEOUT 5 +#define TEST_STACK_SIZE 65536 + +static inline long __syscall1(long n, long a1) +{ + unsigned long ret; + + __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); + + return ret; +} + +int marker; + +static void guest(void) +{ + while (1) + if (__syscall1(TEST_SYSCALL, marker) != TEST_SYSCALL_RET) + abort(); +} + +int main(int argc, char **argv) +{ + struct sigcontext ctx = {}; + struct timespec start, cur; + int status, ret; + pid_t pid; + long sysnr; + void *stack; + + ksft_set_plan(1); + + stack = mmap(NULL, TEST_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (stack == MAP_FAILED) + return pr_perror("mmap"); + + pid = fork(); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + marker = TEST_MARKER; + kill(getpid(), SIGSTOP); + abort(); + return 0; + } + + ctx.rip = (long)guest; + ctx.rsp = (long)stack + TEST_STACK_SIZE; + ctx.cs = 0x33; + + sysnr = 0; + clock_gettime(CLOCK_MONOTONIC, &start); + while (1) { + unsigned long long sigmask = 0xffffffff; + siginfo_t siginfo; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + + ret = syscall(__NR_process_vm_exec, pid, &ctx, 0, &siginfo, &sigmask, 8); +#ifdef __DEBUG + ksft_print_msg("ret %d signo %d sysno %d ip %lx\n", + ret, siginfo.si_signo, siginfo.si_syscall, ctx.rip); +#endif + if (ret != 0) + pr_fail("unexpected return code: ret %d errno %d", ret, errno); + if (siginfo.si_signo != SIGSYS) + pr_fail("unexpected signal: %d", siginfo.si_signo); + if (siginfo.si_syscall != TEST_SYSCALL) + pr_fail("unexpected syscall: %d", siginfo.si_syscall); + ctx.rax = TEST_SYSCALL_RET; + sysnr++; + } + ksft_test_result_pass("%ld ns/syscall\n", 1000000000 / sysnr); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c b/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c new file mode 100644 index 000000000000..b2c49095f386 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <asm/unistd.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#define TEST_TIMEOUT 5 +#define TEST_STACK_SIZE 65536 + +#define TEST_VAL 0xaabbccddee + +unsigned long test_val; + +static inline void fault(unsigned long addr) +{ + unsigned long val = 0; + + __asm__ __volatile__ ( + "movq %%rcx, (%%rax)\n" + : + : "a"(addr), "c"(val) + :); +} + + +int marker; + +static void guest(void) +{ + unsigned long addr = 0; + + while (1) { + addr = (addr + 1) % 8; + fault(addr); + if (test_val != TEST_VAL) + _exit(1); + } +} + +int main(char argc, char **argv) +{ + siginfo_t siginfo; + unsigned long long sigmask = 0xffffffff; + struct sigcontext ctx = {}; + struct timespec start, cur; + unsigned long addr; + int status, ret; + char *stack; + pid_t pid; + long faults; + + ksft_set_plan(1); + + stack = mmap(NULL, TEST_STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (stack == MAP_FAILED) + return pr_perror("mmap"); + + pid = fork(); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + marker = 789; + kill(getpid(), SIGSTOP); + abort(); + return 0; + } + + ctx.rip = (long)guest; + ctx.rsp = (long)stack + TEST_STACK_SIZE; + ctx.cs = 0x33; + + faults = 0; + addr = 0; + clock_gettime(CLOCK_MONOTONIC, &start); + while (1) { + addr = (addr + 1) % 8; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + + ret = syscall(__NR_process_vm_exec, pid, &ctx, 0, &siginfo, &sigmask, 8); + if (addr % 8 != ctx.rax) + return pr_fail("unexpected address: %lx", addr); + ctx.rax = (long)&test_val; + ctx.rcx = TEST_VAL; + faults++; + } + ksft_test_result_pass("%ld ns/signal\n", 1000000000 / faults); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c b/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c new file mode 100644 index 000000000000..c0a7f6ee5b1a --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/prctl.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <asm/unistd.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#ifndef PROCESS_VM_EXEC_SYSCALL +#define PROCESS_VM_EXEC_SYSCALL 0x1 +#endif + +#define TEST_VAL 0x1e511e51 + +int test_val = TEST_VAL; + +int main(int argc, char **argv) +{ + struct sigcontext ctx = {}; + unsigned long long sigmask; + int ret, p[2], val; + siginfo_t siginfo; + pid_t pid; + + ksft_set_plan(1); + + pid = fork(); + if (pid < 0) + return pr_perror("fork"); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + kill(getpid(), SIGSTOP); + return 0; + } + + test_val = 0; + if (pipe(p)) + return pr_perror("pipe"); + + ctx.rax = __NR_write; + ctx.rdi = p[1]; + ctx.rsi = (unsigned long) &test_val; + ctx.rdx = sizeof(test_val); + ctx.r10 = 0; + ctx.r8 = 0; + ctx.r9 = 0; + sigmask = 0xffffffff; + ret = syscall(__NR_process_vm_exec, pid, &ctx, PROCESS_VM_EXEC_SYSCALL, + &siginfo, &sigmask, 8); + if (ret != 0) + return pr_perror("process_vm_exec"); + if (siginfo.si_signo != SIGSYS) + return pr_fail("unexpected signal: %d", siginfo.si_signo); + if (ctx.rax != sizeof(test_val)) + pr_fail("unexpected rax: %lx", ctx.rax); + if (kill(pid, SIGKILL)) + return pr_perror("kill"); + if (wait(NULL) != pid) + return pr_perror("kill"); + if (read(p[0], &val, sizeof(val)) != sizeof(val)) + pr_perror("read"); + if (val != TEST_VAL) + pr_fail("unexpected data: %x", val); + ksft_test_result_pass("process_vm_exec(..., PROCESS_VM_EXEC_SYSCALL, ...) \n"); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c b/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c new file mode 100644 index 000000000000..aac14c2e8f11 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <stdlib.h> +#include <unistd.h> +#include <linux/unistd.h> +#include <stdio.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <time.h> + +#include "../kselftest.h" +#include "log.h" + +static inline long __syscall1(long n, long a1) +{ + unsigned long ret; + + __asm__ __volatile__ ("syscall" + : "=a"(ret) + : "a"(n), "D"(a1) + : "rcx", "r11", "memory"); + return ret; +} + +#define TEST_SYSCALL 444 +#define TEST_SYSCALL_RET 555 +#define TEST_MARKER 789 +#define TEST_TIMEOUT 5 + +static int marker; + +static void guest(void) +{ + while (1) { + int ret; + + ret = __syscall1(TEST_SYSCALL, marker); + if (ret != TEST_SYSCALL_RET) + abort(); + } +} + +int main(int argc, char **argv) +{ + struct user_regs_struct regs = {}; + struct timespec start, cur; + int status; + long sysnr; + pid_t pid; + + ksft_set_plan(1); + + pid = fork(); + if (pid == 0) { + marker = TEST_MARKER; + kill(getpid(), SIGSTOP); + /* unreachable */ + abort(); + return 0; + } + + if (waitpid(pid, &status, WUNTRACED) != pid) + return pr_perror("waidpid"); + if (ptrace(PTRACE_ATTACH, pid, 0, 0)) + return pr_perror("PTRACE_ATTACH"); + if (wait(&status) != pid) + return pr_perror("waidpid"); + if (ptrace(PTRACE_CONT, pid, 0, 0)) + return pr_perror("PTRACE_CONT"); + if (waitpid(pid, &status, 0) != pid) + return pr_perror("waidpid"); + + if (ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL)) + return pr_perror("PTRACE_SETOPTIONS"); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_SETREGS"); + regs.rip = (long)guest; + + clock_gettime(CLOCK_MONOTONIC, &start); + for (sysnr = 0; ; sysnr++) { + int status; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_SETREGS"); + if (ptrace(PTRACE_SYSEMU, pid, 0, 0)) + return pr_perror("PTRACE_SYSEMU"); + if (waitpid(pid, &status, 0) != pid) + return pr_perror("waitpid"); + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) + return pr_err("unexpected status: %d", status); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_GETREGS: %d", regs.rdi); + if (regs.rdi != TEST_MARKER) + return pr_err("unexpected marker: %d", regs.rdi); + if (regs.orig_rax != TEST_SYSCALL) + return pr_err("unexpected syscall: %d", regs.orig_rax); + regs.rax = TEST_SYSCALL_RET; + } + ksft_test_result_pass("%ld ns/syscall\n", 1000000000 / sysnr); + ksft_exit_pass(); + return 0; +} -- 2.29.2
WARNING: multiple messages have this Message-ID (diff)
From: Andrei Vagin <avagin@gmail.com> To: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org Cc: linux-um@lists.infradead.org, criu@openvz.org, avagin@google.com, Andrei Vagin <avagin@gmail.com>, Andrew Morton <akpm@linux-foundation.org>, Andy Lutomirski <luto@kernel.org>, Anton Ivanov <anton.ivanov@cambridgegreys.com>, Christian Brauner <christian.brauner@ubuntu.com>, Dmitry Safonov <0x7f454c46@gmail.com>, Ingo Molnar <mingo@redhat.com>, Jeff Dike <jdike@addtoit.com>, Mike Rapoport <rppt@linux.ibm.com>, Michael Kerrisk <mtk.manpages@gmail.com>, Oleg Nesterov <oleg@redhat.com>, Peter Zijlstra <peterz@infradead.org>, Richard Weinberger <richard@nod.at>, Thomas Gleixner <tglx@linutronix.de> Subject: [PATCH 4/4] selftests: add tests for process_vm_exec Date: Tue, 13 Apr 2021 22:52:17 -0700 [thread overview] Message-ID: <20210414055217.543246-5-avagin@gmail.com> (raw) In-Reply-To: <20210414055217.543246-1-avagin@gmail.com> Output: $ make run_tests TAP version 13 1..4 # selftests: process_vm_exec: process_vm_exec # 1..1 # ok 1 275 ns/syscall # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 1 selftests: process_vm_exec: process_vm_exec # selftests: process_vm_exec: process_vm_exec_fault # 1..1 # ok 1 789 ns/signal # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 2 selftests: process_vm_exec: process_vm_exec_fault # selftests: process_vm_exec: ptrace_vm_exec # 1..1 # ok 1 1378 ns/syscall# Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 3 selftests: process_vm_exec: ptrace_vm_exec # selftests: process_vm_exec: process_vm_exec_syscall # 1..1 # ok 1 write works as expectd # # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 ok 4 selftests: process_vm_exec: process_vm_exec_syscall Signed-off-by: Andrei Vagin <avagin@gmail.com> --- .../selftests/process_vm_exec/Makefile | 7 ++ tools/testing/selftests/process_vm_exec/log.h | 26 ++++ .../process_vm_exec/process_vm_exec.c | 105 +++++++++++++++++ .../process_vm_exec/process_vm_exec_fault.c | 111 ++++++++++++++++++ .../process_vm_exec/process_vm_exec_syscall.c | 81 +++++++++++++ .../process_vm_exec/ptrace_vm_exec.c | 111 ++++++++++++++++++ 6 files changed, 441 insertions(+) create mode 100644 tools/testing/selftests/process_vm_exec/Makefile create mode 100644 tools/testing/selftests/process_vm_exec/log.h create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec.c create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c create mode 100644 tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c create mode 100644 tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c diff --git a/tools/testing/selftests/process_vm_exec/Makefile b/tools/testing/selftests/process_vm_exec/Makefile new file mode 100644 index 000000000000..bdf7fcf0fdd3 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +UNAME_M := $(shell uname -m) +TEST_GEN_PROGS_x86_64 := process_vm_exec process_vm_exec_fault ptrace_vm_exec process_vm_exec_syscall +TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) + +include ../lib.mk diff --git a/tools/testing/selftests/process_vm_exec/log.h b/tools/testing/selftests/process_vm_exec/log.h new file mode 100644 index 000000000000..ef268c2cf2b8 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/log.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SELFTEST_PROCESS_VM_EXEC_LOG_H__ +#define __SELFTEST_PROCESS_VM_EXEC_LOG_H__ + +#define pr_msg(fmt, lvl, ...) \ + ksft_print_msg("[%s] (%s:%d)\t" fmt "\n", \ + lvl, __FILE__, __LINE__, ##__VA_ARGS__) + +#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__) + +#define pr_err(fmt, ...) \ + ({ \ + ksft_test_result_error(fmt "\n", ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_fail(fmt, ...) \ + ({ \ + ksft_test_result_fail(fmt "\n", ##__VA_ARGS__); \ + -1; \ + }) + +#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__) + +#endif diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec.c b/tools/testing/selftests/process_vm_exec/process_vm_exec.c new file mode 100644 index 000000000000..aa4009c43e01 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <sys/prctl.h> +#include "asm/unistd.h" +#include <time.h> +#include <sys/mman.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#define TEST_SYSCALL 123 +#define TEST_SYSCALL_RET 456 +#define TEST_MARKER 789 +#define TEST_TIMEOUT 5 +#define TEST_STACK_SIZE 65536 + +static inline long __syscall1(long n, long a1) +{ + unsigned long ret; + + __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); + + return ret; +} + +int marker; + +static void guest(void) +{ + while (1) + if (__syscall1(TEST_SYSCALL, marker) != TEST_SYSCALL_RET) + abort(); +} + +int main(int argc, char **argv) +{ + struct sigcontext ctx = {}; + struct timespec start, cur; + int status, ret; + pid_t pid; + long sysnr; + void *stack; + + ksft_set_plan(1); + + stack = mmap(NULL, TEST_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (stack == MAP_FAILED) + return pr_perror("mmap"); + + pid = fork(); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + marker = TEST_MARKER; + kill(getpid(), SIGSTOP); + abort(); + return 0; + } + + ctx.rip = (long)guest; + ctx.rsp = (long)stack + TEST_STACK_SIZE; + ctx.cs = 0x33; + + sysnr = 0; + clock_gettime(CLOCK_MONOTONIC, &start); + while (1) { + unsigned long long sigmask = 0xffffffff; + siginfo_t siginfo; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + + ret = syscall(__NR_process_vm_exec, pid, &ctx, 0, &siginfo, &sigmask, 8); +#ifdef __DEBUG + ksft_print_msg("ret %d signo %d sysno %d ip %lx\n", + ret, siginfo.si_signo, siginfo.si_syscall, ctx.rip); +#endif + if (ret != 0) + pr_fail("unexpected return code: ret %d errno %d", ret, errno); + if (siginfo.si_signo != SIGSYS) + pr_fail("unexpected signal: %d", siginfo.si_signo); + if (siginfo.si_syscall != TEST_SYSCALL) + pr_fail("unexpected syscall: %d", siginfo.si_syscall); + ctx.rax = TEST_SYSCALL_RET; + sysnr++; + } + ksft_test_result_pass("%ld ns/syscall\n", 1000000000 / sysnr); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c b/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c new file mode 100644 index 000000000000..b2c49095f386 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec_fault.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <asm/unistd.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#define TEST_TIMEOUT 5 +#define TEST_STACK_SIZE 65536 + +#define TEST_VAL 0xaabbccddee + +unsigned long test_val; + +static inline void fault(unsigned long addr) +{ + unsigned long val = 0; + + __asm__ __volatile__ ( + "movq %%rcx, (%%rax)\n" + : + : "a"(addr), "c"(val) + :); +} + + +int marker; + +static void guest(void) +{ + unsigned long addr = 0; + + while (1) { + addr = (addr + 1) % 8; + fault(addr); + if (test_val != TEST_VAL) + _exit(1); + } +} + +int main(char argc, char **argv) +{ + siginfo_t siginfo; + unsigned long long sigmask = 0xffffffff; + struct sigcontext ctx = {}; + struct timespec start, cur; + unsigned long addr; + int status, ret; + char *stack; + pid_t pid; + long faults; + + ksft_set_plan(1); + + stack = mmap(NULL, TEST_STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (stack == MAP_FAILED) + return pr_perror("mmap"); + + pid = fork(); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + marker = 789; + kill(getpid(), SIGSTOP); + abort(); + return 0; + } + + ctx.rip = (long)guest; + ctx.rsp = (long)stack + TEST_STACK_SIZE; + ctx.cs = 0x33; + + faults = 0; + addr = 0; + clock_gettime(CLOCK_MONOTONIC, &start); + while (1) { + addr = (addr + 1) % 8; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + + ret = syscall(__NR_process_vm_exec, pid, &ctx, 0, &siginfo, &sigmask, 8); + if (addr % 8 != ctx.rax) + return pr_fail("unexpected address: %lx", addr); + ctx.rax = (long)&test_val; + ctx.rcx = TEST_VAL; + faults++; + } + ksft_test_result_pass("%ld ns/signal\n", 1000000000 / faults); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c b/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c new file mode 100644 index 000000000000..c0a7f6ee5b1a --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/process_vm_exec_syscall.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/prctl.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <asm/unistd.h> + +#include "../kselftest.h" +#include "log.h" + +#ifndef __NR_process_vm_exec +#define __NR_process_vm_exec 441 +#endif + +#ifndef PROCESS_VM_EXEC_SYSCALL +#define PROCESS_VM_EXEC_SYSCALL 0x1 +#endif + +#define TEST_VAL 0x1e511e51 + +int test_val = TEST_VAL; + +int main(int argc, char **argv) +{ + struct sigcontext ctx = {}; + unsigned long long sigmask; + int ret, p[2], val; + siginfo_t siginfo; + pid_t pid; + + ksft_set_plan(1); + + pid = fork(); + if (pid < 0) + return pr_perror("fork"); + if (pid == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + kill(getpid(), SIGSTOP); + return 0; + } + + test_val = 0; + if (pipe(p)) + return pr_perror("pipe"); + + ctx.rax = __NR_write; + ctx.rdi = p[1]; + ctx.rsi = (unsigned long) &test_val; + ctx.rdx = sizeof(test_val); + ctx.r10 = 0; + ctx.r8 = 0; + ctx.r9 = 0; + sigmask = 0xffffffff; + ret = syscall(__NR_process_vm_exec, pid, &ctx, PROCESS_VM_EXEC_SYSCALL, + &siginfo, &sigmask, 8); + if (ret != 0) + return pr_perror("process_vm_exec"); + if (siginfo.si_signo != SIGSYS) + return pr_fail("unexpected signal: %d", siginfo.si_signo); + if (ctx.rax != sizeof(test_val)) + pr_fail("unexpected rax: %lx", ctx.rax); + if (kill(pid, SIGKILL)) + return pr_perror("kill"); + if (wait(NULL) != pid) + return pr_perror("kill"); + if (read(p[0], &val, sizeof(val)) != sizeof(val)) + pr_perror("read"); + if (val != TEST_VAL) + pr_fail("unexpected data: %x", val); + ksft_test_result_pass("process_vm_exec(..., PROCESS_VM_EXEC_SYSCALL, ...) \n"); + ksft_exit_pass(); + return 0; +} diff --git a/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c b/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c new file mode 100644 index 000000000000..aac14c2e8f11 --- /dev/null +++ b/tools/testing/selftests/process_vm_exec/ptrace_vm_exec.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <stdlib.h> +#include <unistd.h> +#include <linux/unistd.h> +#include <stdio.h> +#include <sys/user.h> +#include <sys/uio.h> +#include <time.h> + +#include "../kselftest.h" +#include "log.h" + +static inline long __syscall1(long n, long a1) +{ + unsigned long ret; + + __asm__ __volatile__ ("syscall" + : "=a"(ret) + : "a"(n), "D"(a1) + : "rcx", "r11", "memory"); + return ret; +} + +#define TEST_SYSCALL 444 +#define TEST_SYSCALL_RET 555 +#define TEST_MARKER 789 +#define TEST_TIMEOUT 5 + +static int marker; + +static void guest(void) +{ + while (1) { + int ret; + + ret = __syscall1(TEST_SYSCALL, marker); + if (ret != TEST_SYSCALL_RET) + abort(); + } +} + +int main(int argc, char **argv) +{ + struct user_regs_struct regs = {}; + struct timespec start, cur; + int status; + long sysnr; + pid_t pid; + + ksft_set_plan(1); + + pid = fork(); + if (pid == 0) { + marker = TEST_MARKER; + kill(getpid(), SIGSTOP); + /* unreachable */ + abort(); + return 0; + } + + if (waitpid(pid, &status, WUNTRACED) != pid) + return pr_perror("waidpid"); + if (ptrace(PTRACE_ATTACH, pid, 0, 0)) + return pr_perror("PTRACE_ATTACH"); + if (wait(&status) != pid) + return pr_perror("waidpid"); + if (ptrace(PTRACE_CONT, pid, 0, 0)) + return pr_perror("PTRACE_CONT"); + if (waitpid(pid, &status, 0) != pid) + return pr_perror("waidpid"); + + if (ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL)) + return pr_perror("PTRACE_SETOPTIONS"); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_SETREGS"); + regs.rip = (long)guest; + + clock_gettime(CLOCK_MONOTONIC, &start); + for (sysnr = 0; ; sysnr++) { + int status; + + clock_gettime(CLOCK_MONOTONIC, &cur); + if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec || + (start.tv_sec + TEST_TIMEOUT == cur.tv_sec && + start.tv_nsec < cur.tv_nsec)) + break; + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_SETREGS"); + if (ptrace(PTRACE_SYSEMU, pid, 0, 0)) + return pr_perror("PTRACE_SYSEMU"); + if (waitpid(pid, &status, 0) != pid) + return pr_perror("waitpid"); + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) + return pr_err("unexpected status: %d", status); + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) + return pr_perror("PTRACE_GETREGS: %d", regs.rdi); + if (regs.rdi != TEST_MARKER) + return pr_err("unexpected marker: %d", regs.rdi); + if (regs.orig_rax != TEST_SYSCALL) + return pr_err("unexpected syscall: %d", regs.orig_rax); + regs.rax = TEST_SYSCALL_RET; + } + ksft_test_result_pass("%ld ns/syscall\n", 1000000000 / sysnr); + ksft_exit_pass(); + return 0; +} -- 2.29.2 _______________________________________________ linux-um mailing list linux-um@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-um
next prev parent reply other threads:[~2021-04-14 5:55 UTC|newest] Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-04-14 5:52 [PATCH 0/4 POC] Allow executing code and syscalls in another address space Andrei Vagin 2021-04-14 5:52 ` Andrei Vagin 2021-04-14 5:52 ` [PATCH 1/4] signal: add a helper to restore a process state from sigcontex Andrei Vagin 2021-04-14 5:52 ` Andrei Vagin 2021-04-14 5:52 ` [PATCH 2/4] arch/x86: implement the process_vm_exec syscall Andrei Vagin 2021-04-14 5:52 ` Andrei Vagin 2021-04-14 17:09 ` Oleg Nesterov 2021-04-14 17:09 ` Oleg Nesterov 2021-04-23 6:59 ` Andrei Vagin 2021-04-23 6:59 ` Andrei Vagin 2021-06-28 16:13 ` Jann Horn 2021-06-28 16:13 ` Jann Horn 2021-06-28 16:30 ` Andy Lutomirski 2021-06-28 17:14 ` Jann Horn 2021-06-28 17:14 ` Jann Horn 2021-06-28 18:18 ` Eric W. Biederman 2021-06-28 18:18 ` Eric W. Biederman 2021-06-29 1:01 ` Andrei Vagin 2021-06-29 1:01 ` Andrei Vagin 2021-07-02 6:22 ` Andrei Vagin 2021-07-02 6:22 ` Andrei Vagin 2021-07-02 11:51 ` Jann Horn 2021-07-02 11:51 ` Jann Horn 2021-07-02 11:51 ` Jann Horn 2021-07-02 20:40 ` Andy Lutomirski 2021-07-02 20:40 ` Andy Lutomirski 2021-07-02 8:51 ` Peter Zijlstra 2021-07-02 8:51 ` Peter Zijlstra 2021-07-02 22:21 ` Andrei Vagin 2021-07-02 22:21 ` Andrei Vagin 2021-07-02 20:56 ` Jann Horn 2021-07-02 20:56 ` Jann Horn 2021-07-02 22:48 ` Andrei Vagin 2021-07-02 22:48 ` Andrei Vagin 2021-04-14 5:52 ` [PATCH 3/4] arch/x86: allow to execute syscalls via process_vm_exec Andrei Vagin 2021-04-14 5:52 ` Andrei Vagin 2021-04-14 5:52 ` Andrei Vagin [this message] 2021-04-14 5:52 ` [PATCH 4/4] selftests: add tests for process_vm_exec Andrei Vagin 2021-04-14 6:46 ` [PATCH 0/4 POC] Allow executing code and syscalls in another address space Jann Horn 2021-04-14 6:46 ` Jann Horn 2021-04-14 22:10 ` Andrei Vagin 2021-04-14 22:10 ` Andrei Vagin 2021-07-02 6:57 ` Andrei Vagin 2021-07-02 6:57 ` Andrei Vagin 2021-07-02 15:12 ` Jann Horn 2021-07-02 15:12 ` Jann Horn 2021-07-02 15:12 ` Jann Horn 2021-07-18 0:38 ` Andrei Vagin 2021-07-18 0:38 ` Andrei Vagin 2021-04-14 7:22 ` Anton Ivanov 2021-04-14 7:22 ` Anton Ivanov 2021-04-14 7:34 ` Johannes Berg 2021-04-14 7:34 ` Johannes Berg 2021-04-14 9:24 ` Benjamin Berg 2021-04-14 9:24 ` Benjamin Berg 2021-04-14 10:27 ` Florian Weimer 2021-04-14 10:27 ` Florian Weimer 2021-04-14 11:24 ` Jann Horn 2021-04-14 11:24 ` Jann Horn 2021-04-14 12:20 ` Florian Weimer 2021-04-14 12:20 ` Florian Weimer 2021-04-14 13:58 ` Jann Horn 2021-04-14 13:58 ` Jann Horn 2021-04-16 19:29 ` Kirill Smelkov 2021-04-16 19:29 ` Kirill Smelkov 2021-04-17 16:28 ` sbaugh 2021-04-17 16:28 ` sbaugh 2021-07-02 22:44 ` Andy Lutomirski 2021-07-02 22:44 ` Andy Lutomirski 2021-07-18 1:34 ` Andrei Vagin 2021-07-18 1:34 ` Andrei Vagin
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20210414055217.543246-5-avagin@gmail.com \ --to=avagin@gmail.com \ --cc=0x7f454c46@gmail.com \ --cc=akpm@linux-foundation.org \ --cc=anton.ivanov@cambridgegreys.com \ --cc=avagin@google.com \ --cc=christian.brauner@ubuntu.com \ --cc=criu@openvz.org \ --cc=jdike@addtoit.com \ --cc=linux-api@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-um@lists.infradead.org \ --cc=luto@kernel.org \ --cc=mingo@redhat.com \ --cc=mtk.manpages@gmail.com \ --cc=oleg@redhat.com \ --cc=peterz@infradead.org \ --cc=richard@nod.at \ --cc=rppt@linux.ibm.com \ --cc=tglx@linutronix.de \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.