All of lore.kernel.org
 help / color / mirror / Atom feed
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, &regs))
+		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, &regs))
+			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, &regs))
+			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, &regs))
+		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, &regs))
+			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, &regs))
+			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


  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: link
Be 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.