1 : /*
2 : * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <limits.h>
8 : #include <stdio.h>
9 : #include <string.h>
10 :
11 : #if !NACL_WINDOWS
12 : # include <sys/mman.h>
13 : #endif
14 :
15 : #include "native_client/src/include/portability.h"
16 : #include "native_client/src/include/portability_io.h"
17 : #include "native_client/src/include/nacl_compiler_annotations.h"
18 : #include "native_client/src/include/nacl_macros.h"
19 :
20 : #include "native_client/src/shared/platform/nacl_check.h"
21 : #include "native_client/src/shared/platform/nacl_host_desc.h"
22 : #include "native_client/src/shared/platform/nacl_log.h"
23 : #include "native_client/src/shared/platform/platform_init.h"
24 : #include "native_client/src/trusted/desc/nacl_desc_effector.h"
25 : #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
26 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
27 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
28 :
29 : static const size_t kNumFileBytes = 2 * 0x10000;
30 :
31 : #if !NACL_WINDOWS
32 : # define NaClHostDescUnmapUnsafe munmap
33 : #endif
34 :
35 :
36 : /*
37 : * mmap PROT_EXEC test
38 : *
39 : * Use a file containing mmappable code. We don't want to
40 : * re-implement a dynamic loader here, nor do we want to duplicate a
41 : * lot of ELF parsing.
42 : */
43 :
44 1 : int prot_exec_test(struct NaClHostDesc *d, void *test_specifics) {
45 1 : struct NaClDescEffector *null_eff = NaClDescEffectorTrustedMem();
46 : uintptr_t addr;
47 : int (*func)(int param);
48 : int param;
49 : int value;
50 :
51 : UNREFERENCED_PARAMETER(test_specifics);
52 :
53 : if ((uintptr_t) -4095 <
54 : (addr = NaClHostDescMap(d,
55 : null_eff,
56 : NULL,
57 : kNumFileBytes,
58 : NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC,
59 : NACL_ABI_MAP_SHARED,
60 1 : /* offset */ 0))) {
61 0 : fprintf(stderr, "prot_exec_test: map failed, errno %d\n", -(int) addr);
62 0 : return 1;
63 : }
64 :
65 1 : func = (int (*)(int)) addr;
66 1 : for (param = 0; param < 16; ++param) {
67 1 : printf("%d -> ", param);
68 1 : fflush(stdout);
69 1 : value = (*func)(param);
70 1 : printf("%d\n", value);
71 1 : CHECK(value == param+1);
72 1 : }
73 :
74 1 : CHECK(0 == NaClHostDescUnmapUnsafe((void *) addr, kNumFileBytes));
75 :
76 1 : return 0;
77 1 : }
78 :
79 : /*
80 : * mmap MAP_SHARED test
81 : *
82 : * Make sure two views of the same file see the changes made from one
83 : * view in the other.
84 : */
85 1 : int map_shared_test(struct NaClHostDesc *d, void *test_specifics) {
86 1 : struct NaClDescEffector *null_eff = NaClDescEffectorTrustedMem();
87 : uintptr_t view1;
88 : uintptr_t view2;
89 : char *v1ptr;
90 : char *v2ptr;
91 :
92 : UNREFERENCED_PARAMETER(test_specifics);
93 :
94 : if ((uintptr_t) -4095 <
95 : (view1 = NaClHostDescMap(d,
96 : null_eff,
97 : NULL,
98 : kNumFileBytes,
99 : NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
100 : NACL_ABI_MAP_SHARED,
101 1 : /* offset */ 0))) {
102 : fprintf(stderr, "map_shared_test: view1 map failed, errno %d\n",
103 0 : -(int) view1);
104 0 : return 1;
105 : }
106 :
107 : if ((uintptr_t) -4095 <
108 : (view2 = NaClHostDescMap(d,
109 : null_eff,
110 : NULL,
111 : kNumFileBytes,
112 : NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
113 : NACL_ABI_MAP_SHARED,
114 1 : /* offset */ 0))) {
115 : fprintf(stderr, "map_shared_test: view2 map failed, errno %d\n",
116 0 : -(int) view2);
117 0 : return 1;
118 : }
119 :
120 1 : v1ptr = (char *) view1;
121 1 : v2ptr = (char *) view2;
122 :
123 1 : CHECK(v1ptr[0] == '\0');
124 1 : CHECK(v2ptr[0] == '\0');
125 1 : v1ptr[0] = 'x';
126 1 : CHECK(v2ptr[0] == 'x');
127 1 : v2ptr[0x400] = 'y';
128 1 : CHECK(v1ptr[0x400] == 'y');
129 :
130 1 : CHECK(0 == NaClHostDescUnmapUnsafe((void *) view1, kNumFileBytes));
131 1 : CHECK(0 == NaClHostDescUnmapUnsafe((void *) view2, kNumFileBytes));
132 :
133 1 : return 0;
134 1 : }
135 :
136 : struct MapPrivateSpecifics {
137 : int shm_not_write;
138 : };
139 :
140 : /*
141 : * mmap MAP_PRIVATE test
142 : *
143 : * Make sure that a MAP_PRIVATE view initially sees the changes made
144 : * in a MAP_SHARED view, but after touching the private view further
145 : * changes become invisible.
146 : */
147 1 : int map_private_test(struct NaClHostDesc *d, void *test_specifics) {
148 : struct MapPrivateSpecifics *params =
149 1 : (struct MapPrivateSpecifics *) test_specifics;
150 1 : struct NaClDescEffector *null_eff = NaClDescEffectorTrustedMem();
151 : uintptr_t view1;
152 : uintptr_t view2;
153 : nacl_off64_t off;
154 : ssize_t bytes_written;
155 : char *v1ptr;
156 : char *v2ptr;
157 :
158 : if ((uintptr_t) -4095 <
159 : (view1 = NaClHostDescMap(d,
160 : null_eff,
161 : NULL,
162 : kNumFileBytes,
163 : NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
164 : NACL_ABI_MAP_SHARED,
165 1 : /* offset */ 0))) {
166 : fprintf(stderr, "map_private_test: view1 map failed, errno %d\n",
167 0 : -(int) view1);
168 0 : return 1;
169 : }
170 :
171 1 : NaClLog(2, "map_private_test: view1 = 0x%"NACL_PRIxPTR"\n", view1);
172 :
173 : if ((uintptr_t) -4095 <
174 : (view2 = NaClHostDescMap(d,
175 : null_eff,
176 : NULL,
177 : kNumFileBytes,
178 : NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
179 : NACL_ABI_MAP_PRIVATE,
180 1 : /* offset */ 0))) {
181 : fprintf(stderr, "map_private_test: view2 map failed, errno %d\n",
182 0 : -(int) view2);
183 0 : return 1;
184 : }
185 :
186 1 : NaClLog(2, "map_private_test: view2 = 0x%"NACL_PRIxPTR"\n", view2);
187 :
188 1 : v1ptr = (char *) view1;
189 1 : v2ptr = (char *) view2;
190 :
191 1 : CHECK(v1ptr[0] == '\0');
192 1 : CHECK(v2ptr[0] == '\0');
193 1 : if (params->shm_not_write) {
194 1 : NaClLog(2, "map_private_test: changing via shm view\n");
195 1 : v1ptr[0] = 'x'; /* write through shared view */
196 1 : } else {
197 1 : NaClLog(2, "map_private_test: changing via write interface\n");
198 1 : off = NaClHostDescSeek(d, 0, 0);
199 1 : if (off < 0) {
200 0 : fprintf(stderr, "Could not seek: NaCl errno %d\n", (int) -off);
201 0 : return 1;
202 : }
203 1 : bytes_written = NaClHostDescWrite(d, "x", 1);
204 1 : if (1 != bytes_written) {
205 0 : fprintf(stderr, "Could not write: NaCl errno %d\n", (int) -bytes_written);
206 0 : return 1;
207 : }
208 : }
209 : #if NACL_LINUX || NACL_WINDOWS
210 : /*
211 : * Most OSes have this behavior: a PRIVATE mapping is copy-on-write,
212 : * but the COW occurs when the fault occurs on that mapping, not
213 : * other mappings; otherwise, the page tables just point the system
214 : * to the buffer cache (or, if evicted, a stub entry that permits
215 : * faulting in the page). So, a write through a writable file
216 : * descriptor or a SHARED mapping would modify the buffer cache, and
217 : * the PRIVATE mapping would see such changes until a fault occurs.
218 : */
219 1 : CHECK(v2ptr[0] == 'x'); /* visible! */
220 : #elif NACL_OSX
221 : /*
222 : * On OSX, however, the underlying Mach primitives provide
223 : * bidirectional COW.
224 : */
225 : CHECK(v2ptr[0] == '\0'); /* NOT visible! */
226 : #else
227 : # error "Unsupported OS"
228 : #endif
229 :
230 1 : v2ptr[0] = 'z'; /* COW fault */
231 1 : v1ptr[0] = 'y';
232 1 : CHECK(v2ptr[0] == 'z'); /* private! */
233 :
234 1 : CHECK(v1ptr[0x400] == '\0');
235 1 : v2ptr[0x400] = 'y';
236 1 : CHECK(v1ptr[0x400] == '\0');
237 :
238 1 : CHECK(0 == NaClHostDescUnmapUnsafe((void *) view1, kNumFileBytes));
239 1 : CHECK(0 == NaClHostDescUnmapUnsafe((void *) view2, kNumFileBytes));
240 :
241 1 : return 0;
242 1 : }
243 :
244 : /*
245 : * Write out kNumFileBytes (a multiple of 64K) bytes of data.
246 : */
247 1 : int CreateTestData(struct NaClHostDesc *d) {
248 : size_t nbytes;
249 1 : size_t written = 0;
250 : ssize_t result;
251 : static char buffer[4096];
252 :
253 1 : memset(buffer, 0, sizeof buffer);
254 1 : while (written < kNumFileBytes) {
255 1 : nbytes = kNumFileBytes - written;
256 1 : if (nbytes > sizeof buffer) {
257 1 : nbytes = sizeof buffer;
258 : }
259 1 : result = NaClHostDescWrite(d, buffer, nbytes);
260 1 : if (result < 0) {
261 0 : return (int) result;
262 : }
263 1 : written += result;
264 1 : }
265 1 : return 0;
266 1 : }
267 :
268 : struct TestParams {
269 : char const *test_name;
270 : int (*test_func)(struct NaClHostDesc *, void *test_specifics);
271 : int open_flags;
272 : int file_perms;
273 :
274 : unsigned char const *test_data_start;
275 : size_t test_data_size;
276 :
277 : void *test_specifics;
278 : };
279 :
280 : void CreateTestFile(struct NaClHostDesc *d_out,
281 : char const *pathname,
282 1 : struct TestParams *param) {
283 : struct NaClHostDesc hd;
284 : int err;
285 : nacl_off64_t off;
286 : size_t desired_write;
287 : ssize_t bytes_written;
288 :
289 1 : printf("pathname = %s, perms 0%o\n", pathname, param->file_perms);
290 : if (0 != (err = NaClHostDescOpen(&hd,
291 : pathname,
292 : NACL_ABI_O_WRONLY |
293 : NACL_ABI_O_CREAT |
294 : NACL_ABI_O_TRUNC,
295 1 : param->file_perms))) {
296 0 : fprintf(stderr, "Could not open test scratch file: NaCl errno %d\n", -err);
297 0 : exit(1);
298 : }
299 1 : if (0 != (err = CreateTestData(&hd))) {
300 : fprintf(stderr,
301 : "Could not write test data into test scratch file: NaCl errno %d\n",
302 0 : -err);
303 0 : exit(1);
304 : }
305 1 : if (NULL != param->test_data_start) {
306 1 : off = NaClHostDescSeek(&hd, 0, 0);
307 1 : if (off < 0) {
308 : fprintf(stderr,
309 : "Could not seek to create test data: NaCl errno %d\n",
310 0 : (int) -off);
311 0 : exit(1);
312 : }
313 1 : desired_write = param->test_data_size;
314 : bytes_written = NaClHostDescWrite(&hd,
315 : param->test_data_start,
316 1 : desired_write);
317 1 : if (bytes_written < 0) {
318 : fprintf(stderr,
319 : "Could not write specialized test data: NaCl errno %d\n",
320 0 : (int) -bytes_written);
321 0 : exit(1);
322 : }
323 1 : if ((size_t) bytes_written != desired_write) {
324 : fprintf(stderr,
325 : "Error while writing specialized test data:"
326 : " tried to write %d, actual %d\n",
327 0 : (int) desired_write, (int) bytes_written);
328 0 : exit(1);
329 : }
330 : }
331 1 : if (0 != (err = NaClHostDescClose(&hd))) {
332 : fprintf(stderr,
333 0 : "Error while closing test data file, errno %d\n", -err);
334 0 : exit(1);
335 : }
336 : if (0 != (err = NaClHostDescOpen(d_out,
337 : pathname,
338 : param->open_flags,
339 1 : param->file_perms))) {
340 0 : fprintf(stderr, "Could not open test scratch file: NaCl errno %d\n", -err);
341 0 : exit(1);
342 : }
343 1 : }
344 :
345 1 : void CloseTestFile(struct NaClHostDesc *d) {
346 1 : CHECK(0 == NaClHostDescClose(d));
347 1 : }
348 :
349 : struct MapPrivateSpecifics test0 = { 0 };
350 : struct MapPrivateSpecifics test1 = { 1 };
351 :
352 : /*
353 : * This is the function the machine instructions for which is below.
354 : * Instead of making assumptions about taking addresses of functions,
355 : * we open-code the machine instructions. Since porting this test now
356 : * requires a little more effort, here is the procedure: Put in
357 : * "garbage" data in the test_machine_code for your architecture.
358 : * Build the Test. The test will fail -- don't bother to run it.
359 : * However, you should now be able to use a debugger to disassemble
360 : * the x_plus_1 function, and update the test_machine_code contents.
361 : */
362 0 : int x_plus_1(int x) {
363 0 : return x+1;
364 0 : }
365 :
366 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
367 : # if NACL_BUILD_SUBARCH == 32
368 : unsigned char const test_machine_code[] = {
369 : 0x8b, 0x44, 0x24, 0x04, 0x83, 0xc0, 0x01, 0xc3,
370 : };
371 : # elif NACL_BUILD_SUBARCH == 64
372 : # if NACL_WINDOWS
373 : unsigned char const test_machine_code[] = {
374 : 0x8d, 0x41, 0x01, 0xc3, /* lea 0x1(%rcx), %eax; ret */
375 : };
376 : # else
377 : unsigned char const test_machine_code[] = {
378 : 0x8d, 0x47, 0x01, 0xc3, /* lea 0x1(%rdi), %eax; ret */
379 : };
380 : # endif
381 : # else
382 : # error "Who leaked the secret x86-128 project?"
383 : # endif
384 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
385 : unsigned char const test_machine_code[] = {
386 : 0x01, 0x00, 0x80, 0xe2, 0x1e, 0xff, 0x2f, 0xe1
387 : };
388 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
389 : unsigned char const test_machine_code[] = {
390 : 0x01, 0x00, 0x82, 0x20, /* addi v0, a0, 1 */
391 : 0x08, 0x00, 0xe0, 0x03, /* jr ra */
392 : 0x00, 0x00, 0x00, 0x00, /* nop */
393 : };
394 : #else
395 : # error "What architecture?"
396 : #endif
397 :
398 : struct TestParams tests[] = {
399 : {
400 : "Shared Mapping Test",
401 : map_shared_test,
402 : (NACL_ABI_O_RDWR | NACL_ABI_O_CREAT), 0666,
403 : NULL, 0,
404 : NULL,
405 : }, {
406 : "Private Mapping Test, modify by write",
407 : map_private_test,
408 : (NACL_ABI_O_RDWR | NACL_ABI_O_CREAT), 0666,
409 : NULL, 0,
410 : &test0,
411 : }, {
412 : "Private Mapping Test, modify by shm",
413 : map_private_test,
414 : (NACL_ABI_O_RDWR | NACL_ABI_O_CREAT), 0666,
415 : NULL, 0,
416 : &test1,
417 : }, {
418 : "PROT_EXEC Mapping Test",
419 : prot_exec_test,
420 : (NACL_ABI_O_RDWR | NACL_ABI_O_CREAT), 0777,
421 : test_machine_code,
422 : sizeof test_machine_code,
423 : NULL,
424 : },
425 : };
426 :
427 : /*
428 : * It is the responsibility of the invoking environment to delete the
429 : * file passed as command-line argument. See the build.scons file.
430 : */
431 1 : int main(int ac, char **av) {
432 1 : char const *test_dir_name = "/tmp/nacl_host_desc_test";
433 : struct NaClHostDesc hd;
434 : size_t err_count;
435 : size_t ix;
436 : int opt;
437 1 : int num_runs = 1;
438 : int test_run;
439 :
440 1 : while (EOF != (opt = getopt(ac, av, "c:t:"))) {
441 1 : switch (opt) {
442 : case 'c':
443 0 : num_runs = atoi(optarg);
444 0 : break;
445 : case 't':
446 1 : test_dir_name = optarg;
447 1 : break;
448 : default:
449 : fprintf(stderr,
450 : "Usage: nacl_host_desc_mmap_test [-c run_count]\n"
451 0 : " [-t test_temp_dir]\n");
452 0 : exit(1);
453 : }
454 1 : }
455 :
456 1 : NaClPlatformInit();
457 :
458 1 : err_count = 0;
459 1 : for (test_run = 0; test_run < num_runs; ++test_run) {
460 1 : printf("Test run %d\n\n", test_run);
461 1 : for (ix = 0; ix < NACL_ARRAY_SIZE(tests); ++ix) {
462 : char test_file_name[PATH_MAX];
463 : SNPRINTF(test_file_name, sizeof test_file_name,
464 1 : "%s/f%d.%"NACL_PRIuS, test_dir_name, test_run, ix);
465 1 : printf("%s\n", tests[ix].test_name);
466 1 : CreateTestFile(&hd, test_file_name, &tests[ix]);
467 1 : err_count += (*tests[ix].test_func)(&hd, tests[ix].test_specifics);
468 1 : CloseTestFile(&hd);
469 1 : }
470 1 : }
471 :
472 1 : NaClPlatformFini();
473 :
474 : /* we ignore the 2^32 or 2^64 total errors case */
475 1 : return (err_count > 255) ? 255 : err_count;
476 1 : }
|