From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751983AbcDFQpe (ORCPT ); Wed, 6 Apr 2016 12:45:34 -0400 Received: from mail-db3on0144.outbound.protection.outlook.com ([157.55.234.144]:32402 "EHLO emea01-db3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751729AbcDFQpa (ORCPT ); Wed, 6 Apr 2016 12:45:30 -0400 Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=none action=none header.from=virtuozzo.com; From: Dmitry Safonov To: CC: , , , , , , , , , , , , <0x7f454c46@gmail.com>, Dmitry Safonov Subject: [PATCH 2/2] x86/tools/testing: add test for ARCH_SET_COMPAT Date: Wed, 6 Apr 2016 19:29:30 +0300 Message-ID: <1459960170-4454-3-git-send-email-dsafonov@virtuozzo.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1459960170-4454-1-git-send-email-dsafonov@virtuozzo.com> References: <1459960170-4454-1-git-send-email-dsafonov@virtuozzo.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [195.214.232.10] X-ClientProxiedBy: AM4PR01CA0012.eurprd01.prod.exchangelabs.com (10.164.74.150) To DB5PR08MB0983.eurprd08.prod.outlook.com (10.166.13.154) X-MS-Office365-Filtering-Correlation-Id: cf1b4e4b-e6c2-4570-5915-08d35e38c14e X-Microsoft-Exchange-Diagnostics: 1;DB5PR08MB0983;2:dZ4yn+GuohldNlTaDyznqu+okyesZCJTdkXmeYm2EIqJDUHau4w4rND9/V8cWSaN2wDl3lgi4UZhl4EU5W0reK2OCWpcswMpNwgp9ychEAr5fvwqg9YErJ457cDh89f8ExfEGec5hSNOjjYICCbWwNyg4mfmFhj880qQwKbj9SFjhYoBCerx41ZHl5MM7wff;3:M3M9wzH0i7PiYYXgku3vne1ZHsnUsPW/nq0DdMBNPHNtWbQtuTIoM9tmRnUNzGxLPzMooJiK51bGBiYQ2c31N69IuLDwcdnRMZKDbeYxWGw+OMyulNZ+v7kg3A05Tri3;25:gIHii67FO0zj/KwN+pgGreO2mLr9Tz19NeBZNYY5u9ouY8tRhEOkKPnNMiHz2nsYP1D3DkYSOSbyRGBl/nF5fWQfgPCCtaoagD/Gh3ykvcfZ4sArygXaagB5FoIfwrrAImIAIDgHoJRwzV0Mg99sRvnk8FRwm3rbjAKKzQu7wTlG6ALF5+CgfZSbqzFFwzLEF4z2cYhWHCPuMUOy88Ub6TuDyx2Imvsy+TaVWdC7XgQR7UDgkhlzJzceOx2MGk1LPwn434QIfoVNumBD1GC/nl+2Crj8vVonvvdSkqNNtEjwMzMrSU4W9lXTjTz/+FGXJBlIZmLXC8M8n+HfhNlf1Jy4D1ywKycY/JRXNgsL4vv2Mkr9wd6BWZGx8GXc2y00b4xaLw86/ERaKpJFdoU/6oMbVCOfTKxOWzRAo69N+oVs8+oiRwKpXnwRYaGLtTKTXfCjMGIQzhQxPFtuRxR8Ws7xbfGFP15fpJpixxygWBBfnGZYQtmxLtigMuszzpLWik5ssVB4CSXs6vtp9jRQyvTJTtAa5z5M/xSzSGFnHSlrPs8TDWfjz1dg5wxcTnAt X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DB5PR08MB0983; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040074)(601004)(2401047)(8121501046)(5005006)(10201501046)(3002001)(6041046)(6043046);SRVR:DB5PR08MB0983;BCL:0;PCL:0;RULEID:;SRVR:DB5PR08MB0983; X-Microsoft-Exchange-Diagnostics: 1;DB5PR08MB0983;4:iWuAy+xgIGndCyAnLsDZYd1x3SQflkMUQMRI1c9zVmTN/18d0GGOjng4vVRxs4ulpZnVOph648y41l95azAUloBuGxifY06tmTXlkJ3u5S4HUvYr3iWCtBKLFydCTcgEu1/MtDSUI2Kk2z8wqphuG7hgg5117uYMhqAsm4YOaqWsAB8oGblMN1pC1rnsuHkqMHlDWMkMdS+rzhhnT7gifBRyp4H7ivlFQ5ciccAhYOdLbbGfIgHnmOjjHT2DLDMksN376E71nRdf0T/JrGJ1djre3O1TzBgEdNg08CFVRLxxvE0YVm4ShQ2Hkek/dyZ9k46K2EdxbrK32A0+xtYyyzA20tR1L9RVUCUji0cABlPXH34mCe7kuE6W2Mjcx+B+LiV2/OBc9OUlmReyv+yn9AGxTThirsg8VBUV6mSqzyI= X-Forefront-PRVS: 0904004ECB X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6009001)(189998001)(48376002)(5004730100002)(81166005)(50466002)(19580405001)(19580395003)(76176999)(86362001)(110136002)(42186005)(66066001)(107886002)(36756003)(4326007)(92566002)(53416004)(3846002)(2950100001)(2906002)(586003)(50986999)(77096005)(1096002)(6116002)(5008740100001)(229853001)(4001430100002)(5003940100001)(33646002)(50226001)(2351001);DIR:OUT;SFP:1102;SCL:1;SRVR:DB5PR08MB0983;H:dsafonov.sw.ru;FPR:;SPF:None;MLV:sfv;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;DB5PR08MB0983;23:4lU0Wvylhf0vpV/Uu3NNfssdP+kdINeThmesKz2u/whNiJDm3m066v+9t9OourrM4oU0rfXwdBSIwS3Y5MthrMkQSI6XPKobUVTEip7k5dnOJvWX/AP2dmr9xf+95qG9TP3M27XLGwE7LXrg9AMFtxxaSqtnkBS3hFqs2f8rKVsW5IIo0agrkuF14AT2ZdCSTs/0R6DH72binMSVo7M7G1WhWgSuRf5HFq7CFPQJgFI/KYersO8PlzMcPmXuLu+KwHJ6ZA27EuVAd2wPUEne8XqwGhqYZO51v7dt/TXybpGwIe8f1AlAlxaxmW9o/zjsGzp3asg2/nFAMG5vfSMAWYQYgHtpkeQ+Bn9rp1uGNJynlzXXmJ/lH3wMoqDd5Zh4Xs9nIiAnqX3RVr8Du8YgMPLqeeKd9/zlzqyHtdUnyfRd8g6JKFCvVD+ad3LuMgSIvbzobkJphr2vn65lVSKZrkfploumLuBb68A2DwraJIbAjqFSoiBAdurWWkDEe0d6o2sUID7+kldvG4R9Z2gZIfojQTWC+hKfKCSOT7PCSjfWCHIlKABQNHK/GZaW0mKUEd9JEXl0Szaq9vQrQ6O3fwgr99pwMmzrF91UlqxFCpbBvTP85AUsHssqUNsLPD1f0Q0rCpWjA7rDc8ttE0P1gfxFsdovoe5K4FXzrOJ6KmK6foBSwWH19+ru3hcSRK2/kO4tyiQVdVpcPOo6rOez/ECZiHC3Oez8hzV9w1pugkxqXAQFTmdfzCvMFVwC+eGJgnxH+toWlj7tZQ5JHBeTTG/nwL54f8sVpeIKmOqySyogF0hnSUsssTZ4IONvrbux/mNMhY6g3s7WBOoVe59ov+MccRodSfgafv87QjYbdJahQl7pcGbmWQ4wjm7gkMQqbAaoOrH8f2ZtRFZBdfl1qKc/maoIo/18kM6S5iFeau9Rg+9a5vwycaPsGVumUkhW X-Microsoft-Exchange-Diagnostics: 1;DB5PR08MB0983;5:SUE3gmQzU0SHh/yQv+dvdQy0Z6uqBMNoLL1A1vfXW/A/bX73rr1UIm8fRhQ3u+4ur4lSiqOCn2XGepPgp+0eoDJvnQfu17gyGzHVTRHoHrI04snT/VNGs3w7K2BknSvpconcMtJR2hISoXF/H3Y87A==;24:ArhOddA8PRCZLKRwFGrSr7zl6eHDWk9wzV1tvqG5ltok+BQXmiKmha0OtwW1TFD1xow5clcBvmMQ45TQjmGXVluo/KwoTq+g0VcKicNx8Yo=;20:ulAh6U5QP+PXFjWHqD291VWonHFo7X4ypf6eOCO1DnzeGUlKw0mXsIq98BRBjlHx9xm+bJzZicEFOCHzEj/Wce12NV27/Cr7OmS/wd+A0xbYt/YHQQG9muQMzLpx7zQxI2yD+PeyEcZ41kr3+Swg4vOMVbxCO6gKPd6+CWmVyUE= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 Apr 2016 16:30:23.1344 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB5PR08MB0983 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 Cc: Pavel Emelyanov Cc: Konstantin Khorenko CC: Dmitry Safonov <0x7f454c46@gmail.com> Signed-off-by: Dmitry Safonov --- 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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