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