1*5bc73bb3SAlexey Dobriyan /*
2*5bc73bb3SAlexey Dobriyan  * Copyright (c) 2022 Alexey Dobriyan <[email protected]>
3*5bc73bb3SAlexey Dobriyan  *
4*5bc73bb3SAlexey Dobriyan  * Permission to use, copy, modify, and distribute this software for any
5*5bc73bb3SAlexey Dobriyan  * purpose with or without fee is hereby granted, provided that the above
6*5bc73bb3SAlexey Dobriyan  * copyright notice and this permission notice appear in all copies.
7*5bc73bb3SAlexey Dobriyan  *
8*5bc73bb3SAlexey Dobriyan  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*5bc73bb3SAlexey Dobriyan  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*5bc73bb3SAlexey Dobriyan  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*5bc73bb3SAlexey Dobriyan  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*5bc73bb3SAlexey Dobriyan  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*5bc73bb3SAlexey Dobriyan  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*5bc73bb3SAlexey Dobriyan  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*5bc73bb3SAlexey Dobriyan  */
16*5bc73bb3SAlexey Dobriyan /*
17*5bc73bb3SAlexey Dobriyan  * Create a process without mappings by unmapping everything at once and
18*5bc73bb3SAlexey Dobriyan  * holding it with ptrace(2). See what happens to
19*5bc73bb3SAlexey Dobriyan  *
20*5bc73bb3SAlexey Dobriyan  *	/proc/${pid}/maps
21*5bc73bb3SAlexey Dobriyan  *	/proc/${pid}/numa_maps
22*5bc73bb3SAlexey Dobriyan  *	/proc/${pid}/smaps
23*5bc73bb3SAlexey Dobriyan  *	/proc/${pid}/smaps_rollup
24*5bc73bb3SAlexey Dobriyan  */
25*5bc73bb3SAlexey Dobriyan #undef NDEBUG
26*5bc73bb3SAlexey Dobriyan #include <assert.h>
27*5bc73bb3SAlexey Dobriyan #include <errno.h>
28*5bc73bb3SAlexey Dobriyan #include <stdio.h>
29*5bc73bb3SAlexey Dobriyan #include <stdlib.h>
30*5bc73bb3SAlexey Dobriyan #include <string.h>
31*5bc73bb3SAlexey Dobriyan #include <fcntl.h>
32*5bc73bb3SAlexey Dobriyan #include <sys/mman.h>
33*5bc73bb3SAlexey Dobriyan #include <sys/ptrace.h>
34*5bc73bb3SAlexey Dobriyan #include <sys/resource.h>
35*5bc73bb3SAlexey Dobriyan #include <sys/types.h>
36*5bc73bb3SAlexey Dobriyan #include <sys/wait.h>
37*5bc73bb3SAlexey Dobriyan #include <unistd.h>
38*5bc73bb3SAlexey Dobriyan 
39*5bc73bb3SAlexey Dobriyan /*
40*5bc73bb3SAlexey Dobriyan  * 0: vsyscall VMA doesn't exist	vsyscall=none
41*5bc73bb3SAlexey Dobriyan  * 1: vsyscall VMA is --xp		vsyscall=xonly
42*5bc73bb3SAlexey Dobriyan  * 2: vsyscall VMA is r-xp		vsyscall=emulate
43*5bc73bb3SAlexey Dobriyan  */
44*5bc73bb3SAlexey Dobriyan static int g_vsyscall;
45*5bc73bb3SAlexey Dobriyan static const char *g_proc_pid_maps_vsyscall;
46*5bc73bb3SAlexey Dobriyan static const char *g_proc_pid_smaps_vsyscall;
47*5bc73bb3SAlexey Dobriyan 
48*5bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_0[] = "";
49*5bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_1[] =
50*5bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]\n";
51*5bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_2[] =
52*5bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]\n";
53*5bc73bb3SAlexey Dobriyan 
54*5bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_0[] = "";
55*5bc73bb3SAlexey Dobriyan 
56*5bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_1[] =
57*5bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]\n"
58*5bc73bb3SAlexey Dobriyan "Size:                  4 kB\n"
59*5bc73bb3SAlexey Dobriyan "KernelPageSize:        4 kB\n"
60*5bc73bb3SAlexey Dobriyan "MMUPageSize:           4 kB\n"
61*5bc73bb3SAlexey Dobriyan "Rss:                   0 kB\n"
62*5bc73bb3SAlexey Dobriyan "Pss:                   0 kB\n"
63*5bc73bb3SAlexey Dobriyan "Pss_Dirty:             0 kB\n"
64*5bc73bb3SAlexey Dobriyan "Shared_Clean:          0 kB\n"
65*5bc73bb3SAlexey Dobriyan "Shared_Dirty:          0 kB\n"
66*5bc73bb3SAlexey Dobriyan "Private_Clean:         0 kB\n"
67*5bc73bb3SAlexey Dobriyan "Private_Dirty:         0 kB\n"
68*5bc73bb3SAlexey Dobriyan "Referenced:            0 kB\n"
69*5bc73bb3SAlexey Dobriyan "Anonymous:             0 kB\n"
70*5bc73bb3SAlexey Dobriyan "LazyFree:              0 kB\n"
71*5bc73bb3SAlexey Dobriyan "AnonHugePages:         0 kB\n"
72*5bc73bb3SAlexey Dobriyan "ShmemPmdMapped:        0 kB\n"
73*5bc73bb3SAlexey Dobriyan "FilePmdMapped:         0 kB\n"
74*5bc73bb3SAlexey Dobriyan "Shared_Hugetlb:        0 kB\n"
75*5bc73bb3SAlexey Dobriyan "Private_Hugetlb:       0 kB\n"
76*5bc73bb3SAlexey Dobriyan "Swap:                  0 kB\n"
77*5bc73bb3SAlexey Dobriyan "SwapPss:               0 kB\n"
78*5bc73bb3SAlexey Dobriyan "Locked:                0 kB\n"
79*5bc73bb3SAlexey Dobriyan "THPeligible:    0\n"
80*5bc73bb3SAlexey Dobriyan /*
81*5bc73bb3SAlexey Dobriyan  * "ProtectionKey:" field is conditional. It is possible to check it as well,
82*5bc73bb3SAlexey Dobriyan  * but I don't have such machine.
83*5bc73bb3SAlexey Dobriyan  */
84*5bc73bb3SAlexey Dobriyan ;
85*5bc73bb3SAlexey Dobriyan 
86*5bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_2[] =
87*5bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]\n"
88*5bc73bb3SAlexey Dobriyan "Size:                  4 kB\n"
89*5bc73bb3SAlexey Dobriyan "KernelPageSize:        4 kB\n"
90*5bc73bb3SAlexey Dobriyan "MMUPageSize:           4 kB\n"
91*5bc73bb3SAlexey Dobriyan "Rss:                   0 kB\n"
92*5bc73bb3SAlexey Dobriyan "Pss:                   0 kB\n"
93*5bc73bb3SAlexey Dobriyan "Pss_Dirty:             0 kB\n"
94*5bc73bb3SAlexey Dobriyan "Shared_Clean:          0 kB\n"
95*5bc73bb3SAlexey Dobriyan "Shared_Dirty:          0 kB\n"
96*5bc73bb3SAlexey Dobriyan "Private_Clean:         0 kB\n"
97*5bc73bb3SAlexey Dobriyan "Private_Dirty:         0 kB\n"
98*5bc73bb3SAlexey Dobriyan "Referenced:            0 kB\n"
99*5bc73bb3SAlexey Dobriyan "Anonymous:             0 kB\n"
100*5bc73bb3SAlexey Dobriyan "LazyFree:              0 kB\n"
101*5bc73bb3SAlexey Dobriyan "AnonHugePages:         0 kB\n"
102*5bc73bb3SAlexey Dobriyan "ShmemPmdMapped:        0 kB\n"
103*5bc73bb3SAlexey Dobriyan "FilePmdMapped:         0 kB\n"
104*5bc73bb3SAlexey Dobriyan "Shared_Hugetlb:        0 kB\n"
105*5bc73bb3SAlexey Dobriyan "Private_Hugetlb:       0 kB\n"
106*5bc73bb3SAlexey Dobriyan "Swap:                  0 kB\n"
107*5bc73bb3SAlexey Dobriyan "SwapPss:               0 kB\n"
108*5bc73bb3SAlexey Dobriyan "Locked:                0 kB\n"
109*5bc73bb3SAlexey Dobriyan "THPeligible:    0\n"
110*5bc73bb3SAlexey Dobriyan /*
111*5bc73bb3SAlexey Dobriyan  * "ProtectionKey:" field is conditional. It is possible to check it as well,
112*5bc73bb3SAlexey Dobriyan  * but I'm too tired.
113*5bc73bb3SAlexey Dobriyan  */
114*5bc73bb3SAlexey Dobriyan ;
115*5bc73bb3SAlexey Dobriyan 
116*5bc73bb3SAlexey Dobriyan static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
117*5bc73bb3SAlexey Dobriyan {
118*5bc73bb3SAlexey Dobriyan 	_exit(EXIT_FAILURE);
119*5bc73bb3SAlexey Dobriyan }
120*5bc73bb3SAlexey Dobriyan 
121*5bc73bb3SAlexey Dobriyan static void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___)
122*5bc73bb3SAlexey Dobriyan {
123*5bc73bb3SAlexey Dobriyan 	_exit(g_vsyscall);
124*5bc73bb3SAlexey Dobriyan }
125*5bc73bb3SAlexey Dobriyan 
126*5bc73bb3SAlexey Dobriyan /*
127*5bc73bb3SAlexey Dobriyan  * vsyscall page can't be unmapped, probe it directly.
128*5bc73bb3SAlexey Dobriyan  */
129*5bc73bb3SAlexey Dobriyan static void vsyscall(void)
130*5bc73bb3SAlexey Dobriyan {
131*5bc73bb3SAlexey Dobriyan 	pid_t pid;
132*5bc73bb3SAlexey Dobriyan 	int wstatus;
133*5bc73bb3SAlexey Dobriyan 
134*5bc73bb3SAlexey Dobriyan 	pid = fork();
135*5bc73bb3SAlexey Dobriyan 	if (pid < 0) {
136*5bc73bb3SAlexey Dobriyan 		fprintf(stderr, "fork, errno %d\n", errno);
137*5bc73bb3SAlexey Dobriyan 		exit(1);
138*5bc73bb3SAlexey Dobriyan 	}
139*5bc73bb3SAlexey Dobriyan 	if (pid == 0) {
140*5bc73bb3SAlexey Dobriyan 		setrlimit(RLIMIT_CORE, &(struct rlimit){});
141*5bc73bb3SAlexey Dobriyan 
142*5bc73bb3SAlexey Dobriyan 		/* Hide "segfault at ffffffffff600000" messages. */
143*5bc73bb3SAlexey Dobriyan 		struct sigaction act = {};
144*5bc73bb3SAlexey Dobriyan 		act.sa_flags = SA_SIGINFO;
145*5bc73bb3SAlexey Dobriyan 		act.sa_sigaction = sigaction_SIGSEGV_vsyscall;
146*5bc73bb3SAlexey Dobriyan 		sigaction(SIGSEGV, &act, NULL);
147*5bc73bb3SAlexey Dobriyan 
148*5bc73bb3SAlexey Dobriyan 		g_vsyscall = 0;
149*5bc73bb3SAlexey Dobriyan 		/* gettimeofday(NULL, NULL); */
150*5bc73bb3SAlexey Dobriyan 		asm volatile (
151*5bc73bb3SAlexey Dobriyan 			"call %P0"
152*5bc73bb3SAlexey Dobriyan 			:
153*5bc73bb3SAlexey Dobriyan 			: "i" (0xffffffffff600000), "D" (NULL), "S" (NULL)
154*5bc73bb3SAlexey Dobriyan 			: "rax", "rcx", "r11"
155*5bc73bb3SAlexey Dobriyan 		);
156*5bc73bb3SAlexey Dobriyan 
157*5bc73bb3SAlexey Dobriyan 		g_vsyscall = 1;
158*5bc73bb3SAlexey Dobriyan 		*(volatile int *)0xffffffffff600000UL;
159*5bc73bb3SAlexey Dobriyan 
160*5bc73bb3SAlexey Dobriyan 		g_vsyscall = 2;
161*5bc73bb3SAlexey Dobriyan 		exit(g_vsyscall);
162*5bc73bb3SAlexey Dobriyan 	}
163*5bc73bb3SAlexey Dobriyan 	waitpid(pid, &wstatus, 0);
164*5bc73bb3SAlexey Dobriyan 	if (WIFEXITED(wstatus)) {
165*5bc73bb3SAlexey Dobriyan 		g_vsyscall = WEXITSTATUS(wstatus);
166*5bc73bb3SAlexey Dobriyan 	} else {
167*5bc73bb3SAlexey Dobriyan 		fprintf(stderr, "error: vsyscall wstatus %08x\n", wstatus);
168*5bc73bb3SAlexey Dobriyan 		exit(1);
169*5bc73bb3SAlexey Dobriyan 	}
170*5bc73bb3SAlexey Dobriyan }
171*5bc73bb3SAlexey Dobriyan 
172*5bc73bb3SAlexey Dobriyan static int test_proc_pid_maps(pid_t pid)
173*5bc73bb3SAlexey Dobriyan {
174*5bc73bb3SAlexey Dobriyan 	char buf[4096];
175*5bc73bb3SAlexey Dobriyan 	snprintf(buf, sizeof(buf), "/proc/%u/maps", pid);
176*5bc73bb3SAlexey Dobriyan 	int fd = open(buf, O_RDONLY);
177*5bc73bb3SAlexey Dobriyan 	if (fd == -1) {
178*5bc73bb3SAlexey Dobriyan 		perror("open /proc/${pid}/maps");
179*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
180*5bc73bb3SAlexey Dobriyan 	} else {
181*5bc73bb3SAlexey Dobriyan 		ssize_t rv = read(fd, buf, sizeof(buf));
182*5bc73bb3SAlexey Dobriyan 		close(fd);
183*5bc73bb3SAlexey Dobriyan 		if (g_vsyscall == 0) {
184*5bc73bb3SAlexey Dobriyan 			assert(rv == 0);
185*5bc73bb3SAlexey Dobriyan 		} else {
186*5bc73bb3SAlexey Dobriyan 			size_t len = strlen(g_proc_pid_maps_vsyscall);
187*5bc73bb3SAlexey Dobriyan 			assert(rv == len);
188*5bc73bb3SAlexey Dobriyan 			assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0);
189*5bc73bb3SAlexey Dobriyan 		}
190*5bc73bb3SAlexey Dobriyan 		return EXIT_SUCCESS;
191*5bc73bb3SAlexey Dobriyan 	}
192*5bc73bb3SAlexey Dobriyan }
193*5bc73bb3SAlexey Dobriyan 
194*5bc73bb3SAlexey Dobriyan static int test_proc_pid_numa_maps(pid_t pid)
195*5bc73bb3SAlexey Dobriyan {
196*5bc73bb3SAlexey Dobriyan 	char buf[4096];
197*5bc73bb3SAlexey Dobriyan 	snprintf(buf, sizeof(buf), "/proc/%u/numa_maps", pid);
198*5bc73bb3SAlexey Dobriyan 	int fd = open(buf, O_RDONLY);
199*5bc73bb3SAlexey Dobriyan 	if (fd == -1) {
200*5bc73bb3SAlexey Dobriyan 		if (errno == ENOENT) {
201*5bc73bb3SAlexey Dobriyan 			/*
202*5bc73bb3SAlexey Dobriyan 			 * /proc/${pid}/numa_maps is under CONFIG_NUMA,
203*5bc73bb3SAlexey Dobriyan 			 * it doesn't necessarily exist.
204*5bc73bb3SAlexey Dobriyan 			 */
205*5bc73bb3SAlexey Dobriyan 			return EXIT_SUCCESS;
206*5bc73bb3SAlexey Dobriyan 		}
207*5bc73bb3SAlexey Dobriyan 		perror("open /proc/${pid}/numa_maps");
208*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
209*5bc73bb3SAlexey Dobriyan 	} else {
210*5bc73bb3SAlexey Dobriyan 		ssize_t rv = read(fd, buf, sizeof(buf));
211*5bc73bb3SAlexey Dobriyan 		close(fd);
212*5bc73bb3SAlexey Dobriyan 		assert(rv == 0);
213*5bc73bb3SAlexey Dobriyan 		return EXIT_SUCCESS;
214*5bc73bb3SAlexey Dobriyan 	}
215*5bc73bb3SAlexey Dobriyan }
216*5bc73bb3SAlexey Dobriyan 
217*5bc73bb3SAlexey Dobriyan static int test_proc_pid_smaps(pid_t pid)
218*5bc73bb3SAlexey Dobriyan {
219*5bc73bb3SAlexey Dobriyan 	char buf[4096];
220*5bc73bb3SAlexey Dobriyan 	snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid);
221*5bc73bb3SAlexey Dobriyan 	int fd = open(buf, O_RDONLY);
222*5bc73bb3SAlexey Dobriyan 	if (fd == -1) {
223*5bc73bb3SAlexey Dobriyan 		if (errno == ENOENT) {
224*5bc73bb3SAlexey Dobriyan 			/*
225*5bc73bb3SAlexey Dobriyan 			 * /proc/${pid}/smaps is under CONFIG_PROC_PAGE_MONITOR,
226*5bc73bb3SAlexey Dobriyan 			 * it doesn't necessarily exist.
227*5bc73bb3SAlexey Dobriyan 			 */
228*5bc73bb3SAlexey Dobriyan 			return EXIT_SUCCESS;
229*5bc73bb3SAlexey Dobriyan 		}
230*5bc73bb3SAlexey Dobriyan 		perror("open /proc/${pid}/smaps");
231*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
232*5bc73bb3SAlexey Dobriyan 	} else {
233*5bc73bb3SAlexey Dobriyan 		ssize_t rv = read(fd, buf, sizeof(buf));
234*5bc73bb3SAlexey Dobriyan 		close(fd);
235*5bc73bb3SAlexey Dobriyan 		if (g_vsyscall == 0) {
236*5bc73bb3SAlexey Dobriyan 			assert(rv == 0);
237*5bc73bb3SAlexey Dobriyan 		} else {
238*5bc73bb3SAlexey Dobriyan 			size_t len = strlen(g_proc_pid_maps_vsyscall);
239*5bc73bb3SAlexey Dobriyan 			/* TODO "ProtectionKey:" */
240*5bc73bb3SAlexey Dobriyan 			assert(rv > len);
241*5bc73bb3SAlexey Dobriyan 			assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0);
242*5bc73bb3SAlexey Dobriyan 		}
243*5bc73bb3SAlexey Dobriyan 		return EXIT_SUCCESS;
244*5bc73bb3SAlexey Dobriyan 	}
245*5bc73bb3SAlexey Dobriyan }
246*5bc73bb3SAlexey Dobriyan 
247*5bc73bb3SAlexey Dobriyan static const char g_smaps_rollup[] =
248*5bc73bb3SAlexey Dobriyan "00000000-00000000 ---p 00000000 00:00 0                                  [rollup]\n"
249*5bc73bb3SAlexey Dobriyan "Rss:                   0 kB\n"
250*5bc73bb3SAlexey Dobriyan "Pss:                   0 kB\n"
251*5bc73bb3SAlexey Dobriyan "Pss_Dirty:             0 kB\n"
252*5bc73bb3SAlexey Dobriyan "Pss_Anon:              0 kB\n"
253*5bc73bb3SAlexey Dobriyan "Pss_File:              0 kB\n"
254*5bc73bb3SAlexey Dobriyan "Pss_Shmem:             0 kB\n"
255*5bc73bb3SAlexey Dobriyan "Shared_Clean:          0 kB\n"
256*5bc73bb3SAlexey Dobriyan "Shared_Dirty:          0 kB\n"
257*5bc73bb3SAlexey Dobriyan "Private_Clean:         0 kB\n"
258*5bc73bb3SAlexey Dobriyan "Private_Dirty:         0 kB\n"
259*5bc73bb3SAlexey Dobriyan "Referenced:            0 kB\n"
260*5bc73bb3SAlexey Dobriyan "Anonymous:             0 kB\n"
261*5bc73bb3SAlexey Dobriyan "LazyFree:              0 kB\n"
262*5bc73bb3SAlexey Dobriyan "AnonHugePages:         0 kB\n"
263*5bc73bb3SAlexey Dobriyan "ShmemPmdMapped:        0 kB\n"
264*5bc73bb3SAlexey Dobriyan "FilePmdMapped:         0 kB\n"
265*5bc73bb3SAlexey Dobriyan "Shared_Hugetlb:        0 kB\n"
266*5bc73bb3SAlexey Dobriyan "Private_Hugetlb:       0 kB\n"
267*5bc73bb3SAlexey Dobriyan "Swap:                  0 kB\n"
268*5bc73bb3SAlexey Dobriyan "SwapPss:               0 kB\n"
269*5bc73bb3SAlexey Dobriyan "Locked:                0 kB\n"
270*5bc73bb3SAlexey Dobriyan ;
271*5bc73bb3SAlexey Dobriyan 
272*5bc73bb3SAlexey Dobriyan static int test_proc_pid_smaps_rollup(pid_t pid)
273*5bc73bb3SAlexey Dobriyan {
274*5bc73bb3SAlexey Dobriyan 	char buf[4096];
275*5bc73bb3SAlexey Dobriyan 	snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid);
276*5bc73bb3SAlexey Dobriyan 	int fd = open(buf, O_RDONLY);
277*5bc73bb3SAlexey Dobriyan 	if (fd == -1) {
278*5bc73bb3SAlexey Dobriyan 		if (errno == ENOENT) {
279*5bc73bb3SAlexey Dobriyan 			/*
280*5bc73bb3SAlexey Dobriyan 			 * /proc/${pid}/smaps_rollup is under CONFIG_PROC_PAGE_MONITOR,
281*5bc73bb3SAlexey Dobriyan 			 * it doesn't necessarily exist.
282*5bc73bb3SAlexey Dobriyan 			 */
283*5bc73bb3SAlexey Dobriyan 			return EXIT_SUCCESS;
284*5bc73bb3SAlexey Dobriyan 		}
285*5bc73bb3SAlexey Dobriyan 		perror("open /proc/${pid}/smaps_rollup");
286*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
287*5bc73bb3SAlexey Dobriyan 	} else {
288*5bc73bb3SAlexey Dobriyan 		ssize_t rv = read(fd, buf, sizeof(buf));
289*5bc73bb3SAlexey Dobriyan 		close(fd);
290*5bc73bb3SAlexey Dobriyan 		assert(rv == sizeof(g_smaps_rollup) - 1);
291*5bc73bb3SAlexey Dobriyan 		assert(memcmp(buf, g_smaps_rollup, sizeof(g_smaps_rollup) - 1) == 0);
292*5bc73bb3SAlexey Dobriyan 		return EXIT_SUCCESS;
293*5bc73bb3SAlexey Dobriyan 	}
294*5bc73bb3SAlexey Dobriyan }
295*5bc73bb3SAlexey Dobriyan 
296*5bc73bb3SAlexey Dobriyan int main(void)
297*5bc73bb3SAlexey Dobriyan {
298*5bc73bb3SAlexey Dobriyan 	int rv = EXIT_SUCCESS;
299*5bc73bb3SAlexey Dobriyan 
300*5bc73bb3SAlexey Dobriyan 	vsyscall();
301*5bc73bb3SAlexey Dobriyan 
302*5bc73bb3SAlexey Dobriyan 	switch (g_vsyscall) {
303*5bc73bb3SAlexey Dobriyan 	case 0:
304*5bc73bb3SAlexey Dobriyan 		g_proc_pid_maps_vsyscall  = proc_pid_maps_vsyscall_0;
305*5bc73bb3SAlexey Dobriyan 		g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_0;
306*5bc73bb3SAlexey Dobriyan 		break;
307*5bc73bb3SAlexey Dobriyan 	case 1:
308*5bc73bb3SAlexey Dobriyan 		g_proc_pid_maps_vsyscall  = proc_pid_maps_vsyscall_1;
309*5bc73bb3SAlexey Dobriyan 		g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_1;
310*5bc73bb3SAlexey Dobriyan 		break;
311*5bc73bb3SAlexey Dobriyan 	case 2:
312*5bc73bb3SAlexey Dobriyan 		g_proc_pid_maps_vsyscall  = proc_pid_maps_vsyscall_2;
313*5bc73bb3SAlexey Dobriyan 		g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_2;
314*5bc73bb3SAlexey Dobriyan 		break;
315*5bc73bb3SAlexey Dobriyan 	default:
316*5bc73bb3SAlexey Dobriyan 		abort();
317*5bc73bb3SAlexey Dobriyan 	}
318*5bc73bb3SAlexey Dobriyan 
319*5bc73bb3SAlexey Dobriyan 	pid_t pid = fork();
320*5bc73bb3SAlexey Dobriyan 	if (pid == -1) {
321*5bc73bb3SAlexey Dobriyan 		perror("fork");
322*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
323*5bc73bb3SAlexey Dobriyan 	} else if (pid == 0) {
324*5bc73bb3SAlexey Dobriyan 		rv = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
325*5bc73bb3SAlexey Dobriyan 		if (rv != 0) {
326*5bc73bb3SAlexey Dobriyan 			if (errno == EPERM) {
327*5bc73bb3SAlexey Dobriyan 				fprintf(stderr,
328*5bc73bb3SAlexey Dobriyan "Did you know? ptrace(PTRACE_TRACEME) doesn't work under strace.\n"
329*5bc73bb3SAlexey Dobriyan 				);
330*5bc73bb3SAlexey Dobriyan 				kill(getppid(), SIGTERM);
331*5bc73bb3SAlexey Dobriyan 				return EXIT_FAILURE;
332*5bc73bb3SAlexey Dobriyan 			}
333*5bc73bb3SAlexey Dobriyan 			perror("ptrace PTRACE_TRACEME");
334*5bc73bb3SAlexey Dobriyan 			return EXIT_FAILURE;
335*5bc73bb3SAlexey Dobriyan 		}
336*5bc73bb3SAlexey Dobriyan 
337*5bc73bb3SAlexey Dobriyan 		/*
338*5bc73bb3SAlexey Dobriyan 		 * Hide "segfault at ..." messages. Signal handler won't run.
339*5bc73bb3SAlexey Dobriyan 		 */
340*5bc73bb3SAlexey Dobriyan 		struct sigaction act = {};
341*5bc73bb3SAlexey Dobriyan 		act.sa_flags = SA_SIGINFO;
342*5bc73bb3SAlexey Dobriyan 		act.sa_sigaction = sigaction_SIGSEGV;
343*5bc73bb3SAlexey Dobriyan 		sigaction(SIGSEGV, &act, NULL);
344*5bc73bb3SAlexey Dobriyan 
345*5bc73bb3SAlexey Dobriyan #ifdef __amd64__
346*5bc73bb3SAlexey Dobriyan 		munmap(NULL, ((size_t)1 << 47) - 4096);
347*5bc73bb3SAlexey Dobriyan #else
348*5bc73bb3SAlexey Dobriyan #error "implement 'unmap everything'"
349*5bc73bb3SAlexey Dobriyan #endif
350*5bc73bb3SAlexey Dobriyan 		return EXIT_FAILURE;
351*5bc73bb3SAlexey Dobriyan 	} else {
352*5bc73bb3SAlexey Dobriyan 		/*
353*5bc73bb3SAlexey Dobriyan 		 * TODO find reliable way to signal parent that munmap(2) completed.
354*5bc73bb3SAlexey Dobriyan 		 * Child can't do it directly because it effectively doesn't exist
355*5bc73bb3SAlexey Dobriyan 		 * anymore. Looking at child's VM files isn't 100% reliable either:
356*5bc73bb3SAlexey Dobriyan 		 * due to a bug they may not become empty or empty-like.
357*5bc73bb3SAlexey Dobriyan 		 */
358*5bc73bb3SAlexey Dobriyan 		sleep(1);
359*5bc73bb3SAlexey Dobriyan 
360*5bc73bb3SAlexey Dobriyan 		if (rv == EXIT_SUCCESS) {
361*5bc73bb3SAlexey Dobriyan 			rv = test_proc_pid_maps(pid);
362*5bc73bb3SAlexey Dobriyan 		}
363*5bc73bb3SAlexey Dobriyan 		if (rv == EXIT_SUCCESS) {
364*5bc73bb3SAlexey Dobriyan 			rv = test_proc_pid_numa_maps(pid);
365*5bc73bb3SAlexey Dobriyan 		}
366*5bc73bb3SAlexey Dobriyan 		if (rv == EXIT_SUCCESS) {
367*5bc73bb3SAlexey Dobriyan 			rv = test_proc_pid_smaps(pid);
368*5bc73bb3SAlexey Dobriyan 		}
369*5bc73bb3SAlexey Dobriyan 		if (rv == EXIT_SUCCESS) {
370*5bc73bb3SAlexey Dobriyan 			rv = test_proc_pid_smaps_rollup(pid);
371*5bc73bb3SAlexey Dobriyan 		}
372*5bc73bb3SAlexey Dobriyan 		/*
373*5bc73bb3SAlexey Dobriyan 		 * TODO test /proc/${pid}/statm, task_statm()
374*5bc73bb3SAlexey Dobriyan 		 * ->start_code, ->end_code aren't updated by munmap().
375*5bc73bb3SAlexey Dobriyan 		 * Output can be "0 0 0 2 0 0 0\n" where "2" can be anything.
376*5bc73bb3SAlexey Dobriyan 		 */
377*5bc73bb3SAlexey Dobriyan 
378*5bc73bb3SAlexey Dobriyan 		/* Cut the rope. */
379*5bc73bb3SAlexey Dobriyan 		int wstatus;
380*5bc73bb3SAlexey Dobriyan 		waitpid(pid, &wstatus, 0);
381*5bc73bb3SAlexey Dobriyan 		assert(WIFSTOPPED(wstatus));
382*5bc73bb3SAlexey Dobriyan 		assert(WSTOPSIG(wstatus) == SIGSEGV);
383*5bc73bb3SAlexey Dobriyan 	}
384*5bc73bb3SAlexey Dobriyan 
385*5bc73bb3SAlexey Dobriyan 	return rv;
386*5bc73bb3SAlexey Dobriyan }
387