File Index
Files and directories
Sign in
FILE

test_mremap_vdso.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/test_mremap_vdso.c C
  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#include <stdbool.h>
 18
 19#include <sys/mman.h>
 20#include <sys/auxv.h>
 21#include <sys/syscall.h>
 22#include <sys/wait.h>
 23#include "kselftest.h"
 24
 25#define PAGE_SIZE	4096
 26
 27static int try_to_remap(void *vdso_addr, unsigned long size)
 28{
 29	void *dest_addr, *new_addr;
 30
 31	/* Searching for memory location where to remap */
 32	dest_addr = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
 33	if (dest_addr == MAP_FAILED) {
 34		ksft_print_msg("WARN: mmap failed (%d): %m\n", errno);
 35		return 0;
 36	}
 37
 38	ksft_print_msg("Moving vDSO: [%p, %#lx] -> [%p, %#lx]\n",
 39		       vdso_addr, (unsigned long)vdso_addr + size,
 40		       dest_addr, (unsigned long)dest_addr + size);
 41	fflush(stdout);
 42
 43	new_addr = mremap(vdso_addr, size, size,
 44			MREMAP_FIXED|MREMAP_MAYMOVE, dest_addr);
 45	if ((unsigned long)new_addr == (unsigned long)-1) {
 46		munmap(dest_addr, size);
 47		if (errno == EINVAL) {
 48			ksft_print_msg("vDSO partial move failed, will try with bigger size\n");
 49			return -1; /* Retry with larger */
 50		}
 51		ksft_print_msg("[FAIL]\tmremap failed (%d): %m\n", errno);
 52		return 1;
 53	}
 54
 55	return 0;
 56
 57}
 58
 59#define VDSO_NAME "[vdso]"
 60#define VMFLAGS "VmFlags:"
 61#define MSEAL_FLAGS "sl"
 62#define MAX_LINE_LEN 512
 63
 64bool vdso_sealed(FILE *maps)
 65{
 66	char line[MAX_LINE_LEN];
 67	bool has_vdso = false;
 68
 69	while (fgets(line, sizeof(line), maps)) {
 70		if (strstr(line, VDSO_NAME))
 71			has_vdso = true;
 72
 73		if (has_vdso && !strncmp(line, VMFLAGS, strlen(VMFLAGS))) {
 74			if (strstr(line, MSEAL_FLAGS))
 75				return true;
 76
 77			return false;
 78		}
 79	}
 80
 81	return false;
 82}
 83
 84int main(int argc, char **argv, char **envp)
 85{
 86	pid_t child;
 87	FILE *maps;
 88
 89	ksft_print_header();
 90	ksft_set_plan(1);
 91
 92	maps = fopen("/proc/self/smaps", "r");
 93	if (!maps) {
 94		ksft_test_result_skip(
 95			"Could not open /proc/self/smaps, errno=%d\n",
 96			 errno);
 97
 98		return 0;
 99	}
100
101	if (vdso_sealed(maps)) {
102		ksft_test_result_skip("vdso is sealed\n");
103		return 0;
104	}
105
106	fclose(maps);
107
108	child = fork();
109	if (child == -1)
110		ksft_exit_fail_msg("failed to fork (%d): %m\n", errno);
111
112	if (child == 0) {
113		unsigned long vdso_size = PAGE_SIZE;
114		unsigned long auxval;
115		int ret = -1;
116
117		auxval = getauxval(AT_SYSINFO_EHDR);
118		ksft_print_msg("AT_SYSINFO_EHDR is %#lx\n", auxval);
119		if (!auxval || auxval == -ENOENT) {
120			ksft_print_msg("WARN: getauxval failed\n");
121			return 0;
122		}
123
124		/* Simpler than parsing ELF header */
125		while (ret < 0) {
126			ret = try_to_remap((void *)auxval, vdso_size);
127			vdso_size += PAGE_SIZE;
128		}
129
130#ifdef __i386__
131		/* Glibc is likely to explode now - exit with raw syscall */
132		asm volatile ("int $0x80" : : "a" (__NR_exit), "b" (!!ret));
133#else /* __x86_64__ */
134		syscall(SYS_exit, ret);
135#endif
136	} else {
137		int status;
138
139		if (waitpid(child, &status, 0) != child ||
140			!WIFEXITED(status))
141			ksft_test_result_fail("mremap() of the vDSO does not work on this kernel!\n");
142		else if (WEXITSTATUS(status) != 0)
143			ksft_test_result_fail("Child failed with %d\n", WEXITSTATUS(status));
144		else
145			ksft_test_result_pass("%s\n", __func__);
146	}
147
148	ksft_finished();
149}
~/ Read-only file preview.