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
31static 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
51static 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
73int 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, "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