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