Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

proc: fix test for "vsyscall=xonly" boot option

Booting with vsyscall=xonly results in the following vsyscall VMA:

ffffffffff600000-ffffffffff601000 --xp ... [vsyscall]


Test does read from fixed vsyscall address to determine if kernel
supports vsyscall page but it doesn't work because, well, vsyscall
page is execute only.

Fix test by trying to execute from the first byte of the page which
contains gettimeofday() stub. This should work because vsyscall
entry points have stable addresses by design.

Alexey, avoiding parsing .config, /proc/config.gz and
/proc/cmdline at all costs.

Link: https://lkml.kernel.org/r/Ys2KgeiEMboU8Ytu@localhost.localdomain
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: <dylanbhatch@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Alexey Dobriyan and committed by
akpm
3adb2d87 d919a1e7

+68 -7
+68 -7
tools/testing/selftests/proc/proc-pid-vm.c
··· 211 211 } 212 212 #endif 213 213 214 - static bool g_vsyscall = false; 214 + /* 215 + * 0: vsyscall VMA doesn't exist vsyscall=none 216 + * 1: vsyscall VMA is r-xp vsyscall=emulate 217 + * 2: vsyscall VMA is --xp vsyscall=xonly 218 + */ 219 + static int g_vsyscall; 220 + static const char *str_vsyscall; 215 221 216 - static const char str_vsyscall[] = 222 + static const char str_vsyscall_0[] = ""; 223 + static const char str_vsyscall_1[] = 217 224 "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; 225 + static const char str_vsyscall_2[] = 226 + "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"; 218 227 219 228 #ifdef __x86_64__ 220 229 static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___) ··· 232 223 } 233 224 234 225 /* 235 - * vsyscall page can't be unmapped, probe it with memory load. 226 + * vsyscall page can't be unmapped, probe it directly. 236 227 */ 237 228 static void vsyscall(void) 238 229 { ··· 255 246 act.sa_sigaction = sigaction_SIGSEGV; 256 247 (void)sigaction(SIGSEGV, &act, NULL); 257 248 249 + /* gettimeofday(NULL, NULL); */ 250 + asm volatile ( 251 + "call %P0" 252 + : 253 + : "i" (0xffffffffff600000), "D" (NULL), "S" (NULL) 254 + : "rax", "rcx", "r11" 255 + ); 256 + exit(0); 257 + } 258 + waitpid(pid, &wstatus, 0); 259 + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { 260 + /* vsyscall page exists and is executable. */ 261 + } else { 262 + /* vsyscall page doesn't exist. */ 263 + g_vsyscall = 0; 264 + return; 265 + } 266 + 267 + pid = fork(); 268 + if (pid < 0) { 269 + fprintf(stderr, "fork, errno %d\n", errno); 270 + exit(1); 271 + } 272 + if (pid == 0) { 273 + struct rlimit rlim = {0, 0}; 274 + (void)setrlimit(RLIMIT_CORE, &rlim); 275 + 276 + /* Hide "segfault at ffffffffff600000" messages. */ 277 + struct sigaction act; 278 + memset(&act, 0, sizeof(struct sigaction)); 279 + act.sa_flags = SA_SIGINFO; 280 + act.sa_sigaction = sigaction_SIGSEGV; 281 + (void)sigaction(SIGSEGV, &act, NULL); 282 + 258 283 *(volatile int *)0xffffffffff600000UL; 259 284 exit(0); 260 285 } 261 286 waitpid(pid, &wstatus, 0); 262 287 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { 263 - g_vsyscall = true; 288 + /* vsyscall page is readable and executable. */ 289 + g_vsyscall = 1; 290 + return; 264 291 } 292 + 293 + /* vsyscall page is executable but unreadable. */ 294 + g_vsyscall = 2; 265 295 } 266 296 267 297 int main(void) ··· 309 261 int exec_fd; 310 262 311 263 vsyscall(); 264 + switch (g_vsyscall) { 265 + case 0: 266 + str_vsyscall = str_vsyscall_0; 267 + break; 268 + case 1: 269 + str_vsyscall = str_vsyscall_1; 270 + break; 271 + case 2: 272 + str_vsyscall = str_vsyscall_2; 273 + break; 274 + default: 275 + abort(); 276 + } 312 277 313 278 atexit(ate); 314 279 ··· 375 314 376 315 /* Test /proc/$PID/maps */ 377 316 { 378 - const size_t len = strlen(buf0) + (g_vsyscall ? strlen(str_vsyscall) : 0); 317 + const size_t len = strlen(buf0) + strlen(str_vsyscall); 379 318 char buf[256]; 380 319 ssize_t rv; 381 320 int fd; ··· 388 327 rv = read(fd, buf, sizeof(buf)); 389 328 assert(rv == len); 390 329 assert(memcmp(buf, buf0, strlen(buf0)) == 0); 391 - if (g_vsyscall) { 330 + if (g_vsyscall > 0) { 392 331 assert(memcmp(buf + strlen(buf0), str_vsyscall, strlen(str_vsyscall)) == 0); 393 332 } 394 333 } ··· 435 374 assert(memmem(buf, rv, S[i], strlen(S[i]))); 436 375 } 437 376 438 - if (g_vsyscall) { 377 + if (g_vsyscall > 0) { 439 378 assert(memmem(buf, rv, str_vsyscall, strlen(str_vsyscall))); 440 379 } 441 380 }