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 "native_client/src/trusted/service_runtime/sys_memory.h"
8 :
9 : #include <errno.h>
10 : #include <string.h>
11 :
12 : #include "native_client/src/include/nacl_assert.h"
13 : #include "native_client/src/include/nacl_platform.h"
14 : #include "native_client/src/shared/platform/nacl_check.h"
15 : #include "native_client/src/shared/platform/nacl_log.h"
16 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
17 : #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
18 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
19 : #include "native_client/src/trusted/fault_injection/fault_injection.h"
20 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
21 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
22 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
23 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
24 : #include "native_client/src/trusted/service_runtime/internal_errno.h"
25 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
26 : #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
27 : #include "native_client/src/trusted/service_runtime/nacl_text.h"
28 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
29 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
30 : #include "native_client/src/trusted/validator/validation_metadata.h"
31 :
32 : #if NACL_WINDOWS
33 : # include "native_client/src/shared/platform/win/xlate_system_error.h"
34 : #endif
35 :
36 :
37 : static int32_t MunmapInternal(struct NaClApp *nap,
38 : uintptr_t sysaddr, size_t length);
39 :
40 : static const size_t kMaxUsableFileSize = (SIZE_T_MAX >> 1);
41 :
42 :
43 71602 : static INLINE size_t size_min(size_t a, size_t b) {
44 214806 : return (a < b) ? a : b;
45 : }
46 :
47 16 : int32_t NaClSysBrk(struct NaClAppThread *natp,
48 16 : uintptr_t new_break) {
49 16 : struct NaClApp *nap = natp->nap;
50 16 : uintptr_t break_addr;
51 16 : int32_t rv = -NACL_ABI_EINVAL;
52 16 : struct NaClVmmapIter iter;
53 16 : struct NaClVmmapEntry *ent;
54 16 : struct NaClVmmapEntry *next_ent;
55 16 : uintptr_t sys_break;
56 16 : uintptr_t sys_new_break;
57 16 : uintptr_t usr_last_data_page;
58 16 : uintptr_t usr_new_last_data_page;
59 16 : uintptr_t last_internal_data_addr;
60 16 : uintptr_t last_internal_page;
61 16 : uintptr_t start_new_region;
62 16 : uintptr_t region_size;
63 :
64 : /*
65 : * The sysbrk() IRT interface is deprecated and is not enabled for
66 : * ABI-stable PNaCl pexes, so for security hardening, disable the
67 : * syscall under PNaCl too.
68 : */
69 16 : if (nap->pnacl_mode)
70 0 : return -NACL_ABI_ENOSYS;
71 :
72 16 : break_addr = nap->break_addr;
73 :
74 16 : NaClLog(3, "Entered NaClSysBrk(new_break 0x%08"NACL_PRIxPTR")\n",
75 : new_break);
76 :
77 16 : sys_new_break = NaClUserToSysAddr(nap, new_break);
78 16 : NaClLog(3, "sys_new_break 0x%08"NACL_PRIxPTR"\n", sys_new_break);
79 :
80 16 : if (kNaClBadAddress == sys_new_break) {
81 13 : goto cleanup_no_lock;
82 : }
83 3 : if (NACL_SYNC_OK != NaClMutexLock(&nap->mu)) {
84 0 : NaClLog(LOG_ERROR, "Could not get app lock for 0x%08"NACL_PRIxPTR"\n",
85 : (uintptr_t) nap);
86 0 : goto cleanup_no_lock;
87 : }
88 3 : if (new_break < nap->data_end) {
89 0 : NaClLog(4, "new_break before data_end (0x%"NACL_PRIxPTR")\n",
90 : nap->data_end);
91 0 : goto cleanup;
92 : }
93 3 : if (new_break <= nap->break_addr) {
94 : /* freeing memory */
95 0 : NaClLog(4, "new_break before break (0x%"NACL_PRIxPTR"); freeing\n",
96 : nap->break_addr);
97 0 : nap->break_addr = new_break;
98 0 : break_addr = new_break;
99 0 : } else {
100 : /*
101 : * See if page containing new_break is in mem_map; if so, we are
102 : * essentially done -- just update break_addr. Otherwise, we
103 : * extend the VM map entry from the page containing the current
104 : * break to the page containing new_break.
105 : */
106 :
107 3 : sys_break = NaClUserToSys(nap, nap->break_addr);
108 :
109 3 : usr_last_data_page = (nap->break_addr - 1) >> NACL_PAGESHIFT;
110 :
111 3 : usr_new_last_data_page = (new_break - 1) >> NACL_PAGESHIFT;
112 :
113 3 : last_internal_data_addr = NaClRoundAllocPage(new_break) - 1;
114 3 : last_internal_page = last_internal_data_addr >> NACL_PAGESHIFT;
115 :
116 3 : NaClLog(4, ("current break sys addr 0x%08"NACL_PRIxPTR", "
117 : "usr last data page 0x%"NACL_PRIxPTR"\n"),
118 : sys_break, usr_last_data_page);
119 3 : NaClLog(4, "new break usr last data page 0x%"NACL_PRIxPTR"\n",
120 : usr_new_last_data_page);
121 3 : NaClLog(4, "last internal data addr 0x%08"NACL_PRIxPTR"\n",
122 : last_internal_data_addr);
123 :
124 3 : if (NULL == NaClVmmapFindPageIter(&nap->mem_map,
125 : usr_last_data_page,
126 : &iter)
127 3 : || NaClVmmapIterAtEnd(&iter)) {
128 0 : NaClLog(LOG_FATAL, ("current break (0x%08"NACL_PRIxPTR", "
129 : "sys 0x%08"NACL_PRIxPTR") "
130 : "not in address map\n"),
131 : nap->break_addr, sys_break);
132 0 : }
133 3 : ent = NaClVmmapIterStar(&iter);
134 3 : NaClLog(4, ("segment containing current break"
135 : ": page_num 0x%08"NACL_PRIxPTR", npages 0x%"NACL_PRIxS"\n"),
136 : ent->page_num, ent->npages);
137 3 : if (usr_new_last_data_page < ent->page_num + ent->npages) {
138 0 : NaClLog(4, "new break within break segment, just bumping addr\n");
139 0 : nap->break_addr = new_break;
140 0 : break_addr = new_break;
141 0 : } else {
142 3 : NaClVmmapIterIncr(&iter);
143 3 : if (!NaClVmmapIterAtEnd(&iter)
144 3 : && ((next_ent = NaClVmmapIterStar(&iter))->page_num
145 : <= last_internal_page)) {
146 : /* ran into next segment! */
147 1 : NaClLog(4,
148 : ("new break request of usr address "
149 : "0x%08"NACL_PRIxPTR" / usr page 0x%"NACL_PRIxPTR
150 : " runs into next region, page_num 0x%"NACL_PRIxPTR", "
151 : "npages 0x%"NACL_PRIxS"\n"),
152 : new_break, usr_new_last_data_page,
153 : next_ent->page_num, next_ent->npages);
154 1 : goto cleanup;
155 : }
156 2 : NaClLog(4,
157 : "extending segment: page_num 0x%08"NACL_PRIxPTR", "
158 : "npages 0x%"NACL_PRIxS"\n",
159 : ent->page_num, ent->npages);
160 : /* go ahead and extend ent to cover, and make pages accessible */
161 2 : start_new_region = (ent->page_num + ent->npages) << NACL_PAGESHIFT;
162 2 : ent->npages = (last_internal_page - ent->page_num + 1);
163 2 : region_size = (((last_internal_page + 1) << NACL_PAGESHIFT)
164 : - start_new_region);
165 2 : if (0 != NaClMprotect((void *) NaClUserToSys(nap, start_new_region),
166 : region_size,
167 : PROT_READ | PROT_WRITE)) {
168 0 : NaClLog(LOG_FATAL,
169 : ("Could not mprotect(0x%08"NACL_PRIxPTR", "
170 : "0x%08"NACL_PRIxPTR", "
171 : "PROT_READ|PROT_WRITE)\n"),
172 : start_new_region,
173 : region_size);
174 0 : }
175 2 : NaClLog(4, "segment now: page_num 0x%08"NACL_PRIxPTR", "
176 : "npages 0x%"NACL_PRIxS"\n",
177 : ent->page_num, ent->npages);
178 2 : nap->break_addr = new_break;
179 2 : break_addr = new_break;
180 : }
181 : /*
182 : * Zero out memory between old break and new break.
183 : */
184 6 : ASSERT(sys_new_break > sys_break);
185 6 : memset((void *) sys_break, 0, sys_new_break - sys_break);
186 2 : }
187 :
188 :
189 :
190 : cleanup:
191 3 : NaClXMutexUnlock(&nap->mu);
192 : cleanup_no_lock:
193 :
194 : /*
195 : * This cast is safe because the incoming value (new_break) cannot
196 : * exceed the user address space--even though its type (uintptr_t)
197 : * theoretically allows larger values.
198 : */
199 16 : rv = (int32_t) break_addr;
200 :
201 16 : NaClLog(3, "NaClSysBrk: returning 0x%08"NACL_PRIx32"\n", rv);
202 16 : return rv;
203 16 : }
204 :
205 139811 : int NaClSysCommonAddrRangeContainsExecutablePages(struct NaClApp *nap,
206 139811 : uintptr_t usraddr,
207 139811 : size_t length) {
208 : /*
209 : * NOTE: currently only trampoline and text region are executable,
210 : * and they are at the beginning of the address space, so this code
211 : * is fine. We will probably never allow users to mark other pages
212 : * as executable; but if so, we will have to revisit how this check
213 : * is implemented.
214 : *
215 : * nap->static_text_end is a multiple of 4K, the memory protection
216 : * granularity. Since this routine is used for checking whether
217 : * memory map adjustments / allocations -- which has 64K granularity
218 : * -- is okay, usraddr must be an allocation granularity value. Our
219 : * callers (as of this writing) does this, but we truncate it down
220 : * to an allocation boundary to be sure.
221 : */
222 279622 : UNREFERENCED_PARAMETER(length);
223 139811 : usraddr = NaClTruncAllocPage(usraddr);
224 139811 : return usraddr < nap->dynamic_text_end;
225 : }
226 :
227 16 : int NaClSysCommonAddrRangeInAllowedDynamicCodeSpace(struct NaClApp *nap,
228 16 : uintptr_t usraddr,
229 16 : size_t length) {
230 16 : uintptr_t usr_region_end = usraddr + length;
231 :
232 16 : if (usr_region_end < usraddr) {
233 : /* Check for unsigned addition overflow */
234 0 : return 0;
235 : }
236 16 : usr_region_end = NaClRoundAllocPage(usr_region_end);
237 16 : if (usr_region_end < usraddr) {
238 : /* 32-bit systems only, rounding caused uint32_t overflow */
239 0 : return 0;
240 : }
241 32 : return (nap->dynamic_text_start <= usraddr &&
242 : usr_region_end <= nap->dynamic_text_end);
243 16 : }
244 :
245 : /* Warning: sizeof(nacl_abi_off_t)!=sizeof(off_t) on OSX */
246 71527 : int32_t NaClSysMmapIntern(struct NaClApp *nap,
247 71527 : void *start,
248 71527 : size_t length,
249 71527 : int prot,
250 71527 : int flags,
251 71527 : int d,
252 71527 : nacl_abi_off_t offset) {
253 71527 : int allowed_flags;
254 71527 : struct NaClDesc *ndp;
255 71527 : uintptr_t usraddr;
256 71527 : uintptr_t usrpage;
257 71527 : uintptr_t sysaddr;
258 71527 : uintptr_t endaddr;
259 71527 : int mapping_code;
260 71527 : uintptr_t map_result;
261 71527 : int holding_app_lock;
262 71527 : struct nacl_abi_stat stbuf;
263 71527 : size_t alloc_rounded_length;
264 71527 : nacl_off64_t file_size;
265 71527 : nacl_off64_t file_bytes;
266 71527 : nacl_off64_t host_rounded_file_bytes;
267 71527 : size_t alloc_rounded_file_bytes;
268 :
269 71527 : holding_app_lock = 0;
270 71527 : ndp = NULL;
271 :
272 71527 : allowed_flags = (NACL_ABI_MAP_FIXED | NACL_ABI_MAP_SHARED
273 : | NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS);
274 :
275 71527 : usraddr = (uintptr_t) start;
276 :
277 71527 : if (0 != (flags & ~allowed_flags)) {
278 0 : NaClLog(2, "invalid mmap flags 0%o, ignoring extraneous bits\n", flags);
279 0 : flags &= allowed_flags;
280 0 : }
281 :
282 71527 : if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
283 : /*
284 : * anonymous mmap, so backing store is just swap: no descriptor is
285 : * involved, and no memory object will be created to represent the
286 : * descriptor.
287 : */
288 71427 : ndp = NULL;
289 71427 : } else {
290 100 : ndp = NaClAppGetDesc(nap, d);
291 100 : if (NULL == ndp) {
292 1 : map_result = -NACL_ABI_EBADF;
293 1 : goto cleanup;
294 : }
295 : }
296 :
297 71526 : mapping_code = 0;
298 : /*
299 : * Check if application is trying to do dynamic code loading by
300 : * mmaping a file.
301 : */
302 71562 : if (0 != (NACL_ABI_PROT_EXEC & prot) &&
303 : 0 != (NACL_ABI_MAP_FIXED & flags) &&
304 : NULL != ndp &&
305 16 : NaClSysCommonAddrRangeInAllowedDynamicCodeSpace(nap, usraddr, length)) {
306 16 : if (!nap->enable_dyncode_syscalls) {
307 7 : NaClLog(LOG_WARNING,
308 : "NaClSysMmap: PROT_EXEC when dyncode syscalls are disabled.\n");
309 7 : map_result = -NACL_ABI_EINVAL;
310 7 : goto cleanup;
311 : }
312 9 : if (0 != (NACL_ABI_PROT_WRITE & prot)) {
313 1 : NaClLog(3,
314 : "NaClSysMmap: asked for writable and executable code pages?!?\n");
315 1 : map_result = -NACL_ABI_EINVAL;
316 1 : goto cleanup;
317 : }
318 8 : mapping_code = 1;
319 71518 : } else if (0 != (prot & NACL_ABI_PROT_EXEC)) {
320 4 : map_result = -NACL_ABI_EINVAL;
321 4 : goto cleanup;
322 : }
323 :
324 : /*
325 : * Starting address must be aligned to worst-case allocation
326 : * granularity. (Windows.)
327 : */
328 71514 : if (!NaClIsAllocPageMultiple(usraddr)) {
329 4 : NaClLog(2, "NaClSysMmap: address not allocation granularity aligned\n");
330 4 : map_result = -NACL_ABI_EINVAL;
331 4 : goto cleanup;
332 : }
333 : /*
334 : * Offset should be non-negative (nacl_abi_off_t is signed). This
335 : * condition is caught when the file is stat'd and checked, and
336 : * offset is ignored for anonymous mappings.
337 : */
338 71510 : if (offset < 0) {
339 0 : NaClLog(1, /* application bug */
340 : "NaClSysMmap: negative file offset: %"NACL_PRIdNACL_OFF"\n",
341 : offset);
342 0 : map_result = -NACL_ABI_EINVAL;
343 0 : goto cleanup;
344 : }
345 : /*
346 : * And offset must be a multiple of the allocation unit.
347 : */
348 71510 : if (!NaClIsAllocPageMultiple((uintptr_t) offset)) {
349 1 : NaClLog(1,
350 : ("NaClSysMmap: file offset 0x%08"NACL_PRIxPTR" not multiple"
351 : " of allocation size\n"),
352 : (uintptr_t) offset);
353 1 : map_result = -NACL_ABI_EINVAL;
354 1 : goto cleanup;
355 : }
356 :
357 71509 : if (0 == length) {
358 2 : map_result = -NACL_ABI_EINVAL;
359 2 : goto cleanup;
360 : }
361 71507 : alloc_rounded_length = NaClRoundAllocPage(length);
362 71507 : if (alloc_rounded_length != length) {
363 80 : if (mapping_code) {
364 1 : NaClLog(3, "NaClSysMmap: length not a multiple of allocation size\n");
365 1 : map_result = -NACL_ABI_EINVAL;
366 1 : goto cleanup;
367 : }
368 79 : NaClLog(1,
369 : "NaClSysMmap: rounded length to 0x%"NACL_PRIxS"\n",
370 : alloc_rounded_length);
371 79 : }
372 :
373 71506 : if (NULL == ndp) {
374 : /*
375 : * Note: sentinel values are bigger than the NaCl module addr space.
376 : */
377 71422 : file_size = kMaxUsableFileSize;
378 71422 : file_bytes = kMaxUsableFileSize;
379 71422 : host_rounded_file_bytes = kMaxUsableFileSize;
380 71422 : alloc_rounded_file_bytes = kMaxUsableFileSize;
381 71422 : } else {
382 : /*
383 : * We stat the file to figure out its actual size.
384 : *
385 : * This is necessary because the POSIXy interface we provide
386 : * allows mapping beyond the extent of a file but Windows'
387 : * interface does not. We simulate the POSIX behaviour on
388 : * Windows.
389 : */
390 84 : map_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
391 : Fstat)(ndp, &stbuf);
392 84 : if (0 != map_result) {
393 0 : goto cleanup;
394 : }
395 :
396 : /*
397 : * Preemptively refuse to map anything that's not a regular file or
398 : * shared memory segment. Other types usually report st_size of zero,
399 : * which the code below will handle by just doing a dummy PROT_NONE
400 : * mapping for the requested size and never attempting the underlying
401 : * NaClDesc Map operation. So without this check, the host OS never
402 : * gets the chance to refuse the mapping operation on an object that
403 : * can't do it.
404 : */
405 123 : if (!NACL_ABI_S_ISREG(stbuf.nacl_abi_st_mode) &&
406 : !NACL_ABI_S_ISSHM(stbuf.nacl_abi_st_mode)) {
407 1 : map_result = -NACL_ABI_ENODEV;
408 1 : goto cleanup;
409 : }
410 :
411 : /*
412 : * BUG(bsy): there's a race between this fstat and the actual mmap
413 : * below. It's probably insoluble. Even if we fstat again after
414 : * mmap and compared, the mmap could have "seen" the file with a
415 : * different size, after which the racing thread restored back to
416 : * the same value before the 2nd fstat takes place.
417 : */
418 83 : file_size = stbuf.nacl_abi_st_size;
419 :
420 83 : if (file_size < offset) {
421 0 : map_result = -NACL_ABI_EINVAL;
422 0 : goto cleanup;
423 : }
424 :
425 83 : file_bytes = file_size - offset;
426 83 : NaClLog(4,
427 : "NaClSysMmapIntern: file_bytes 0x%016"NACL_PRIxNACL_OFF"\n",
428 : file_bytes);
429 83 : if ((nacl_off64_t) kMaxUsableFileSize < file_bytes) {
430 0 : host_rounded_file_bytes = kMaxUsableFileSize;
431 0 : } else {
432 83 : host_rounded_file_bytes = NaClRoundHostAllocPage((size_t) file_bytes);
433 : }
434 :
435 249 : ASSERT(host_rounded_file_bytes <= (nacl_off64_t) kMaxUsableFileSize);
436 : /*
437 : * We need to deal with NaClRoundHostAllocPage rounding up to zero
438 : * from ~0u - n, where n < 4096 or 65536 (== 1 alloc page).
439 : *
440 : * Luckily, file_bytes is at most kMaxUsableFileSize which is
441 : * smaller than SIZE_T_MAX, so it should never happen, but we
442 : * leave the explicit check below as defensive programming.
443 : */
444 : alloc_rounded_file_bytes =
445 83 : NaClRoundAllocPage((size_t) host_rounded_file_bytes);
446 :
447 83 : if (0 == alloc_rounded_file_bytes && 0 != host_rounded_file_bytes) {
448 0 : map_result = -NACL_ABI_ENOMEM;
449 0 : goto cleanup;
450 : }
451 :
452 : /*
453 : * NB: host_rounded_file_bytes and alloc_rounded_file_bytes can be
454 : * zero. Such an mmap just makes memory (offset relative to
455 : * usraddr) in the range [0, alloc_rounded_length) inaccessible.
456 : */
457 : }
458 :
459 : /*
460 : * host_rounded_file_bytes is how many bytes we can map from the
461 : * file, given the user-supplied starting offset. It is at least
462 : * one page. If it came from a real file, it is a multiple of
463 : * host-OS allocation size. it cannot be larger than
464 : * kMaxUsableFileSize.
465 : */
466 71511 : if (mapping_code && (size_t) file_bytes < alloc_rounded_length) {
467 1 : NaClLog(3,
468 : "NaClSysMmap: disallowing partial allocation page extension for"
469 : " short files\n");
470 1 : map_result = -NACL_ABI_EINVAL;
471 1 : goto cleanup;
472 : }
473 71504 : length = size_min(alloc_rounded_length, (size_t) host_rounded_file_bytes);
474 :
475 : /*
476 : * Lock the addr space.
477 : */
478 71504 : NaClXMutexLock(&nap->mu);
479 :
480 71504 : NaClVmHoleOpeningMu(nap);
481 :
482 71504 : holding_app_lock = 1;
483 :
484 71504 : if (0 == (flags & NACL_ABI_MAP_FIXED)) {
485 : /*
486 : * The user wants us to pick an address range.
487 : */
488 70458 : if (0 == usraddr) {
489 : /*
490 : * Pick a hole in addr space of appropriate size, anywhere.
491 : * We pick one that's best for the system.
492 : */
493 70444 : usrpage = NaClVmmapFindMapSpace(&nap->mem_map,
494 : alloc_rounded_length >> NACL_PAGESHIFT);
495 70444 : NaClLog(4, "NaClSysMmap: FindMapSpace: page 0x%05"NACL_PRIxPTR"\n",
496 : usrpage);
497 70444 : if (0 == usrpage) {
498 2 : map_result = -NACL_ABI_ENOMEM;
499 2 : goto cleanup;
500 : }
501 70442 : usraddr = usrpage << NACL_PAGESHIFT;
502 70442 : NaClLog(4, "NaClSysMmap: new starting addr: 0x%08"NACL_PRIxPTR
503 : "\n", usraddr);
504 70442 : } else {
505 : /*
506 : * user supplied an addr, but it's to be treated as a hint; we
507 : * find a hole of the right size in the app's address space,
508 : * according to the usual mmap semantics.
509 : */
510 14 : usrpage = NaClVmmapFindMapSpaceAboveHint(&nap->mem_map,
511 : usraddr,
512 : (alloc_rounded_length
513 : >> NACL_PAGESHIFT));
514 14 : NaClLog(4, "NaClSysMmap: FindSpaceAboveHint: page 0x%05"NACL_PRIxPTR"\n",
515 : usrpage);
516 14 : if (0 == usrpage) {
517 2 : NaClLog(4, "NaClSysMmap: hint failed, doing generic allocation\n");
518 2 : usrpage = NaClVmmapFindMapSpace(&nap->mem_map,
519 : alloc_rounded_length >> NACL_PAGESHIFT);
520 2 : }
521 14 : if (0 == usrpage) {
522 0 : map_result = -NACL_ABI_ENOMEM;
523 0 : goto cleanup;
524 : }
525 14 : usraddr = usrpage << NACL_PAGESHIFT;
526 14 : NaClLog(4, "NaClSysMmap: new starting addr: 0x%08"NACL_PRIxPTR"\n",
527 : usraddr);
528 : }
529 70456 : }
530 :
531 : /*
532 : * Validate [usraddr, endaddr) is okay.
533 : */
534 71502 : if (usraddr >= ((uintptr_t) 1 << nap->addr_bits)) {
535 0 : NaClLog(2,
536 : ("NaClSysMmap: start address (0x%08"NACL_PRIxPTR") outside address"
537 : " space\n"),
538 : usraddr);
539 0 : map_result = -NACL_ABI_EINVAL;
540 0 : goto cleanup;
541 : }
542 71502 : endaddr = usraddr + alloc_rounded_length;
543 71502 : if (endaddr < usraddr) {
544 0 : NaClLog(0,
545 : ("NaClSysMmap: integer overflow -- "
546 : "NaClSysMmap(0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS",0x%x,0x%x,%d,"
547 : "0x%08"NACL_PRIxPTR"\n"),
548 : usraddr, length, prot, flags, d, (uintptr_t) offset);
549 0 : map_result = -NACL_ABI_EINVAL;
550 0 : goto cleanup;
551 : }
552 : /*
553 : * NB: we use > instead of >= here.
554 : *
555 : * endaddr is the address of the first byte beyond the target region
556 : * and it can equal the address space limit. (of course, normally
557 : * the main thread's stack is there.)
558 : */
559 71502 : if (endaddr > ((uintptr_t) 1 << nap->addr_bits)) {
560 0 : NaClLog(2,
561 : ("NaClSysMmap: end address (0x%08"NACL_PRIxPTR") is beyond"
562 : " the end of the address space\n"),
563 : endaddr);
564 0 : map_result = -NACL_ABI_EINVAL;
565 0 : goto cleanup;
566 : }
567 :
568 71502 : if (mapping_code) {
569 5 : NaClLog(4,
570 : "NaClSysMmap: PROT_EXEC requested, usraddr 0x%08"NACL_PRIxPTR
571 : ", length %"NACL_PRIxS"\n",
572 : usraddr, length);
573 10 : if (!NACL_FI("MMAP_BYPASS_DESCRIPTOR_SAFETY_CHECK",
574 : NaClDescIsSafeForMmap(ndp),
575 : 1)) {
576 1 : NaClLog(4, "NaClSysMmap: descriptor not blessed\n");
577 1 : map_result = -NACL_ABI_EINVAL;
578 1 : goto cleanup;
579 : }
580 4 : NaClLog(4, "NaClSysMmap: allowed\n");
581 71501 : } else if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
582 : usraddr,
583 : length)) {
584 3 : NaClLog(2, "NaClSysMmap: region contains executable pages\n");
585 3 : map_result = -NACL_ABI_EINVAL;
586 3 : goto cleanup;
587 : }
588 :
589 71496 : NaClVmIoPendingCheck_mu(nap,
590 : (uint32_t) usraddr,
591 : (uint32_t) (usraddr + length - 1));
592 :
593 : /*
594 : * Force NACL_ABI_MAP_FIXED, since we are specifying address in NaCl
595 : * app address space.
596 : */
597 71496 : flags |= NACL_ABI_MAP_FIXED;
598 :
599 : /*
600 : * Turn off PROT_EXEC -- normal user mmapped pages should not be
601 : * executable. This is primarily for the service runtime's own
602 : * bookkeeping -- prot is used in NaClVmmapAddWithOverwrite and will
603 : * be needed for remapping data pages on Windows if page protection
604 : * is set to PROT_NONE and back.
605 : *
606 : * NB: we've captured the notion of mapping executable memory for
607 : * dynamic library loading etc in mapping_code, so when we do map
608 : * text we will explicitly OR in NACL_ABI_PROT_EXEC as needed.
609 : */
610 71496 : prot &= ~NACL_ABI_PROT_EXEC;
611 :
612 : /*
613 : * Exactly one of NACL_ABI_MAP_SHARED and NACL_ABI_MAP_PRIVATE is set.
614 : */
615 71496 : if ((0 == (flags & NACL_ABI_MAP_SHARED)) ==
616 : (0 == (flags & NACL_ABI_MAP_PRIVATE))) {
617 0 : map_result = -NACL_ABI_EINVAL;
618 0 : goto cleanup;
619 : }
620 :
621 71496 : sysaddr = NaClUserToSys(nap, usraddr);
622 :
623 : /* [0, length) */
624 71496 : if (length > 0) {
625 71496 : if (NULL == ndp) {
626 71417 : NaClLog(4,
627 : ("NaClSysMmap: NaClDescIoDescMap(,,0x%08"NACL_PRIxPTR","
628 : "0x%08"NACL_PRIxS",0x%x,0x%x,0x%08"NACL_PRIxPTR")\n"),
629 : sysaddr, length, prot, flags, (uintptr_t) offset);
630 71417 : map_result = NaClDescIoDescMapAnon(nap->effp,
631 : (void *) sysaddr,
632 : length,
633 : prot,
634 : flags,
635 : (off_t) offset);
636 71496 : } else if (mapping_code) {
637 : /*
638 : * Map a read-only view in trusted memory, ask validator if
639 : * valid without patching; if okay, then map in untrusted
640 : * executable memory. Fallback to using the dyncode_create
641 : * interface otherwise.
642 : *
643 : * On Windows, threads are already stopped by the
644 : * NaClVmHoleOpeningMu invocation above.
645 : *
646 : * For mmap, stopping threads on Windows is needed to ensure
647 : * that nothing gets allocated into the temporary address space
648 : * hole. This would otherwise have been particularly dangerous,
649 : * since the hole is in an executable region. We must abort the
650 : * program if some other trusted thread (or injected thread)
651 : * allocates into this space. We also need interprocessor
652 : * interrupts to flush the icaches associated other cores, since
653 : * they may contain stale data. NB: mmap with PROT_EXEC should
654 : * do this for us, since otherwise loading shared libraries in a
655 : * multithreaded environment cannot work in a portable fashion.
656 : * (Mutex locks only ensure dcache coherency.)
657 : *
658 : * For eventual munmap, stopping threads also involve looking at
659 : * their registers to make sure their %rip/%eip/%ip are not
660 : * inside the region being modified (impossible for initial
661 : * insertion). This is needed because mmap->munmap->mmap could
662 : * cause problems due to scheduler races.
663 : *
664 : * Use NaClDynamicRegionCreate to mark region as allocated.
665 : *
666 : * See NaClElfFileMapSegment in elf_util.c for corresponding
667 : * mmap-based main executable loading.
668 : */
669 4 : uintptr_t image_sys_addr;
670 4 : NaClValidationStatus validator_status = NaClValidationFailed;
671 4 : struct NaClValidationMetadata metadata;
672 4 : int sys_ret; /* syscall return convention */
673 4 : int ret;
674 :
675 4 : NaClLog(4, "NaClSysMmap: checking descriptor type\n");
676 4 : if (NACL_VTBL(NaClDesc, ndp)->typeTag != NACL_DESC_HOST_IO) {
677 0 : NaClLog(4, "NaClSysMmap: not supported type, got %d\n",
678 : NACL_VTBL(NaClDesc, ndp)->typeTag);
679 0 : map_result = -NACL_ABI_EINVAL;
680 0 : goto cleanup;
681 : }
682 :
683 : /*
684 : * First, try to mmap. Check if target address range is
685 : * available. It must be neither in use by NaClText interface,
686 : * nor used by previous mmap'd code. We record mmap'd code
687 : * regions in the NaClText's data structures to avoid having to
688 : * deal with looking in two data structures.
689 : *
690 : * This mapping is PROT_READ | PROT_WRITE, MAP_PRIVATE so that
691 : * if validation fails in read-only mode, we can re-run the
692 : * validator to patch in place.
693 : */
694 :
695 8 : image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)->
696 : Map)(ndp,
697 4 : NaClDescEffectorTrustedMem(),
698 : (void *) NULL,
699 : length,
700 : NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
701 : NACL_ABI_MAP_PRIVATE,
702 : offset);
703 4 : if (NaClPtrIsNegErrno(&image_sys_addr)) {
704 0 : map_result = image_sys_addr;
705 0 : goto cleanup;
706 : }
707 :
708 : /* Ask validator / validation cache */
709 4 : NaClMetadataFromNaClDescCtor(&metadata, ndp);
710 12 : validator_status = NACL_FI("MMAP_FORCE_MMAP_VALIDATION_FAIL",
711 : (*nap->validator->
712 : Validate)(usraddr,
713 : (uint8_t *) image_sys_addr,
714 : length,
715 : 0, /* stubout_mode: no */
716 : 1, /* readonly_text: yes */
717 : nap->cpu_features,
718 : &metadata,
719 : nap->validation_cache),
720 : NaClValidationFailed);
721 4 : NaClLog(3, "NaClSysMmap: prot_exec, validator_status %d\n",
722 : validator_status);
723 4 : NaClMetadataDtor(&metadata);
724 :
725 4 : if (NaClValidationSucceeded == validator_status) {
726 : /*
727 : * Check if target address range is actually available. It
728 : * must be neither in use by NaClText interface, nor used by
729 : * previous mmap'd code. We record mmap'd code regions in the
730 : * NaClText's data structures to avoid lo both having to deal
731 : * with looking in two data structures. We could do this
732 : * first since this is a cheaper check, but it shouldn't
733 : * matter since application errors ought to be rare and we
734 : * shouldn't optimize for error handling, and this makes the
735 : * code simpler (releasing a created region is more code).
736 : */
737 3 : NaClXMutexLock(&nap->dynamic_load_mutex);
738 3 : ret = NaClDynamicRegionCreate(nap, NaClUserToSys(nap, usraddr), length,
739 : 1);
740 3 : NaClXMutexUnlock(&nap->dynamic_load_mutex);
741 3 : if (!ret) {
742 0 : NaClLog(3, "NaClSysMmap: PROT_EXEC region"
743 : " overlaps other dynamic code\n");
744 0 : map_result = -NACL_ABI_EINVAL;
745 0 : goto cleanup;
746 : }
747 : /*
748 : * Remove scratch mapping.
749 : */
750 3 : NaClDescUnmapUnsafe(ndp, (void *) image_sys_addr, length);
751 : /*
752 : * We must succeed in mapping into the untrusted executable
753 : * space, since otherwise it would mean that the temporary
754 : * hole (for Windows) was filled by some other thread, and
755 : * that's unrecoverable. For Linux and OSX, this should never
756 : * happen, since it's an atomic overmap.
757 : */
758 3 : NaClLog(3, "NaClSysMmap: mapping into executable memory\n");
759 3 : image_sys_addr = (*NACL_VTBL(NaClDesc, ndp)->
760 : Map)(ndp,
761 : nap->effp,
762 : (void *) sysaddr,
763 : length,
764 : NACL_ABI_PROT_READ | NACL_ABI_PROT_EXEC,
765 : NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_FIXED,
766 : offset);
767 3 : if (image_sys_addr != sysaddr) {
768 0 : NaClLog(LOG_FATAL,
769 : "NaClSysMmap: map into executable memory failed:"
770 : " got 0x%"NACL_PRIxPTR"\n", image_sys_addr);
771 0 : }
772 3 : map_result = (int32_t) usraddr;
773 3 : goto cleanup;
774 : }
775 :
776 1 : NaClLog(3,
777 : "NaClSysMmap: did not validate in readonly_text mode;"
778 : " attempting to use dyncode interface.\n");
779 :
780 1 : if (holding_app_lock) {
781 1 : NaClVmHoleClosingMu(nap);
782 1 : NaClXMutexUnlock(&nap->mu);
783 1 : }
784 :
785 2 : if (NACL_FI("MMAP_STUBOUT_EMULATION", 0, 1)) {
786 1 : NaClLog(3, "NaClSysMmap: emulating stubout mode by touching memory\n");
787 1 : *(volatile uint8_t *) image_sys_addr =
788 : *(volatile uint8_t *) image_sys_addr;
789 1 : }
790 :
791 : /*
792 : * Fallback implementation. Use the mapped memory as source for
793 : * the dynamic code insertion interface.
794 : */
795 1 : sys_ret = NaClTextDyncodeCreate(nap,
796 : (uint32_t) usraddr,
797 : (uint8_t *) image_sys_addr,
798 : (uint32_t) length,
799 : NULL);
800 1 : if (sys_ret < 0) {
801 0 : map_result = sys_ret;
802 0 : } else {
803 1 : map_result = (int32_t) usraddr;
804 : }
805 :
806 : #if NACL_WINDOWS
807 : sys_ret = (*NACL_VTBL(NaClDesc, ndp)->
808 : UnmapUnsafe)(ndp, (void *) image_sys_addr, length);
809 : #else
810 1 : sys_ret = munmap((void *) image_sys_addr, length);
811 : #endif
812 1 : if (0 != sys_ret) {
813 0 : NaClLog(LOG_FATAL,
814 : "NaClSysMmap: could not unmap text at 0x%"NACL_PRIxPTR","
815 : " length 0x%"NACL_PRIxS", NaCl errno %d\n",
816 : image_sys_addr, length, -sys_ret);
817 0 : }
818 1 : goto cleanup_no_locks;
819 : } else {
820 : /*
821 : * This is a fix for Windows, where we cannot pass a size that
822 : * goes beyond the non-page-rounded end of the file.
823 : */
824 75 : size_t length_to_map = size_min(length, (size_t) file_bytes);
825 :
826 75 : NaClLog(4,
827 : ("NaClSysMmap: (*ndp->Map)(,,0x%08"NACL_PRIxPTR","
828 : "0x%08"NACL_PRIxS",0x%x,0x%x,0x%08"NACL_PRIxPTR")\n"),
829 : sysaddr, length, prot, flags, (uintptr_t) offset);
830 :
831 75 : map_result = (*((struct NaClDescVtbl const *) ndp->base.vtbl)->
832 : Map)(ndp,
833 : nap->effp,
834 : (void *) sysaddr,
835 : length_to_map,
836 : prot,
837 : flags,
838 : (off_t) offset);
839 : }
840 : /*
841 : * "Small" negative integers are errno values. Larger ones are
842 : * virtual addresses.
843 : */
844 71492 : if (NaClPtrIsNegErrno(&map_result)) {
845 2 : if ((uintptr_t) -NACL_ABI_E_MOVE_ADDRESS_SPACE == map_result) {
846 0 : NaClLog(LOG_FATAL,
847 : ("NaClSysMmap: Map failed, but we"
848 : " cannot handle address space move, error %"NACL_PRIuS"\n"),
849 : (size_t) map_result);
850 0 : }
851 : /*
852 : * Propagate all other errors to user code.
853 : */
854 2 : goto cleanup;
855 : }
856 71490 : if (map_result != sysaddr) {
857 0 : NaClLog(LOG_FATAL, "system mmap did not honor NACL_ABI_MAP_FIXED\n");
858 0 : }
859 71490 : }
860 : /*
861 : * If we are mapping beyond the end of the file, we fill this space
862 : * with PROT_NONE pages.
863 : *
864 : * Windows forces us to expose a mixture of 64k and 4k pages, and we
865 : * expose the same mappings on other platforms. For example,
866 : * suppose untrusted code requests to map 0x40000 bytes from a file
867 : * of extent 0x100. We will create the following regions:
868 : *
869 : * 0- 0x100 A: Bytes from the file
870 : * 0x100- 0x1000 B: The rest of the 4k page is accessible but undefined
871 : * 0x1000-0x10000 C: The rest of the 64k page is inaccessible (PROT_NONE)
872 : * 0x10000-0x40000 D: Further 64k pages are also inaccessible (PROT_NONE)
873 : *
874 : * On Windows, a single MapViewOfFileEx() call creates A, B and C.
875 : * This call will not accept a size greater than 0x100, so we have
876 : * to create D separately. The hardware requires B to be accessible
877 : * (whenever A is accessible), but Windows does not allow C to be
878 : * mapped as accessible. This is unfortunate because it interferes
879 : * with how ELF dynamic linkers usually like to set up an ELF
880 : * object's BSS.
881 : */
882 : /* inaccessible: [length, alloc_rounded_length) */
883 71490 : if (length < alloc_rounded_length) {
884 : /*
885 : * On Unix, this maps regions C and D as inaccessible. On
886 : * Windows, it just maps region D; region C has already been made
887 : * inaccessible.
888 : */
889 11 : size_t map_len = alloc_rounded_length - length;
890 11 : map_result = MunmapInternal(nap, sysaddr + length, map_len);
891 11 : if (map_result != 0) {
892 0 : goto cleanup;
893 : }
894 11 : }
895 :
896 71490 : if (alloc_rounded_length > 0) {
897 142980 : NaClVmmapAddWithOverwrite(&nap->mem_map,
898 71490 : NaClSysToUser(nap, sysaddr) >> NACL_PAGESHIFT,
899 : alloc_rounded_length >> NACL_PAGESHIFT,
900 : prot,
901 : flags,
902 : ndp,
903 : offset,
904 : file_size);
905 71490 : }
906 :
907 71490 : map_result = usraddr;
908 :
909 : cleanup:
910 71524 : if (holding_app_lock) {
911 71501 : NaClVmHoleClosingMu(nap);
912 71501 : NaClXMutexUnlock(&nap->mu);
913 143025 : }
914 : cleanup_no_locks:
915 71525 : if (NULL != ndp) {
916 97 : NaClDescUnref(ndp);
917 97 : }
918 :
919 : /*
920 : * Check to ensure that map_result will fit into a 32-bit value. This is
921 : * a bit tricky because there are two valid ranges: one is the range from
922 : * 0 to (almost) 2^32, the other is from -1 to -4096 (our error range).
923 : * For a 32-bit value these ranges would overlap, but if the value is 64-bit
924 : * they will be disjoint.
925 : */
926 71525 : if (map_result > UINT32_MAX
927 31 : && !NaClPtrIsNegErrno(&map_result)) {
928 0 : NaClLog(LOG_FATAL, "Overflow in NaClSysMmap: return address is "
929 : "0x%"NACL_PRIxPTR"\n", map_result);
930 0 : }
931 71525 : NaClLog(3, "NaClSysMmap: returning 0x%08"NACL_PRIxPTR"\n", map_result);
932 :
933 71525 : return (int32_t) map_result;
934 : }
935 :
936 71486 : int32_t NaClSysMmap(struct NaClAppThread *natp,
937 71486 : void *start,
938 71486 : size_t length,
939 71486 : int prot,
940 71486 : int flags,
941 71486 : int d,
942 71486 : nacl_abi_off_t *offp) {
943 71486 : struct NaClApp *nap = natp->nap;
944 71486 : int32_t retval;
945 71486 : uintptr_t sysaddr;
946 71486 : nacl_abi_off_t offset;
947 :
948 71486 : NaClLog(3,
949 : "Entered NaClSysMmap(0x%08"NACL_PRIxPTR",0x%"NACL_PRIxS","
950 : "0x%x,0x%x,%d,0x%08"NACL_PRIxPTR")\n",
951 : (uintptr_t) start, length, prot, flags, d, (uintptr_t) offp);
952 :
953 71486 : if ((nacl_abi_off_t *) 0 == offp) {
954 : /*
955 : * This warning is really targetted towards trusted code,
956 : * especially tests that didn't notice the argument type change.
957 : * Unfortunatey, zero is a common and legitimate offset value, and
958 : * the compiler will not complain since an automatic type
959 : * conversion works.
960 : */
961 0 : NaClLog(LOG_WARNING,
962 : "NaClSysMmap: NULL pointer used"
963 : " for offset in/out argument\n");
964 0 : return -NACL_ABI_EINVAL;
965 : }
966 :
967 71486 : sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) offp, sizeof offset);
968 71486 : if (kNaClBadAddress == sysaddr) {
969 0 : NaClLog(3,
970 : "NaClSysMmap: offset in a bad untrusted memory location\n");
971 0 : retval = -NACL_ABI_EFAULT;
972 0 : goto cleanup;
973 : }
974 71486 : offset = *(nacl_abi_off_t volatile *) sysaddr;
975 :
976 71486 : NaClLog(4, " offset = 0x%08"NACL_PRIxNACL_OFF"\n", offset);
977 :
978 71486 : retval = NaClSysMmapIntern(nap, start, length, prot, flags, d, offset);
979 : cleanup:
980 71484 : return retval;
981 71484 : }
982 :
983 : #if NACL_WINDOWS
984 : static int32_t MunmapInternal(struct NaClApp *nap,
985 : uintptr_t sysaddr, size_t length) {
986 : uintptr_t addr;
987 : uintptr_t endaddr = sysaddr + length;
988 : uintptr_t usraddr;
989 : for (addr = sysaddr; addr < endaddr; addr += NACL_MAP_PAGESIZE) {
990 : struct NaClVmmapEntry const *entry;
991 : uintptr_t page_num;
992 : uintptr_t offset;
993 :
994 : usraddr = NaClSysToUser(nap, addr);
995 :
996 : entry = NaClVmmapFindPage(&nap->mem_map, usraddr >> NACL_PAGESHIFT);
997 : if (NULL == entry) {
998 : continue;
999 : }
1000 : NaClLog(3,
1001 : ("NaClSysMunmap: addr 0x%08x, desc 0x%08"NACL_PRIxPTR"\n"),
1002 : addr, (uintptr_t) entry->desc);
1003 :
1004 : page_num = usraddr - (entry->page_num << NACL_PAGESHIFT);
1005 : offset = (uintptr_t) entry->offset + page_num;
1006 :
1007 : if (NULL != entry->desc &&
1008 : offset < (uintptr_t) entry->file_size) {
1009 : if (!UnmapViewOfFile((void *) addr)) {
1010 : NaClLog(LOG_FATAL,
1011 : ("MunmapInternal: UnmapViewOfFile failed to at addr"
1012 : " 0x%08"NACL_PRIxPTR", error %d\n"),
1013 : addr, GetLastError());
1014 : }
1015 : /*
1016 : * Fill the address space hole that we opened
1017 : * with UnmapViewOfFile().
1018 : */
1019 : if (!VirtualAlloc((void *) addr, NACL_MAP_PAGESIZE, MEM_RESERVE,
1020 : PAGE_READWRITE)) {
1021 : NaClLog(LOG_FATAL, "MunmapInternal: "
1022 : "failed to fill hole with VirtualAlloc(), error %d\n",
1023 : GetLastError());
1024 : }
1025 : } else {
1026 : /*
1027 : * Anonymous memory; we just decommit it and thus
1028 : * make it inaccessible.
1029 : */
1030 : if (!VirtualFree((void *) addr,
1031 : NACL_MAP_PAGESIZE,
1032 : MEM_DECOMMIT)) {
1033 : int error = GetLastError();
1034 : NaClLog(LOG_FATAL,
1035 : ("MunmapInternal: Could not VirtualFree MEM_DECOMMIT"
1036 : " addr 0x%08x, error %d (0x%x)\n"),
1037 : addr, error, error);
1038 : }
1039 : }
1040 : NaClVmmapRemove(&nap->mem_map,
1041 : usraddr >> NACL_PAGESHIFT,
1042 : NACL_PAGES_PER_MAP);
1043 : }
1044 : return 0;
1045 : }
1046 : #else
1047 68281 : static int32_t MunmapInternal(struct NaClApp *nap,
1048 68281 : uintptr_t sysaddr, size_t length) {
1049 136562 : UNREFERENCED_PARAMETER(nap);
1050 68281 : NaClLog(3, "MunmapInternal(0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS")\n",
1051 : (uintptr_t) sysaddr, length);
1052 : /*
1053 : * Overwrite current mapping with inaccessible, anonymous
1054 : * zero-filled pages, which should be copy-on-write and thus
1055 : * relatively cheap. Do not open up an address space hole.
1056 : */
1057 68281 : if (MAP_FAILED == mmap((void *) sysaddr,
1058 : length,
1059 : PROT_NONE,
1060 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
1061 : -1,
1062 : (off_t) 0)) {
1063 0 : NaClLog(4, "mmap to put in anonymous memory failed, errno = %d\n", errno);
1064 0 : return -NaClXlateErrno(errno);
1065 : }
1066 136562 : NaClVmmapRemove(&nap->mem_map,
1067 68281 : NaClSysToUser(nap, (uintptr_t) sysaddr) >> NACL_PAGESHIFT,
1068 : length >> NACL_PAGESHIFT);
1069 68281 : return 0;
1070 68281 : }
1071 : #endif
1072 :
1073 68277 : int32_t NaClSysMunmap(struct NaClAppThread *natp,
1074 68277 : void *start,
1075 68277 : size_t length) {
1076 68277 : struct NaClApp *nap = natp->nap;
1077 68277 : int32_t retval = -NACL_ABI_EINVAL;
1078 68277 : uintptr_t sysaddr;
1079 68277 : int holding_app_lock = 0;
1080 68277 : size_t alloc_rounded_length;
1081 :
1082 68277 : NaClLog(3, "Entered NaClSysMunmap(0x%08"NACL_PRIxPTR", "
1083 : "0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS")\n",
1084 : (uintptr_t) natp, (uintptr_t) start, length);
1085 :
1086 68277 : if (!NaClIsAllocPageMultiple((uintptr_t) start)) {
1087 0 : NaClLog(4, "start addr not allocation multiple\n");
1088 0 : retval = -NACL_ABI_EINVAL;
1089 0 : goto cleanup;
1090 : }
1091 68277 : if (0 == length) {
1092 : /*
1093 : * Without this check we would get the following inconsistent
1094 : * behaviour:
1095 : * * On Linux, an mmap() of zero length yields a failure.
1096 : * * On Mac OS X, an mmap() of zero length returns no error,
1097 : * which would lead to a NaClVmmapUpdate() of zero pages, which
1098 : * should not occur.
1099 : * * On Windows we would iterate through the 64k pages and do
1100 : * nothing, which would not yield a failure.
1101 : */
1102 1 : retval = -NACL_ABI_EINVAL;
1103 1 : goto cleanup;
1104 : }
1105 68276 : alloc_rounded_length = NaClRoundAllocPage(length);
1106 68276 : if (alloc_rounded_length != length) {
1107 54 : length = alloc_rounded_length;
1108 54 : NaClLog(2, "munmap: rounded length to 0x%"NACL_PRIxS"\n", length);
1109 54 : }
1110 68276 : sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) start, length);
1111 68276 : if (kNaClBadAddress == sysaddr) {
1112 0 : NaClLog(4, "munmap: region not user addresses\n");
1113 0 : retval = -NACL_ABI_EFAULT;
1114 0 : goto cleanup;
1115 : }
1116 :
1117 68276 : NaClXMutexLock(&nap->mu);
1118 :
1119 68276 : NaClVmHoleOpeningMu(nap);
1120 :
1121 68276 : holding_app_lock = 1;
1122 :
1123 : /*
1124 : * User should be unable to unmap any executable pages. We check here.
1125 : */
1126 68276 : if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
1127 : (uintptr_t) start,
1128 : length)) {
1129 6 : NaClLog(2, "NaClSysMunmap: region contains executable pages\n");
1130 6 : retval = -NACL_ABI_EINVAL;
1131 6 : goto cleanup;
1132 : }
1133 :
1134 68270 : NaClVmIoPendingCheck_mu(nap,
1135 : (uint32_t) (uintptr_t) start,
1136 : (uint32_t) ((uintptr_t) start + length - 1));
1137 :
1138 68270 : retval = MunmapInternal(nap, sysaddr, length);
1139 : cleanup:
1140 68277 : if (holding_app_lock) {
1141 68276 : NaClVmHoleClosingMu(nap);
1142 68276 : NaClXMutexUnlock(&nap->mu);
1143 68276 : }
1144 68277 : return retval;
1145 : }
1146 :
1147 : #if NACL_WINDOWS
1148 : static int32_t MprotectInternal(struct NaClApp *nap,
1149 : uintptr_t sysaddr, size_t length, int prot) {
1150 : uintptr_t addr;
1151 : uintptr_t endaddr = sysaddr + length;
1152 : uintptr_t usraddr;
1153 : DWORD flProtect;
1154 : DWORD flOldProtect;
1155 :
1156 : /*
1157 : * VirtualProtect region cannot span allocations, all addresses must be
1158 : * in one region of memory returned from VirtualAlloc or VirtualAllocEx.
1159 : */
1160 : for (addr = sysaddr; addr < endaddr; addr += NACL_MAP_PAGESIZE) {
1161 : struct NaClVmmapEntry const *entry;
1162 : uintptr_t page_num;
1163 : uintptr_t offset;
1164 :
1165 : usraddr = NaClSysToUser(nap, addr);
1166 :
1167 : entry = NaClVmmapFindPage(&nap->mem_map, usraddr >> NACL_PAGESHIFT);
1168 : if (NULL == entry) {
1169 : continue;
1170 : }
1171 : NaClLog(3, "MprotectInternal: addr 0x%08x, desc 0x%08"NACL_PRIxPTR"\n",
1172 : addr, (uintptr_t) entry->desc);
1173 :
1174 : page_num = usraddr - (entry->page_num << NACL_PAGESHIFT);
1175 : offset = (uintptr_t) entry->offset + page_num;
1176 :
1177 : if (NULL == entry->desc) {
1178 : flProtect = NaClflProtectMap(prot);
1179 :
1180 : /* Change the page protection */
1181 : if (!VirtualProtect((void *) addr,
1182 : NACL_MAP_PAGESIZE,
1183 : flProtect,
1184 : &flOldProtect)) {
1185 : int error = GetLastError();
1186 : NaClLog(LOG_FATAL, "MprotectInternal: "
1187 : "failed to change the memory protection with VirtualProtect,"
1188 : " addr 0x%08x, error %d (0x%x)\n",
1189 : addr, error, error);
1190 : return -NaClXlateSystemError(error);
1191 : }
1192 : } else if (offset < (uintptr_t) entry->file_size) {
1193 : nacl_off64_t file_bytes;
1194 : size_t chunk_size;
1195 : size_t rounded_chunk_size;
1196 : int desc_flags;
1197 : char const *err_msg;
1198 :
1199 : desc_flags = (*NACL_VTBL(NaClDesc, entry->desc)->GetFlags)(entry->desc);
1200 : NaClflProtectAndDesiredAccessMap(prot,
1201 : (entry->flags
1202 : & NACL_ABI_MAP_PRIVATE) != 0,
1203 : (desc_flags & NACL_ABI_O_ACCMODE),
1204 : /* flMaximumProtect= */ NULL,
1205 : &flProtect,
1206 : /* dwDesiredAccess= */ NULL,
1207 : &err_msg);
1208 : if (0 == flProtect) {
1209 : /*
1210 : * This shouldn't really happen since we already checked the address
1211 : * space using NaClVmmapCheckExistingMapping, but better be safe.
1212 : */
1213 : NaClLog(LOG_FATAL, "MprotectInternal: %s\n", err_msg);
1214 : }
1215 :
1216 : file_bytes = entry->file_size - offset;
1217 : chunk_size = size_min((size_t) file_bytes, NACL_MAP_PAGESIZE);
1218 : rounded_chunk_size = NaClRoundPage(chunk_size);
1219 :
1220 : NaClLog(4, "VirtualProtect(0x%08x, 0x%"NACL_PRIxS", %x)\n",
1221 : addr, rounded_chunk_size, flProtect);
1222 :
1223 : /* Change the page protection */
1224 : if (!VirtualProtect((void *) addr,
1225 : rounded_chunk_size,
1226 : flProtect,
1227 : &flOldProtect)) {
1228 : int error = GetLastError();
1229 : NaClLog(LOG_FATAL, "MprotectInternal: "
1230 : "failed to change the memory protection with VirtualProtect()"
1231 : " addr 0x%08x, error %d (0x%x)\n",
1232 : addr, error, error);
1233 : return -NaClXlateSystemError(error);
1234 : }
1235 : }
1236 : }
1237 :
1238 : return 0;
1239 : }
1240 : #else
1241 33 : static int32_t MprotectInternal(struct NaClApp *nap,
1242 33 : uintptr_t sysaddr, size_t length, int prot) {
1243 33 : uintptr_t usraddr;
1244 33 : uintptr_t last_page_num;
1245 33 : int host_prot;
1246 33 : struct NaClVmmapIter iter;
1247 33 : struct NaClVmmapEntry *entry;
1248 :
1249 33 : host_prot = NaClProtMap(prot);
1250 :
1251 33 : usraddr = NaClSysToUser(nap, sysaddr);
1252 33 : last_page_num = (usraddr + length) >> NACL_PAGESHIFT;
1253 :
1254 33 : NaClVmmapFindPageIter(&nap->mem_map,
1255 : usraddr >> NACL_PAGESHIFT,
1256 : &iter);
1257 33 : entry = NaClVmmapIterStar(&iter);
1258 :
1259 99 : CHECK(usraddr == (entry->page_num << NACL_PAGESHIFT));
1260 :
1261 103 : for (; !NaClVmmapIterAtEnd(&iter) &&
1262 70 : (NaClVmmapIterStar(&iter))->page_num < last_page_num;
1263 37 : NaClVmmapIterIncr(&iter)) {
1264 37 : uintptr_t addr;
1265 37 : size_t entry_len;
1266 :
1267 37 : entry = NaClVmmapIterStar(&iter);
1268 :
1269 37 : addr = NaClUserToSys(nap, entry->page_num << NACL_PAGESHIFT);
1270 37 : entry_len = entry->npages << NACL_PAGESHIFT;
1271 :
1272 37 : NaClLog(3, "MprotectInternal: "
1273 : "addr 0x%08"NACL_PRIxPTR", desc 0x%08"NACL_PRIxPTR"\n",
1274 : addr, (uintptr_t) entry->desc);
1275 :
1276 37 : if (NULL == entry->desc) {
1277 14 : if (0 != mprotect((void *) addr, entry_len, host_prot)) {
1278 0 : NaClLog(LOG_FATAL, "MprotectInternal: "
1279 0 : "mprotect on anonymous memory failed, errno = %d\n", errno);
1280 0 : return -NaClXlateErrno(errno);
1281 : }
1282 37 : } else if (entry->offset < entry->file_size) {
1283 23 : nacl_abi_off64_t file_bytes;
1284 23 : size_t rounded_file_bytes;
1285 23 : size_t prot_len;
1286 :
1287 23 : file_bytes = entry->file_size - entry->offset;
1288 23 : rounded_file_bytes = NaClRoundPage((size_t) file_bytes);
1289 23 : prot_len = size_min(rounded_file_bytes, entry_len);
1290 :
1291 23 : if (0 != mprotect((void *) addr, prot_len, host_prot)) {
1292 0 : NaClLog(LOG_FATAL, "MprotectInternal: "
1293 0 : "mprotect on file-backed memory failed, errno = %d\n", errno);
1294 0 : return -NaClXlateErrno(errno);
1295 : }
1296 23 : }
1297 37 : }
1298 :
1299 99 : CHECK((entry->page_num + entry->npages) == last_page_num);
1300 :
1301 33 : return 0;
1302 33 : }
1303 : #endif
1304 :
1305 38 : int32_t NaClSysMprotectInternal(struct NaClApp *nap,
1306 38 : uint32_t start,
1307 38 : size_t length,
1308 38 : int prot) {
1309 38 : int32_t retval = -NACL_ABI_EINVAL;
1310 38 : uintptr_t sysaddr;
1311 38 : int holding_app_lock = 0;
1312 :
1313 38 : if (!NaClIsAllocPageMultiple((uintptr_t) start)) {
1314 0 : NaClLog(4, "mprotect: start addr not allocation multiple\n");
1315 0 : retval = -NACL_ABI_EINVAL;
1316 0 : goto cleanup;
1317 : }
1318 38 : length = NaClRoundAllocPage(length);
1319 38 : sysaddr = NaClUserToSysAddrRange(nap, (uintptr_t) start, length);
1320 38 : if (kNaClBadAddress == sysaddr) {
1321 0 : NaClLog(4, "mprotect: region not user addresses\n");
1322 0 : retval = -NACL_ABI_EFAULT;
1323 0 : goto cleanup;
1324 : }
1325 38 : if (0 != (~(NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE) & prot)) {
1326 0 : NaClLog(4, "mprotect: prot has other bits than PROT_{READ|WRITE}\n");
1327 0 : retval = -NACL_ABI_EACCES;
1328 0 : goto cleanup;
1329 : }
1330 :
1331 38 : NaClXMutexLock(&nap->mu);
1332 :
1333 38 : holding_app_lock = 1;
1334 :
1335 : /*
1336 : * User should be unable to change protection of any executable pages.
1337 : */
1338 38 : if (NaClSysCommonAddrRangeContainsExecutablePages(nap,
1339 : (uintptr_t) start,
1340 : length)) {
1341 0 : NaClLog(2, "mprotect: region contains executable pages\n");
1342 0 : retval = -NACL_ABI_EACCES;
1343 0 : goto cleanup;
1344 : }
1345 :
1346 76 : if (!NaClVmmapChangeProt(&nap->mem_map,
1347 38 : NaClSysToUser(nap, sysaddr) >> NACL_PAGESHIFT,
1348 : length >> NACL_PAGESHIFT,
1349 : prot)) {
1350 5 : NaClLog(4, "mprotect: no such region\n");
1351 5 : retval = -NACL_ABI_EACCES;
1352 5 : goto cleanup;
1353 : }
1354 :
1355 33 : NaClVmIoPendingCheck_mu(nap,
1356 : (uint32_t) (uintptr_t) start,
1357 : (uint32_t) ((uintptr_t) start + length - 1));
1358 :
1359 33 : retval = MprotectInternal(nap, sysaddr, length, prot);
1360 : cleanup:
1361 38 : if (holding_app_lock) {
1362 38 : NaClXMutexUnlock(&nap->mu);
1363 38 : }
1364 38 : return retval;
1365 : }
1366 :
1367 31 : int32_t NaClSysMprotect(struct NaClAppThread *natp,
1368 31 : uint32_t start,
1369 31 : size_t length,
1370 31 : int prot) {
1371 31 : struct NaClApp *nap = natp->nap;
1372 :
1373 31 : NaClLog(3, "Entered NaClSysMprotect(0x%08"NACL_PRIxPTR", "
1374 : "0x%08"NACL_PRIxPTR", 0x%"NACL_PRIxS", 0x%x)\n",
1375 : (uintptr_t) natp, (uintptr_t) start, length, prot);
1376 :
1377 31 : return NaClSysMprotectInternal(nap, start, length, prot);
1378 : }
|