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 
31 static int
32 ismemset(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 
43 static void
44 wait_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 
52 enum {
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  */
67 static void
68 dotest(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 
107 int
108 main()
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