Ticket #17895: fork-mprotect-cow.c

File fork-mprotect-cow.c, 5.8 KB (added by korli, 2 years ago)
Line 
1/*
2 * libhugetlbfs - Easy use of Linux hugepages
3 * Copyright (C) 2008 David Gibson, IBM Corporation.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#define _GNU_SOURCE
20
21#include <sys/types.h>
22//#include <sys/shm.h>
23#include <sys/wait.h>
24#include <sys/mman.h>
25#include <errno.h>
26#include <setjmp.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <unistd.h>
34//#include <hugetlbfs.h>
35#include "hugetests.h"
36
37/*
38 * Test rationale:
39 *
40 * This checks copy-on-write semantics, specifically the semantics of
41 * a MAP_PRIVATE mapping across a fork(). Some versions of the
42 * powerpc kernel had a bug in huge_ptep_set_wrprotect() which would
43 * fail to flush the hash table after setting the write protect bit in
44 * the parent's page tables, thus allowing the parent to pollute the
45 * child's mapping.
46 */
47
48#define RANDOM_CONSTANT 0x1234ABCD
49#define OTHER_CONSTANT 0xfeef5678
50
51/*
52 * The parent uses this to check if the child terminated badly.
53 */
54static void sigchld_handler(int signum, siginfo_t *si, void *uc)
55{
56 if (WEXITSTATUS(si->si_status) != 0)
57 FAIL("Child failed: %d", WEXITSTATUS(si->si_status));
58 if (WIFSIGNALED(si->si_status))
59 FAIL("Child recived signal %s",
60 strsignal(WTERMSIG(si->si_status)));
61}
62
63static sigjmp_buf sig_escape;
64static void *sig_expected = MAP_FAILED;
65
66static void sig_handler(int signum, siginfo_t *si, void *uc)
67{
68 if (signum == SIGSEGV) {
69 verbose_printf("SIGSEGV at %p (sig_expected=%p)\n", si->si_addr,
70 sig_expected);
71 if (si->si_addr == sig_expected) {
72 siglongjmp(sig_escape, 1);
73 }
74 FAIL("SIGSEGV somewhere unexpected");
75 }
76 FAIL("Unexpected signal %s", strsignal(signum));
77}
78
79static int test_read(void *p)
80{
81 volatile unsigned long *pl = p;
82 unsigned long x;
83
84 if (sigsetjmp(sig_escape, 1)) {
85 /* We got a SEGV */
86 sig_expected = MAP_FAILED;
87 return -1;
88 }
89
90 sig_expected = p;
91 barrier();
92 x = *pl;
93 verbose_printf("Read back %lu\n", x);
94 barrier();
95 sig_expected = MAP_FAILED;
96 /*
97 * gcc 5 complains about x not ever being used, the following
98 * statement is solely here to shut it up
99 */
100 pl = (unsigned long *)x;
101
102 return 0;
103}
104
105static int test_write(void *p, unsigned long val)
106{
107 volatile unsigned long *pl = p;
108 unsigned long x;
109
110 if (sigsetjmp(sig_escape, 1)) {
111 /* We got a SEGV */
112 sig_expected = MAP_FAILED;
113 return -1;
114 }
115
116 sig_expected = p;
117 barrier();
118 *pl = val;
119 x = *pl;
120 barrier();
121 sig_expected = MAP_FAILED;
122
123 return (x != val);
124}
125
126
127int main(int argc, char ** argv)
128{
129 int fd, ret, status;
130 void *syncarea;
131 volatile unsigned int *p;
132 volatile unsigned int *trigger, *child_readback;
133 unsigned int parent_readback;
134 long hpage_size;
135 pid_t pid;
136 int prot;
137 int err;
138 struct sigaction sa = {
139 .sa_sigaction = sigchld_handler,
140 .sa_flags = SA_SIGINFO,
141 };
142
143 test_init(argc, argv);
144
145// check_free_huge_pages(2);
146
147 if (argc != 1)
148 CONFIG("Usage: fork-cow\n");
149
150 /* Get a shared normal page for synchronization */
151 verbose_printf("Mapping synchronization area..");
152 syncarea = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
153 MAP_SHARED|MAP_ANONYMOUS, -1, 0);
154 if (syncarea == MAP_FAILED)
155 FAIL("mmap() sync area: %s", strerror(errno));
156 verbose_printf("done\n");
157
158 trigger = syncarea;
159 *trigger = 0;
160
161 child_readback = trigger + 1;
162 *child_readback = 0;
163
164 hpage_size = check_hugepagesize();
165
166 fd = hugetlbfs_unlinked_fd(hpage_size*8);
167 if (fd < 0)
168 CONFIG("hugetlbfs_unlinked_fd() failed\n");
169
170 verbose_printf("Mapping hugepage area...");
171 p = mmap(NULL, hpage_size*8, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
172 if (p == MAP_FAILED)
173 FAIL("mmap(): %s", strerror(errno));
174 verbose_printf("mapped at %p\n", p);
175
176 /* Touch the page for write in parent */
177 verbose_printf("Parent writes pre-fork...");
178 *p = RANDOM_CONSTANT;
179 verbose_printf("%x\n", RANDOM_CONSTANT);
180
181 prot = 0;
182 verbose_printf("mprotect()ing to prot=%x\n", prot);
183 err = mprotect(p, hpage_size, prot);
184 if (err != 0)
185 FAIL("mprotect(prot=%x): %s", prot,
186 strerror(errno));
187
188
189 ret = sigaction(SIGCHLD, &sa, NULL);
190 if (ret)
191 FAIL("sigaction(): %s", strerror(errno));
192
193 struct sigaction sa2 = {
194 .sa_sigaction = sig_handler,
195 .sa_flags = SA_SIGINFO,
196 };
197
198 err = sigaction(SIGSEGV, &sa2, NULL);
199 if (err)
200 FAIL("Can't install SIGSEGV handler: %s", strerror(errno));
201
202
203 if ((pid = fork()) < 0)
204 FAIL("fork(): %s", strerror(errno));
205
206 if (pid != 0) {
207 int r;
208 /* Parent */
209 verbose_printf("Parent reading..");
210 r = test_read(p);
211 verbose_printf("%d\n", r);
212
213 *trigger = 1;
214
215 while (*trigger != 2)
216 ;
217
218 if (prot & PROT_READ) {
219 if (r != 0)
220 FAIL("Parent read failed on mmap(prot=%x)", prot);
221 } else {
222 if (r != -1)
223 FAIL("Parent read succeeded on mmap(prot=%x)", prot);
224 }
225 } else {
226 int r;
227 /* Child */
228 verbose_printf("Child starts..\n");
229 while (*trigger != 1)
230 ;
231
232 verbose_printf("Child reading..");
233 r = test_read(p);
234 verbose_printf("%d\n", r);
235
236 *trigger = 2;
237
238 if (prot & PROT_READ) {
239 if (r != 0)
240 FAIL("Child read failed on mmap(prot=%x)", prot);
241 } else {
242 if (r != -1)
243 FAIL("Child read succeeded on mmap(prot=%x)", prot);
244 }
245
246 verbose_printf("Child exits...\n");
247 exit(0);
248 }
249
250 ret = waitpid(pid, &status, 0);
251 if (ret < 0)
252 FAIL("waitpid(): %s", strerror(errno));
253
254 PASS();
255}