1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Dmitry Safonov, Arista Networks
4  *
5  * MAP_POPULATE | MAP_PRIVATE should COW VMA pages.
6  */
7 
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/mman.h>
12 #include <sys/socket.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #ifndef MMAP_SZ
21 #define MMAP_SZ		4096
22 #endif
23 
24 #define BUG_ON(condition, description)					\
25 	do {								\
26 		if (condition) {					\
27 			fprintf(stderr, "[FAIL]\t%s:%d\t%s:%s\n", __func__, \
28 				__LINE__, (description), strerror(errno)); \
29 			exit(1);					\
30 		}							\
31 	} while (0)
32 
33 static int parent_f(int sock, unsigned long *smap, int child)
34 {
35 	int status, ret;
36 
37 	ret = read(sock, &status, sizeof(int));
38 	BUG_ON(ret <= 0, "read(sock)");
39 
40 	*smap = 0x22222BAD;
41 	ret = msync(smap, MMAP_SZ, MS_SYNC);
42 	BUG_ON(ret, "msync()");
43 
44 	ret = write(sock, &status, sizeof(int));
45 	BUG_ON(ret <= 0, "write(sock)");
46 
47 	waitpid(child, &status, 0);
48 	BUG_ON(!WIFEXITED(status), "child in unexpected state");
49 
50 	return WEXITSTATUS(status);
51 }
52 
53 static int child_f(int sock, unsigned long *smap, int fd)
54 {
55 	int ret, buf = 0;
56 
57 	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
58 			MAP_PRIVATE | MAP_POPULATE, fd, 0);
59 	BUG_ON(smap == MAP_FAILED, "mmap()");
60 
61 	BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file");
62 
63 	ret = write(sock, &buf, sizeof(int));
64 	BUG_ON(ret <= 0, "write(sock)");
65 
66 	ret = read(sock, &buf, sizeof(int));
67 	BUG_ON(ret <= 0, "read(sock)");
68 
69 	BUG_ON(*smap == 0x22222BAD, "MAP_POPULATE didn't COW private page");
70 	BUG_ON(*smap != 0xdeadbabe, "mapping was corrupted");
71 
72 	return 0;
73 }
74 
75 int main(int argc, char **argv)
76 {
77 	int sock[2], child, ret;
78 	FILE *ftmp;
79 	unsigned long *smap;
80 
81 	ftmp = tmpfile();
82 	BUG_ON(ftmp == 0, "tmpfile()");
83 
84 	ret = ftruncate(fileno(ftmp), MMAP_SZ);
85 	BUG_ON(ret, "ftruncate()");
86 
87 	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
88 			MAP_SHARED, fileno(ftmp), 0);
89 	BUG_ON(smap == MAP_FAILED, "mmap()");
90 
91 	*smap = 0xdeadbabe;
92 	/* Probably unnecessary, but let it be. */
93 	ret = msync(smap, MMAP_SZ, MS_SYNC);
94 	BUG_ON(ret, "msync()");
95 
96 	ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock);
97 	BUG_ON(ret, "socketpair()");
98 
99 	child = fork();
100 	BUG_ON(child == -1, "fork()");
101 
102 	if (child) {
103 		ret = close(sock[0]);
104 		BUG_ON(ret, "close()");
105 
106 		return parent_f(sock[1], smap, child);
107 	}
108 
109 	ret = close(sock[1]);
110 	BUG_ON(ret, "close()");
111 
112 	return child_f(sock[0], smap, fileno(ftmp));
113 }
114