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 #define MMAP_SZ		4096
21 
22 #define BUG_ON(condition, description)					\
23 	do {								\
24 		if (condition) {					\
25 			fprintf(stderr, "[FAIL]\t%s:%d\t%s:%s\n", __func__, \
26 				__LINE__, (description), strerror(errno)); \
27 			exit(1);					\
28 		}							\
29 	} while (0)
30 
31 static int parent_f(int sock, unsigned long *smap, int child)
32 {
33 	int status, ret;
34 
35 	ret = read(sock, &status, sizeof(int));
36 	BUG_ON(ret <= 0, "read(sock)");
37 
38 	*smap = 0x22222BAD;
39 	ret = msync(smap, MMAP_SZ, MS_SYNC);
40 	BUG_ON(ret, "msync()");
41 
42 	ret = write(sock, &status, sizeof(int));
43 	BUG_ON(ret <= 0, "write(sock)");
44 
45 	waitpid(child, &status, 0);
46 	BUG_ON(!WIFEXITED(status), "child in unexpected state");
47 
48 	return WEXITSTATUS(status);
49 }
50 
51 static int child_f(int sock, unsigned long *smap, int fd)
52 {
53 	int ret, buf = 0;
54 
55 	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
56 			MAP_PRIVATE | MAP_POPULATE, fd, 0);
57 	BUG_ON(smap == MAP_FAILED, "mmap()");
58 
59 	BUG_ON(*smap != 0xdeadbabe, "MAP_PRIVATE | MAP_POPULATE changed file");
60 
61 	ret = write(sock, &buf, sizeof(int));
62 	BUG_ON(ret <= 0, "write(sock)");
63 
64 	ret = read(sock, &buf, sizeof(int));
65 	BUG_ON(ret <= 0, "read(sock)");
66 
67 	BUG_ON(*smap == 0x22222BAD, "MAP_POPULATE didn't COW private page");
68 	BUG_ON(*smap != 0xdeadbabe, "mapping was corrupted");
69 
70 	return 0;
71 }
72 
73 int main(int argc, char **argv)
74 {
75 	int sock[2], child, ret;
76 	FILE *ftmp;
77 	unsigned long *smap;
78 
79 	ftmp = tmpfile();
80 	BUG_ON(ftmp == 0, "tmpfile()");
81 
82 	ret = ftruncate(fileno(ftmp), MMAP_SZ);
83 	BUG_ON(ret, "ftruncate()");
84 
85 	smap = mmap(0, MMAP_SZ, PROT_READ | PROT_WRITE,
86 			MAP_SHARED, fileno(ftmp), 0);
87 	BUG_ON(smap == MAP_FAILED, "mmap()");
88 
89 	*smap = 0xdeadbabe;
90 	/* Probably unnecessary, but let it be. */
91 	ret = msync(smap, MMAP_SZ, MS_SYNC);
92 	BUG_ON(ret, "msync()");
93 
94 	ret = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sock);
95 	BUG_ON(ret, "socketpair()");
96 
97 	child = fork();
98 	BUG_ON(child == -1, "fork()");
99 
100 	if (child) {
101 		ret = close(sock[0]);
102 		BUG_ON(ret, "close()");
103 
104 		return parent_f(sock[1], smap, child);
105 	}
106 
107 	ret = close(sock[1]);
108 	BUG_ON(ret, "close()");
109 
110 	return child_f(sock[0], smap, fileno(ftmp));
111 }
112