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