1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Test that we can take signals with and without the VDSO mapped, which trigger |
4 | * different paths in the signal handling code. |
5 | * |
6 | * See handle_rt_signal64() and setup_trampoline() in signal_64.c |
7 | */ |
8 | |
9 | #define _GNU_SOURCE |
10 | |
11 | #include <errno.h> |
12 | #include <stdio.h> |
13 | #include <signal.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | #include <sys/mman.h> |
17 | #include <sys/types.h> |
18 | #include <unistd.h> |
19 | |
20 | // Ensure assert() is not compiled out |
21 | #undef NDEBUG |
22 | #include <assert.h> |
23 | |
24 | #include "utils.h" |
25 | |
26 | static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high) |
27 | { |
28 | unsigned long start, end; |
29 | static char buf[4096]; |
30 | char name[128]; |
31 | FILE *f; |
32 | int rc = -1; |
33 | |
34 | f = fopen("/proc/self/maps" , "r" ); |
35 | if (!f) { |
36 | perror("fopen" ); |
37 | return -1; |
38 | } |
39 | |
40 | while (fgets(buf, sizeof(buf), f)) { |
41 | rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n" , |
42 | &start, &end, name); |
43 | if (rc == 2) |
44 | continue; |
45 | |
46 | if (rc != 3) { |
47 | printf("sscanf errored\n" ); |
48 | rc = -1; |
49 | break; |
50 | } |
51 | |
52 | if (strstr(name, needle)) { |
53 | *low = start; |
54 | *high = end - 1; |
55 | rc = 0; |
56 | break; |
57 | } |
58 | } |
59 | |
60 | fclose(f); |
61 | |
62 | return rc; |
63 | } |
64 | |
65 | static volatile sig_atomic_t took_signal = 0; |
66 | |
67 | static void sigusr1_handler(int sig) |
68 | { |
69 | took_signal++; |
70 | } |
71 | |
72 | int test_sigreturn_vdso(void) |
73 | { |
74 | unsigned long low, high, size; |
75 | struct sigaction act; |
76 | char *p; |
77 | |
78 | act.sa_handler = sigusr1_handler; |
79 | act.sa_flags = 0; |
80 | sigemptyset(&act.sa_mask); |
81 | |
82 | assert(sigaction(SIGUSR1, &act, NULL) == 0); |
83 | |
84 | // Confirm the VDSO is mapped, and work out where it is |
85 | assert(search_proc_maps(needle: "[vdso]" , low: &low, high: &high) == 0); |
86 | size = high - low + 1; |
87 | printf("VDSO is at 0x%lx-0x%lx (%lu bytes)\n" , low, high, size); |
88 | |
89 | kill(getpid(), SIGUSR1); |
90 | assert(took_signal == 1); |
91 | printf("Signal delivered OK with VDSO mapped\n" ); |
92 | |
93 | // Remap the VDSO somewhere else |
94 | p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); |
95 | assert(p != MAP_FAILED); |
96 | assert(mremap((void *)low, size, size, MREMAP_MAYMOVE|MREMAP_FIXED, p) != MAP_FAILED); |
97 | assert(search_proc_maps(needle: "[vdso]" , low: &low, high: &high) == 0); |
98 | size = high - low + 1; |
99 | printf("VDSO moved to 0x%lx-0x%lx (%lu bytes)\n" , low, high, size); |
100 | |
101 | kill(getpid(), SIGUSR1); |
102 | assert(took_signal == 2); |
103 | printf("Signal delivered OK with VDSO moved\n" ); |
104 | |
105 | assert(munmap((void *)low, size) == 0); |
106 | printf("Unmapped VDSO\n" ); |
107 | |
108 | // Confirm the VDSO is not mapped anymore |
109 | assert(search_proc_maps(needle: "[vdso]" , low: &low, high: &high) != 0); |
110 | |
111 | // Make the stack executable |
112 | assert(search_proc_maps(needle: "[stack]" , low: &low, high: &high) == 0); |
113 | size = high - low + 1; |
114 | mprotect((void *)low, size, PROT_READ|PROT_WRITE|PROT_EXEC); |
115 | printf("Remapped the stack executable\n" ); |
116 | |
117 | kill(getpid(), SIGUSR1); |
118 | assert(took_signal == 3); |
119 | printf("Signal delivered OK with VDSO unmapped\n" ); |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | int main(void) |
125 | { |
126 | return test_harness(test_sigreturn_vdso, "sigreturn_vdso" ); |
127 | } |
128 | |