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:   Fri, 11 Jun 2021 19:02:42 +0100
From:   Dmitry Safonov <dima@...sta.com>
To:     linux-kernel@...r.kernel.org
Cc:     Dmitry Safonov <0x7f454c46@...il.com>,
        Dmitry Safonov <dima@...sta.com>,
        Alexander Viro <viro@...iv.linux.org.uk>,
        Andrew Morton <akpm@...ux-foundation.org>,
        Andy Lutomirski <luto@...nel.org>,
        Arnd Bergmann <arnd@...db.de>, Borislav Petkov <bp@...en8.de>,
        Catalin Marinas <catalin.marinas@....com>,
        Christophe Leroy <christophe.leroy@...roup.eu>,
        Guo Ren <guoren@...nel.org>, "H. Peter Anvin" <hpa@...or.com>,
        Ingo Molnar <mingo@...hat.com>,
        Oleg Nesterov <oleg@...hat.com>,
        Russell King <linux@...linux.org.uk>,
        Thomas Bogendoerfer <tsbogend@...ha.franken.de>,
        Thomas Gleixner <tglx@...utronix.de>,
        Vincenzo Frascino <vincenzo.frascino@....com>,
        Will Deacon <will@...nel.org>, x86@...nel.org,
        Shuah Khan <shuah@...nel.org>
Subject: [PATCH v3 23/23] x86/vdso/selftest: Add a test for unmapping vDSO

Output for landing on x86:
> [root@...alhost ~]# ./test_munmap_vdso_64
>	AT_SYSINFO_EHDR is 0x7fffead9f000
> [NOTE]	unmapping vDSO: [0x7fffead9f000, 0x7fffeada0000]
> [NOTE]	vDSO partial move failed, will try with bigger size
> [NOTE]	unmapping vDSO: [0x7fffead9f000, 0x7fffeada1000]
> [OK]
> [root@...alhost ~]# ./test_munmap_vdso_32
>	AT_SYSINFO_EHDR is 0xf7eef000
> [NOTE]	unmapping vDSO: [0xf7eef000, 0xf7ef0000]
> [NOTE]	vDSO partial move failed, will try with bigger size
> [NOTE]	unmapping vDSO: [0xf7eef000, 0xf7ef1000]
> [OK]

The test also can check force_sigsegv(SIGSEGV) in do_fast_syscall_32():
> [root@...alhost ~]# ./test_munmap_vdso_32 sysenter
> [NOTE]	Using sysenter after munmap
>	AT_SYSINFO_EHDR is 0xf7efe000
> [NOTE]	unmapping vDSO: [0xf7efe000, 0xf7eff000]
> [NOTE]	vDSO partial move failed, will try with bigger size
> [NOTE]	unmapping vDSO: [0xf7efe000, 0xf7f00000]
> [OK]	32-bit process gets segfault on fast syscall with unmapped vDSO

Cc: Shuah Khan <shuah@...nel.org>
Signed-off-by: Dmitry Safonov <dima@...sta.com>
---
 tools/testing/selftests/x86/.gitignore        |   1 +
 tools/testing/selftests/x86/Makefile          |  11 +-
 .../testing/selftests/x86/test_munmap_vdso.c  | 151 ++++++++++++++++++
 3 files changed, 158 insertions(+), 5 deletions(-)
 create mode 100644 tools/testing/selftests/x86/test_munmap_vdso.c

diff --git a/tools/testing/selftests/x86/.gitignore b/tools/testing/selftests/x86/.gitignore
index 1aaef5bf119a..9ce8337e8fa0 100644
--- a/tools/testing/selftests/x86/.gitignore
+++ b/tools/testing/selftests/x86/.gitignore
@@ -6,6 +6,7 @@ sysret_ss_attrs
 syscall_nt
 ptrace_syscall
 test_mremap_vdso
+test_munmap_vdso
 check_initial_reg_state
 sigreturn
 ldt_gdt
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 333980375bc7..43016351ddb3 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -10,12 +10,13 @@ CAN_BUILD_I386 := $(shell ./check_cc.sh $(CC) trivial_32bit_program.c -m32)
 CAN_BUILD_X86_64 := $(shell ./check_cc.sh $(CC) trivial_64bit_program.c)
 CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie)
 
-TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
-			check_initial_reg_state sigreturn iopl ioperm \
-			test_vsyscall mov_ss_trap \
+TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt	\
+			test_mremap_vdso test_munmap_vdso		\
+			check_initial_reg_state sigreturn iopl ioperm	\
+			test_vsyscall mov_ss_trap			\
 			syscall_arg_fault fsgsbase_restore
-TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
-			test_FCMOV test_FCOMI test_FISTTP \
+TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso	\
+			test_FCMOV test_FCOMI test_FISTTP		\
 			vdso_restorer
 TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering
 # Some selftests require 32bit support enabled also on 64bit systems
diff --git a/tools/testing/selftests/x86/test_munmap_vdso.c b/tools/testing/selftests/x86/test_munmap_vdso.c
new file mode 100644
index 000000000000..f56433dae279
--- /dev/null
+++ b/tools/testing/selftests/x86/test_munmap_vdso.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 32/64-bit test to check vDSO munmap.
+ *
+ * Copyright (c) 2021 Dmitry Safonov
+ */
+/*
+ * Can be built statically:
+ * gcc -Os -Wall -static -m32 test_munmap_vdso.c
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/auxv.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+
+#define PAGE_SIZE	4096
+
+static int try_to_unmap(void *vdso_addr, unsigned long size)
+{
+	int ret;
+
+	printf("[NOTE]\tunmapping vDSO: [%p, %#lx]\n",
+		vdso_addr, (unsigned long)vdso_addr + size);
+	fflush(stdout);
+
+#ifdef __i386__
+	/* vDSO is a landing for fast syscalls - don't use it for munmap() */
+	asm volatile ("int $0x80" : "=a" (ret)
+			: "a" (SYS_munmap),
+			  "b" (vdso_addr),
+			  "c" (size));
+	errno = -ret;
+#else /* __x86_64__ */
+	ret = munmap(vdso_addr, size);
+#endif
+	if (ret) {
+		if (errno == EINVAL) {
+			printf("[NOTE]\tvDSO partial move failed, will try with bigger size\n");
+			return -1; /* Retry with larger */
+		}
+		printf("[FAIL]\tmunmap failed (%d): %m\n", errno);
+		return 1;
+	}
+
+	return 0;
+}
+
+int main(int argc, char **argv, char **envp)
+{
+	pid_t child;
+
+#ifdef __i386__
+	enum syscall_type_t {
+		INT80, SYSCALL32, SYSENTER
+	} syscall_type = INT80;
+
+	if (argc > 1) {
+		if (!strcmp(argv[1], "syscall32")) {
+			syscall_type = SYSCALL32;
+			printf("[NOTE]\tUsing syscall32 after munmap\n");
+		} else if (!strcmp(argv[1], "sysenter")) {
+			syscall_type = SYSENTER;
+			printf("[NOTE]\tUsing sysenter after munmap\n");
+		}
+	}
+#endif
+
+	child = fork();
+	if (child == -1) {
+		printf("[WARN]\tfailed to fork (%d): %m\n", errno);
+		return 1;
+	}
+
+	if (child == 0) {
+		unsigned long vdso_size = PAGE_SIZE;
+		unsigned long auxval;
+		int ret = -1;
+
+		auxval = getauxval(AT_SYSINFO_EHDR);
+		printf("\tAT_SYSINFO_EHDR is %#lx\n", auxval);
+		if (!auxval || auxval == -ENOENT) {
+			printf("[WARN]\tgetauxval failed\n");
+			return 0;
+		}
+
+		/* Simpler than parsing ELF header */
+		while (ret < 0) {
+			ret = try_to_unmap((void *)auxval, vdso_size);
+			vdso_size += PAGE_SIZE;
+		}
+
+		/* Glibc is likely to explode now - exit with raw syscall */
+#ifdef __i386__
+		switch (syscall_type) {
+		case SYSCALL32:
+			asm volatile ("syscall" : : "a" (__NR_exit), "b" (!!ret));
+		case SYSENTER:
+			asm volatile ("sysenter" : : "a" (__NR_exit), "b" (!!ret));
+		default:
+		case INT80:
+			asm volatile ("int $0x80" : : "a" (__NR_exit), "b" (!!ret));
+		}
+#else /* __x86_64__ */
+		syscall(SYS_exit, ret);
+#endif
+	} else {
+		int status;
+
+		if (waitpid(child, &status, 0) != child) {
+			printf("[FAIL]\tUnexpected child, killing the expected one\n");
+			kill(child, SIGKILL);
+			return 1;
+		}
+
+
+#ifdef __i386__
+		switch (syscall_type) {
+		case SYSCALL32:
+		case SYSENTER:
+			if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
+				printf("[OK]\t32-bit process gets segfault on fast syscall with unmapped vDSO\n");
+				return 0;
+			}
+		default:
+		case INT80:
+			/* same as on x86_64 */
+		}
+#endif
+
+		if (!WIFEXITED(status)) {
+			printf("[FAIL]\tmunmap() of the vDSO does not work on this kernel!\n");
+			if (WIFSIGNALED(status))
+				printf("[FAIL]\tprocess crashed with %s\n",
+					strsignal(WTERMSIG(status)));
+			return 1;
+		} else if (WEXITSTATUS(status) != 0) {
+			printf("[FAIL]\tChild failed with %d\n",
+					WEXITSTATUS(status));
+			return 1;
+		}
+		printf("[OK]\n");
+	}
+
+	return 0;
+}
-- 
2.31.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ