lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 6 Apr 2016 19:29:30 +0300
From:	Dmitry Safonov <dsafonov@...tuozzo.com>
To:	<linux-kernel@...r.kernel.org>
CC:	<luto@...capital.net>, <tglx@...utronix.de>, <mingo@...hat.com>,
	<hpa@...or.com>, <x86@...nel.org>, <shuahkh@....samsung.com>,
	<bp@...en8.de>, <akpm@...ux-foundation.org>,
	<linux-kselftest@...r.kernel.org>, <gorcunov@...nvz.org>,
	<xemul@...tuozzo.com>, <khorenko@...tuozzo.com>,
	<0x7f454c46@...il.com>, Dmitry Safonov <dsafonov@...tuozzo.com>
Subject: [PATCH 2/2] x86/tools/testing: add test for ARCH_SET_COMPAT

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@...nvz.org>
Cc: Pavel Emelyanov <xemul@...tuozzo.com>
Cc: Konstantin Khorenko <khorenko@...tuozzo.com>
CC: Dmitry Safonov <0x7f454c46@...il.com>
Signed-off-by: Dmitry Safonov <dsafonov@...tuozzo.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ