xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/x86/test_mremap_vdso.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * 32-bit test to check vDSO mremap.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2016 Dmitry Safonov
6*4882a593Smuzhiyun  * Suggested-by: Andrew Lutomirski
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * Can be built statically:
10*4882a593Smuzhiyun  * gcc -Os -Wall -static -m32 test_mremap_vdso.c
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun #define _GNU_SOURCE
13*4882a593Smuzhiyun #include <stdio.h>
14*4882a593Smuzhiyun #include <errno.h>
15*4882a593Smuzhiyun #include <unistd.h>
16*4882a593Smuzhiyun #include <string.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <sys/mman.h>
19*4882a593Smuzhiyun #include <sys/auxv.h>
20*4882a593Smuzhiyun #include <sys/syscall.h>
21*4882a593Smuzhiyun #include <sys/wait.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define PAGE_SIZE	4096
24*4882a593Smuzhiyun 
try_to_remap(void * vdso_addr,unsigned long size)25*4882a593Smuzhiyun static int try_to_remap(void *vdso_addr, unsigned long size)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun 	void *dest_addr, *new_addr;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	/* Searching for memory location where to remap */
30*4882a593Smuzhiyun 	dest_addr = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
31*4882a593Smuzhiyun 	if (dest_addr == MAP_FAILED) {
32*4882a593Smuzhiyun 		printf("[WARN]\tmmap failed (%d): %m\n", errno);
33*4882a593Smuzhiyun 		return 0;
34*4882a593Smuzhiyun 	}
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	printf("[NOTE]\tMoving vDSO: [%p, %#lx] -> [%p, %#lx]\n",
37*4882a593Smuzhiyun 		vdso_addr, (unsigned long)vdso_addr + size,
38*4882a593Smuzhiyun 		dest_addr, (unsigned long)dest_addr + size);
39*4882a593Smuzhiyun 	fflush(stdout);
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	new_addr = mremap(vdso_addr, size, size,
42*4882a593Smuzhiyun 			MREMAP_FIXED|MREMAP_MAYMOVE, dest_addr);
43*4882a593Smuzhiyun 	if ((unsigned long)new_addr == (unsigned long)-1) {
44*4882a593Smuzhiyun 		munmap(dest_addr, size);
45*4882a593Smuzhiyun 		if (errno == EINVAL) {
46*4882a593Smuzhiyun 			printf("[NOTE]\tvDSO partial move failed, will try with bigger size\n");
47*4882a593Smuzhiyun 			return -1; /* Retry with larger */
48*4882a593Smuzhiyun 		}
49*4882a593Smuzhiyun 		printf("[FAIL]\tmremap failed (%d): %m\n", errno);
50*4882a593Smuzhiyun 		return 1;
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	return 0;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
main(int argc,char ** argv,char ** envp)57*4882a593Smuzhiyun int main(int argc, char **argv, char **envp)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	pid_t child;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	child = fork();
62*4882a593Smuzhiyun 	if (child == -1) {
63*4882a593Smuzhiyun 		printf("[WARN]\tfailed to fork (%d): %m\n", errno);
64*4882a593Smuzhiyun 		return 1;
65*4882a593Smuzhiyun 	}
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (child == 0) {
68*4882a593Smuzhiyun 		unsigned long vdso_size = PAGE_SIZE;
69*4882a593Smuzhiyun 		unsigned long auxval;
70*4882a593Smuzhiyun 		int ret = -1;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 		auxval = getauxval(AT_SYSINFO_EHDR);
73*4882a593Smuzhiyun 		printf("\tAT_SYSINFO_EHDR is %#lx\n", auxval);
74*4882a593Smuzhiyun 		if (!auxval || auxval == -ENOENT) {
75*4882a593Smuzhiyun 			printf("[WARN]\tgetauxval failed\n");
76*4882a593Smuzhiyun 			return 0;
77*4882a593Smuzhiyun 		}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 		/* Simpler than parsing ELF header */
80*4882a593Smuzhiyun 		while (ret < 0) {
81*4882a593Smuzhiyun 			ret = try_to_remap((void *)auxval, vdso_size);
82*4882a593Smuzhiyun 			vdso_size += PAGE_SIZE;
83*4882a593Smuzhiyun 		}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun #ifdef __i386__
86*4882a593Smuzhiyun 		/* Glibc is likely to explode now - exit with raw syscall */
87*4882a593Smuzhiyun 		asm volatile ("int $0x80" : : "a" (__NR_exit), "b" (!!ret));
88*4882a593Smuzhiyun #else /* __x86_64__ */
89*4882a593Smuzhiyun 		syscall(SYS_exit, ret);
90*4882a593Smuzhiyun #endif
91*4882a593Smuzhiyun 	} else {
92*4882a593Smuzhiyun 		int status;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 		if (waitpid(child, &status, 0) != child ||
95*4882a593Smuzhiyun 			!WIFEXITED(status)) {
96*4882a593Smuzhiyun 			printf("[FAIL]\tmremap() of the vDSO does not work on this kernel!\n");
97*4882a593Smuzhiyun 			return 1;
98*4882a593Smuzhiyun 		} else if (WEXITSTATUS(status) != 0) {
99*4882a593Smuzhiyun 			printf("[FAIL]\tChild failed with %d\n",
100*4882a593Smuzhiyun 					WEXITSTATUS(status));
101*4882a593Smuzhiyun 			return 1;
102*4882a593Smuzhiyun 		}
103*4882a593Smuzhiyun 		printf("[OK]\n");
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	return 0;
107*4882a593Smuzhiyun }
108