All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Safonov <dsafonov@virtuozzo.com>
To: <linux-kernel@vger.kernel.org>
Cc: <luto@amacapital.net>, <tglx@linutronix.de>, <mingo@redhat.com>,
	<hpa@zytor.com>, <x86@kernel.org>, <shuahkh@osg.samsung.com>,
	<bp@alien8.de>, <akpm@linux-foundation.org>,
	<linux-kselftest@vger.kernel.org>, <gorcunov@openvz.org>,
	<xemul@virtuozzo.com>, <khorenko@virtuozzo.com>,
	<0x7f454c46@gmail.com>, Dmitry Safonov <dsafonov@virtuozzo.com>
Subject: [PATCH 2/2] x86/tools/testing: add test for ARCH_SET_COMPAT
Date: Wed, 6 Apr 2016 19:29:30 +0300	[thread overview]
Message-ID: <1459960170-4454-3-git-send-email-dsafonov@virtuozzo.com> (raw)
In-Reply-To: <1459960170-4454-1-git-send-email-dsafonov@virtuozzo.com>

This is simple test to determine if arch_prctl(ARCH_SET_COMPAT) is
working by ptracing switched application with PTRACE_GETREGS -
it should return 32-bit registers set.

Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Konstantin Khorenko <khorenko@virtuozzo.com>
CC: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
 tools/testing/selftests/x86/Makefile               |   1 +
 .../testing/selftests/x86/arch_prctl_set_compat.c  | 295 +++++++++++++++++++++
 2 files changed, 296 insertions(+)
 create mode 100644 tools/testing/selftests/x86/arch_prctl_set_compat.c

diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index b47ebd170690..bdadd12698f4 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -9,6 +9,7 @@ TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_sysc
 TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
 			test_FCMOV test_FCOMI test_FISTTP \
 			vdso_restorer
+TARGETS_C_64BIT_ONLY := arch_prctl_set_compat
 
 TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
 TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY)
diff --git a/tools/testing/selftests/x86/arch_prctl_set_compat.c b/tools/testing/selftests/x86/arch_prctl_set_compat.c
new file mode 100644
index 000000000000..326a46d4b056
--- /dev/null
+++ b/tools/testing/selftests/x86/arch_prctl_set_compat.c
@@ -0,0 +1,295 @@
+/*
+ * arch_prctl_set_compat.c - tests switching to compatible mode from 64-bit
+ * Copyright (c) 2016 Dmitry Safonov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * This switches to compatible mode with the help from arch_prctl friend.
+ * Switching is simple syscall, but one need unmap every vma that is
+ * higher than 32-bit TASK_SIZE, make raw 32/64-bit syscalls.
+ * So this is also a really good example. By the end tracee is
+ * compatible task that makes 32-bit syscalls to stop itself.
+ * For returning in some 32-bit code it may be handy to use sigreturn
+ * there with formed frame.
+ *
+ * Switching from 32-bit compatible application to native is just one
+ * arch_prctl syscall, so this is for harder task: switching from native to
+ * compat mode.
+ */
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <sys/syscall.h>
+#include <asm/prctl.h>
+#include <sys/prctl.h>
+#include <sys/personality.h>
+#include <sys/mman.h>
+
+#include <sys/uio.h>
+#include <sys/ptrace.h>
+#include <linux/elf.h>
+#include <sys/wait.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define __stringify_1(x...)	#x
+#define __stringify(x...)	__stringify_1(x)
+
+#ifndef ARCH_SET_COMPAT
+#define ARCH_SET_COMPAT		0x2001
+#define ARCH_SET_NATIVE		0x2002
+#define ARCH_GET_PERSONALITY	0x2003
+#endif
+
+#define PAGE_SIZE		4096
+#define TASK_SIZE_MAX		((1UL << 47) - PAGE_SIZE)
+#define IA32_PAGE_OFFSET	0xFFFFe000
+
+/* Just a typical random stack on x86_64 compatible task */
+#define STACK_START		0xffdb8000
+#define STACK_END		0xffdd9000
+
+/* Some empty randoms inside compatible address space */
+#define ARG_START 0xf77c8000
+#define ARG_END 0xf77c8000
+#define ENV_START 0xf77c8000
+#define ENV_END 0xf77c8000
+
+/*
+ * After removing all mappings higher than compatible TASK_SIZE,
+ * we remove libc mapping. That's the reason for plain syscalls
+ */
+#define __NR_munmap		11
+#define __NR_arch_prctl		158
+
+#define __NR32_getpid		20
+#define __NR32_kill		37
+
+/* unmaps everything above IA32_PAGE_OFFSET */
+static inline void unmap_uncompat_mappings(void)
+{
+	unsigned long addr = IA32_PAGE_OFFSET;
+	unsigned long len = TASK_SIZE_MAX - IA32_PAGE_OFFSET;
+
+	asm volatile(
+		"	movq $"__stringify(__NR_munmap)", %%rax\n"
+		"	syscall\n"
+		:
+		: "D" (addr), "S" (len)
+	);
+}
+
+static inline void sys_arch_prctl(int code, unsigned long addr)
+{
+	asm volatile(
+		"	movq $"__stringify(__NR_arch_prctl)", %%rax\n"
+		"	syscall\n"
+		:
+		: "D" (code), "S" (addr)
+	);
+}
+
+static inline void
+prctl_print(int opcode, unsigned long arg1, unsigned long arg2)
+{
+	long ret = syscall(SYS_prctl, opcode, arg1, arg2, 0, 0);
+
+	if (ret)
+		fprintf(stderr, "[ERR]\tprctl failed with %ld : %m\n", ret);
+}
+
+/*
+ * Runed in different task just for test purposes:
+ * tracer with the help of PTRACE_GETREGS will fetch it's registers set size
+ * and determine, if it's compatible task.
+ * Then tracer will kill tracee, sorry for it.
+ */
+void tracee_func(void)
+{
+	personality(PER_LINUX32);
+
+	/* emptify arg & env, moving them to compatible address space */
+	prctl_print(PR_SET_MM, PR_SET_MM_ARG_START, ARG_START);
+	prctl_print(PR_SET_MM, PR_SET_MM_ARG_END, ARG_END);
+	prctl_print(PR_SET_MM, PR_SET_MM_ENV_START, ENV_START);
+	prctl_print(PR_SET_MM, PR_SET_MM_ENV_END, ENV_END);
+
+	/* stack: get a new one */
+	if (mmap((void *)STACK_START, STACK_END - STACK_START,
+				PROT_READ | PROT_WRITE,
+				MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0)
+			== MAP_FAILED) {
+		fprintf(stderr, "[ERR]\tfailed to mmap new stack : %m\n");
+	} else {
+		prctl_print(PR_SET_MM, PR_SET_MM_START_STACK, STACK_START);
+		asm volatile (
+				"	mov %%rax,%%rsp\n"
+				:
+				: "a" (STACK_END));
+	}
+	/* we are cool guys: we have our own stack */
+
+	unmap_uncompat_mappings();
+	/*
+	 * we are poor boys: unmapped everything with glibc,
+	 * do not use it from now - we are on our own!
+	 */
+
+	sys_arch_prctl(ARCH_SET_COMPAT, 0);
+
+	/* Now switch to compatibility mode */
+	asm volatile (
+		"	pushq $0x23\n"	/* USER32_CS */
+		"	pushq $1f\n"
+		"	lretq\n"
+		/* here we are: ready to execute some 32-bit code */
+		"1:\n"
+		".code32\n"
+		/* getpid() */
+		"	movl $"__stringify(__NR32_getpid)", %eax\n"
+		"	int $0x80\n"
+		"	movl %eax, %ebx\n" /* pid */
+		/* raise SIGSTOP */
+		"	movl $"__stringify(__NR32_kill)", %eax\n"
+		"	movl $19, %ecx\n"
+		"	int $0x80\n"
+		".code64\n"
+	);
+
+}
+
+typedef struct {
+	uint64_t	r15, r14, r13, r12, bp, bx, r11, r10, r9, r8;
+	uint64_t	ax, cx, dx, si, di, orig_ax, ip, cs, flags;
+	uint64_t	sp, ss, fs_base, gs_base, ds, es, fs, gs;
+} user_regs_64;
+
+typedef struct {
+	uint32_t	bx, cx, dx, si, di, bp, ax, ds, es, fs, gs;
+	uint32_t	orig_ax, ip, cs, flags, sp, ss;
+} user_regs_32;
+
+typedef union {
+	user_regs_64 native;
+	user_regs_32 compat;
+} user_regs_struct_t;
+
+int ptrace_task_compatible(pid_t pid)
+{
+	struct iovec iov;
+	user_regs_struct_t r;
+	size_t reg_size = sizeof(user_regs_64);
+
+	iov.iov_base = &r;
+	iov.iov_len = reg_size;
+
+	errno = 0;
+	if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov)) {
+		fprintf(stderr, "[NOTE]\tCan't get register set: PTRACE_GETREGSET failed for pid %d : %m\n",
+			pid);
+		return 0;
+	}
+
+	return iov.iov_len == sizeof(user_regs_32);
+}
+
+void dump_proc_maps(pid_t pid)
+{
+#define BUF_SIZE	1024
+	char buf[BUF_SIZE];
+	int fd;
+	size_t nread;
+
+	snprintf(buf, BUF_SIZE, "/proc/%d/maps", pid);
+	fd = open(buf, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "[NOTE]\tCant open %s to dump : %s\n", buf);
+		return;
+	}
+
+	while ((nread = read(fd, buf, sizeof(buf))) > 0)
+		fwrite(buf, 1, nread, stdout);
+
+	close(fd);
+}
+
+int main(int argc, char **argv)
+{
+	pid_t pid;
+	int ret = 0;
+	int status;
+	int in_compat = syscall(SYS_arch_prctl, 0x2003, 0);
+	int dump_maps = 0;
+
+	if (in_compat < 0) {
+		fprintf(stderr,
+			"[ERR]\tSYS_arch_prctl returned %d : %m\n", in_compat);
+	}
+
+	if (in_compat == 1) {
+		fprintf(stderr, "[SKIP]\tRun in 64-bit x86 userspace\n");
+		return 0;
+	}
+
+	if (argc > 1)
+		dump_maps = !(strcmp(argv[1], "--dump-proc"));
+
+	if (dump_maps)
+		dump_proc_maps(getpid());
+
+	fflush(NULL);
+	pid = fork();
+	if (pid < 0) {
+		fprintf(stderr, "[SKIP]\tCan't fork : %m\n");
+		return 1;
+	}
+
+	if (pid == 0) {/* child */
+		ptrace(PTRACE_TRACEME, 0, 0, 0);
+		tracee_func();
+	}
+
+	/* parent, the tracer */
+	waitpid(pid, &status, 0);
+	if (WIFEXITED(status)) {
+		fprintf(stderr, "[FAIL]\tTest was suddenly killed\n");
+		return 2;
+	}
+
+	if (WIFSIGNALED(status)) {
+		fprintf(stderr, "[FAIL]\tTest killed with signal %d\n",
+				WTERMSIG(status));
+		return 3;
+	}
+
+	if (!WIFSTOPPED(status))
+		fprintf(stderr, "[NOTE]\twaitpid() returned, but tracee wasn't stopped\n");
+
+	if (!ptrace_task_compatible) {
+		fprintf(stderr, "[FAIL]\tTask didn't become compatible\n");
+		ret = 4;
+	}
+
+	if (dump_maps)
+		dump_proc_maps(pid);
+
+	kill(pid, SIGKILL);
+	fprintf(stderr, "[OK]\tSuccessfuly changed mode to compatible\n");
+
+	return ret;
+}
+
-- 
2.7.4

      parent reply	other threads:[~2016-04-06 16:45 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-06 16:29 [PATCH 0/2] x86: add arch_prctl to switch between native/compat modes Dmitry Safonov
2016-04-06 16:29 ` [PATCH 1/2] x86/arch_prctl: add ARCH_SET_{COMPAT,NATIVE} to change compatible mode Dmitry Safonov
2016-04-06 18:04   ` Andy Lutomirski
2016-04-06 18:49     ` Andy Lutomirski
2016-04-07 12:11     ` Dmitry Safonov
2016-04-07 12:21       ` Cyrill Gorcunov
2016-04-07 12:35         ` Dmitry Safonov
2016-04-07 14:39       ` Andy Lutomirski
2016-04-07 15:18         ` Dmitry Safonov
2016-04-08 13:50         ` Dmitry Safonov
2016-04-08 15:56           ` Andy Lutomirski
2016-04-08 16:18             ` Dmitry Safonov
2016-04-08 20:44               ` Andy Lutomirski
2016-04-09  8:06                 ` Dmitry Safonov
2016-04-13 16:55                 ` Dmitry Safonov
2016-04-14 18:27                   ` Andy Lutomirski
2016-04-20 11:04                     ` Peter Zijlstra
2016-04-20 15:40                       ` Andy Lutomirski
2016-04-20 19:05                         ` Peter Zijlstra
2016-04-21 19:39                           ` Andy Lutomirski
2016-04-21 20:12                             ` Peter Zijlstra
2016-04-21 23:27                               ` Andy Lutomirski
2016-04-21 23:46                                 ` Andy Lutomirski
2016-04-25 15:16                                 ` Peter Zijlstra
2016-04-25 16:50                                   ` Andy Lutomirski
2016-04-06 16:29 ` Dmitry Safonov [this message]

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=1459960170-4454-3-git-send-email-dsafonov@virtuozzo.com \
    --to=dsafonov@virtuozzo.com \
    --cc=0x7f454c46@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=bp@alien8.de \
    --cc=gorcunov@openvz.org \
    --cc=hpa@zytor.com \
    --cc=khorenko@virtuozzo.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=mingo@redhat.com \
    --cc=shuahkh@osg.samsung.com \
    --cc=tglx@linutronix.de \
    --cc=x86@kernel.org \
    --cc=xemul@virtuozzo.com \
    /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.