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
ismemset(const void * s,int c,size_t n)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
wait_for_clean_exit(pid_t pid)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
dotest(int fd,size_t len,int flags)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
main()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