1/*	$OpenBSD: minherit_zero.c,v 1.1 2014/06/13 07:17:54 matthew Exp $	*/
2/*
3 * Copyright (c) 2014 Google Inc.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/mman.h>
19#include <sys/wait.h>
20#include <assert.h>
21#include <fcntl.h>
22#include <paths.h>
23#include <string.h>
24#include <unistd.h>
25
26#define CHECK(x) assert((x))
27#define CHECK_EQ(a, b) assert((a) == (b))
28#define CHECK_NE(a, b) assert((a) != (b))
29#define CHECK_GE(a, b) assert((a) >= (b))
30
31static int
32ismemset(const void *s, int c, size_t n)
33{
34	const unsigned char *p = s;
35	size_t i;
36
37	for (i = 0; i < n; i++)
38		if (p[i] != c)
39			return (0);
40	return (1);
41}
42
43static void
44wait_for_clean_exit(pid_t pid)
45{
46	int status;
47	CHECK_EQ(pid, waitpid(pid, &status, 0));
48	CHECK(WIFEXITED(status));
49	CHECK_EQ(0, WEXITSTATUS(status));
50}
51
52enum {
53	NPAGES = 4,
54
55	PARENT_BYTE = 42,
56	CHILD_BYTE = 53,
57	GRANDCHILD_BYTE = 65
58};
59
60/*
61 * We map some memory, configure it's inheritance for MAP_INHERIT_ZERO,
62 * then check that when we fork child or grandchild processes, that they
63 * receive new zero'd out memory mappings.  Additionally, we sanity check
64 * that after the child (or grandchild) process exits, that the parent's
65 * memory is still in tact.
66 */
67static void
68dotest(int fd, size_t len, int flags)
69{
70	void *p;
71	pid_t pid;
72
73	p = mmap(NULL, len, PROT_READ|PROT_WRITE, flags, fd, 0);
74	CHECK_NE(MAP_FAILED, p);
75
76	CHECK_EQ(0, minherit(p, len, MAP_INHERIT_ZERO));
77
78	memset(p, PARENT_BYTE, len);
79
80	pid = fork();
81	CHECK_GE(pid, 0);
82	if (pid == 0) {
83		CHECK(ismemset(p, 0, len));
84		memset(p, CHILD_BYTE, len);
85
86		pid = fork();
87		CHECK_GE(pid, 0);
88		if (pid == 0) {
89			CHECK(ismemset(p, 0, len));
90			memset(p, GRANDCHILD_BYTE, len);
91			_exit(0);
92		}
93
94		wait_for_clean_exit(pid);
95		CHECK(ismemset(p, CHILD_BYTE, len));
96		memset(p, 0, len);
97		_exit(0);
98	}
99
100	wait_for_clean_exit(pid);
101	CHECK(ismemset(p, PARENT_BYTE, len));
102	memset(p, 0, len);
103
104	CHECK_EQ(0, munmap(p, len));
105}
106
107int
108main()
109{
110	long pagesize;
111	size_t len;
112
113	pagesize = sysconf(_SC_PAGESIZE);
114	CHECK_GE(pagesize, 1);
115	len = NPAGES * pagesize;
116
117	/* First run test with private anonymous memory. */
118	dotest(-1, len, MAP_ANON|MAP_PRIVATE);
119
120	/* Test again with shared anonymous memory. */
121	dotest(-1, len, MAP_ANON|MAP_SHARED);
122
123	/* Finally, test with private file mapping. */
124	int fd = open(_PATH_BSHELL, O_RDONLY);
125	CHECK_GE(fd, 0);
126	dotest(fd, len, MAP_FILE|MAP_PRIVATE);
127	CHECK_EQ(0, close(fd));
128
129	return (0);
130}
131