1 : /*
2 : * Copyright (c) 2012 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 : /*
8 : * NaCl Service Runtime. I/O Descriptor / Handle abstraction. Memory
9 : * mapping using descriptors.
10 : */
11 : #include "native_client/src/include/portability.h"
12 : #include "native_client/src/include/portability_io.h"
13 :
14 : #include <windows.h>
15 : #include <direct.h>
16 : #include <io.h>
17 : #include <sys/types.h>
18 : #include <sys/stat.h>
19 : #include <share.h>
20 :
21 : #include "native_client/src/include/nacl_macros.h"
22 : #include "native_client/src/include/nacl_platform.h"
23 : #include "native_client/src/shared/platform/nacl_check.h"
24 : #include "native_client/src/shared/platform/nacl_find_addrsp.h"
25 : #include "native_client/src/shared/platform/nacl_host_desc.h"
26 : #include "native_client/src/shared/platform/nacl_log.h"
27 : #include "native_client/src/shared/platform/nacl_sync.h"
28 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
29 : #include "native_client/src/shared/platform/win/xlate_system_error.h"
30 : #include "native_client/src/trusted/desc/nacl_desc_effector.h"
31 : #include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
32 :
33 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
34 : #include "native_client/src/trusted/service_runtime/internal_errno.h"
35 : #include "native_client/src/trusted/service_runtime/sel_util-inl.h"
36 :
37 : #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
38 : #include "native_client/src/trusted/service_runtime/include/sys/errno.h"
39 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
40 : #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
41 : #include "native_client/src/trusted/service_runtime/include/sys/unistd.h"
42 :
43 : #define OFFSET_FOR_FILEPOS_LOCK (GG_LONGLONG(0x7000000000000000))
44 :
45 : /*
46 : * By convention, we use locking the byte at OFFSET_FOR_FILEPOS_LOCK
47 : * as locking for the implicit file position associated with a file
48 : * handle. According to MSDN, LockFileEx of a byte range that does
49 : * not (yet) exist in a file is not an error, which makes sense in
50 : * that one might want to have exclusive access to a file region that
51 : * is beyond the end of the file before populating it. We assume that
52 : * OFFSET_FOR_FILEPOS_LOCK is large enough that no real file will
53 : * actually be that big (even if sparse) and cause problems.
54 : *
55 : * One drawback of this is that two independent file handles on the
56 : * same file will share the same lock. If this leads to actual
57 : * contention issues, we can use the following randomized approach,
58 : * ASSUMING that each file handle / posix-level host descriptor is
59 : * introduced to NaCl at most once (e.g., no dup'ing and invoking
60 : * NaClHostDescPosixTake multiple times): we pick a random offset from
61 : * OFFSET_FOR_FILEPOS_LOCK, and make sure we transfer that with the
62 : * file handle in the nrd_xfer protocol. This way, we use a range of
63 : * byte offsets for locking files and avoid false contention. We
64 : * would be subject to the birthday paradox, of course, so if we
65 : * picked a 16-bit random offset to use, then if a file is opened ~256
66 : * times we would start seeing performance issues caused by
67 : * contention, which is probably acceptable; a 32-bit nonce would be
68 : * plenty.
69 : *
70 : * On Windows, fcntl is not available. A very similar function to
71 : * lockf, _locking, exists in the Windows CRT. It does not permit
72 : * specification of the start of a region, only size (just like lockf)
73 : * -- implicitly from the current position -- which is less than
74 : * useful for our purposes.
75 : */
76 4 : static void NaClTakeFilePosLock(HANDLE hFile) {
77 : OVERLAPPED overlap;
78 : DWORD err;
79 :
80 4 : memset(&overlap, 0, sizeof overlap);
81 4 : overlap.Offset = (DWORD) OFFSET_FOR_FILEPOS_LOCK;
82 4 : overlap.OffsetHigh = (DWORD) (OFFSET_FOR_FILEPOS_LOCK >> 32);
83 : /*
84 : * LockFileEx should never fail -- untrusted code cannot cause hFile
85 : * to become invalid, since all NaClHostDesc objects are wrapped in
86 : * NaClDesc objects and all uses of NaClDesc objects take a
87 : * reference before use, so a threading race that closes a
88 : * descriptor at the untrusted code level will only dereference the
89 : * NaClDesc (and make it unavailable to the untrusted code), but the
90 : * object will not be destroyed until after the NaClDesc-level
91 : * operation (which in turn invokes the NaClHostDesc level
92 : * operation) completes. Only after the operation completes will
93 : * the reference to the NaClDesc be drop by the syscall handler.
94 : */
95 : if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK,
96 : /* dwReserved= */ 0,
97 : /* nNumberOfBytesToLockLow= */ 1,
98 : /* nNumberOfBytesToLockHigh= */ 0,
99 4 : &overlap)) {
100 0 : err = GetLastError();
101 : NaClLog(LOG_FATAL, "NaClTakeFilePosLock: LockFileEx failed, error %u\n",
102 0 : err);
103 : }
104 4 : }
105 :
106 : /*
107 : * Map our ABI to the host OS's ABI.
108 : * Note: there is no X bit equivalent on windows so NACL_ABI_S_IXUSR
109 : * is ignored.
110 : */
111 0 : static INLINE mode_t NaClMapMode(nacl_abi_mode_t abi_mode) {
112 0 : mode_t m = 0;
113 0 : if (0 != (abi_mode & NACL_ABI_S_IRUSR))
114 0 : m |= _S_IREAD;
115 0 : if (0 != (abi_mode & NACL_ABI_S_IWUSR))
116 0 : m |= _S_IWRITE;
117 0 : return m;
118 0 : }
119 :
120 : /* Windows doesn't define R_OK or W_OK macros but expects these constants */
121 : #define WIN_F_OK 0
122 : #define WIN_R_OK 4
123 : #define WIN_W_OK 2
124 :
125 : /*
126 : * Map our ABI to the host OS's ABI.
127 : * There is no X_OK (0x1) on win32 so we ignore
128 : * NACL_ABI_X_OK and report everything that exists
129 : * as being executable.
130 : */
131 0 : static INLINE int NaClMapAccessMode(int nacl_mode) {
132 0 : int mode = 0;
133 0 : if (nacl_mode == NACL_ABI_F_OK) {
134 0 : mode = WIN_F_OK;
135 0 : } else {
136 0 : if (nacl_mode & NACL_ABI_R_OK)
137 0 : mode |= WIN_R_OK;
138 0 : if (nacl_mode & NACL_ABI_W_OK)
139 0 : mode |= WIN_W_OK;
140 : }
141 0 : return mode;
142 0 : }
143 :
144 4 : static void NaClDropFilePosLock(HANDLE hFile) {
145 : OVERLAPPED overlap;
146 : DWORD err;
147 :
148 4 : memset(&overlap, 0, sizeof overlap);
149 4 : overlap.Offset = (DWORD) OFFSET_FOR_FILEPOS_LOCK;
150 4 : overlap.OffsetHigh = (DWORD) (OFFSET_FOR_FILEPOS_LOCK >> 32);
151 : if (!UnlockFileEx(hFile,
152 : /* dwReserved= */ 0,
153 : /* nNumberOfBytesToLockLow= */ 1,
154 : /* nNumberOfBytesToLockHigh= */ 0,
155 4 : &overlap)) {
156 0 : err = GetLastError();
157 : NaClLog(LOG_FATAL, "NaClDropFilePosLock: UnlockFileEx failed, error %u\n",
158 0 : err);
159 : }
160 4 : }
161 :
162 1 : static nacl_off64_t NaClLockAndGetCurrentFilePos(HANDLE hFile) {
163 : LARGE_INTEGER to_move;
164 : LARGE_INTEGER cur_pos;
165 : DWORD err;
166 :
167 1 : NaClTakeFilePosLock(hFile);
168 1 : to_move.QuadPart = 0;
169 1 : if (!SetFilePointerEx(hFile, to_move, &cur_pos, FILE_CURRENT)) {
170 0 : err = GetLastError();
171 : NaClLog(LOG_FATAL,
172 : "NaClLockAndGetCurrentFilePos: SetFilePointerEx failed, error %u\n",
173 0 : err);
174 : }
175 1 : return cur_pos.QuadPart;
176 1 : }
177 :
178 : static void NaClSetCurrentFilePosAndUnlock(HANDLE hFile,
179 1 : nacl_off64_t pos) {
180 : LARGE_INTEGER to_move;
181 : DWORD err;
182 :
183 1 : to_move.QuadPart = pos;
184 1 : if (!SetFilePointerEx(hFile, to_move, (LARGE_INTEGER *) NULL, FILE_BEGIN)) {
185 0 : err = GetLastError();
186 : NaClLog(LOG_FATAL,
187 : "NaClSetCurrentFilePosAndUnlock: SetFilePointerEx failed:"
188 : " error %d\n",
189 0 : err);
190 : }
191 1 : NaClDropFilePosLock(hFile);
192 1 : }
193 :
194 : /*
195 : * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
196 : *
197 : * The implementation of the host descriptor abstractions will
198 : * probably change. In particularly, blocking I/O calls must be
199 : * interruptible in order to implement the address-space move
200 : * mechanism for mmap error recovery, and the it seems that the only
201 : * way that this would be feasible is to do the following: instead of
202 : * using the POSIX abstraction layer, do the I/O using Windows file
203 : * handles opened for asynchronous operations. When a potentially
204 : * blocking system call (e.g., read or write) is performed, use
205 : * overlapped I/O via ReadFile/WriteFile to initiate the I/O operation
206 : * in a non-blocking manner, and use a separate event object, so that
207 : * the thread can, after initiating the I/O, perform
208 : * WaitForMultipleObjects on both I/O completion (event in the
209 : * OVERLAPPED structure) and on mmap-generated interrupts. The event
210 : * can be signalled via SetEvent by any other thread that wish to
211 : * perform a safe mmap operation.
212 : *
213 : * When the safe mmap is to occur, all other application threads are
214 : * stopped (beware, of course, of the race condition where two threads
215 : * try to do mmap), and the remaining running thread performs
216 : * VirtualFree and MapViewOfFileEx. If a thread (from some injected
217 : * DLL) puts some memory in the hole created by VirtualFree before the
218 : * MapViewOfFileEx runs, then we have to move the entire address space
219 : * to avoid allowing the untrusted NaCl app from touching this
220 : * innocent thread's memory.
221 : *
222 : * What this implies is that a mechanism must exist in order for the
223 : * mmapping thread to stop all other application threads, and this is
224 : * why the blocking syscalls must be interruptible. When interrupted,
225 : * the thread that initiated the I/O must perform CancelIo and check,
226 : * via GetOverlappedResult, to see how much have completed, etc, then
227 : * put itself into a restartable state -- we might simply return EINTR
228 : * if no work has been dnoe and require libc to restart the syscall in
229 : * the SysV style, though it should be possible to just restart the
230 : * syscall in the BSD style -- and to signal the mmapping thread that
231 : * it is ready.
232 : *
233 : * Alternatively, these interrupted operations can return a private
234 : * version of EAGAIN, so that the code calling the host descriptor
235 : * (syscall handler) can quiesce the thread and restart the I/O
236 : * operation once the address space move is complete.
237 : *
238 : * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
239 : */
240 :
241 : /*
242 : * TODO(bsy, gregoryd): check that _get_errno is indeed a thread-safe way
243 : * to get the error from the last 'syscall' into the posix layer.
244 : */
245 0 : int GetErrno(void) {
246 : int thread_errno;
247 :
248 0 : (void) _get_errno(&thread_errno);
249 0 : return NaClXlateErrno(thread_errno);
250 0 : }
251 :
252 3 : static INLINE size_t size_min(size_t a, size_t b) {
253 3 : return (a < b) ? a : b;
254 3 : }
255 :
256 : /*
257 : * The mapping and unmapping code work in 64K chunks rather than a
258 : * single large allocation because all of our uses will use 64K
259 : * chunks. Higher level code keeps track of whether memory came from
260 : * VirtualAlloc or NaClHostDescMap, and will call the appropriate
261 : * deallocation functions.
262 : *
263 : * NB: if prot is NACL_ABI_PROT_NONE, then the memory should be
264 : * deallocated via VirtualFree as if it came from paging file rather
265 : * than via a file mapping object representing the paging file (and
266 : * thus UnmapViewOfFile).
267 : */
268 :
269 : /*
270 : * out_flProtect == 0 means error, and the error string can be used
271 : * for a logging message (except for the cases that the caller should
272 : * be checking for).
273 : *
274 : * in parameters are all NACL_ABI_ values or bool (0/1).
275 : *
276 : * accmode may be NACL_ABI_O_RDONLY or NACL_ABI_O_RDWR, but not
277 : * NACL_ABI_O_WRONLY (see below).
278 : *
279 : * Caller should check for:
280 : *
281 : * - PROT_NONE -> special case handling,
282 : * - NACL_ABI_O_APPEND and PROT_WRITE -> EACCES,
283 : * - accmode is O_WRONLY -> EACCES,
284 : *
285 : * The interpretation here is that PROT_EXEC or PROT_WRITE implies
286 : * asking for PROT_READ, since most hardware behaves this way. So if
287 : * the descriptor is O_WRONLY, we generally refuse.
288 : *
289 : * The file mapping object created by CreateFileMapping's flProtect
290 : * argument specifies the MAXIMUM protection, and MapViewOfFileEx will
291 : * request a lesser level of access. We should always
292 : * CreateFileMapping with a high level of access so that
293 : * VirtualProtect (used by mprotect) can be used to turn on write when
294 : * the initial mmap had read-only mappings.
295 : *
296 : * BUG(phosek): Due to Windows XP limitation, in particular the missing
297 : * PAGE_EXECUTE_WRITECOPY protection support for file mapping objects,
298 : * we cannot mmap r/o file as private, read/write and later make it
299 : * executable or vice versa mmap r/o file as private, read/execute and
300 : * later make it writable. This is a platform difference, but since
301 : * untrusted code is not allowed to mmap files as write/execute, this
302 : * difference is invisible to application developers and will therefore
303 : * likely remain unresolved as the solution would likely be very
304 : * expensive. Furthemore, after dropping the support for Windows XP, this
305 : * difference can be easily resolved by updating the flag mapping.
306 : */
307 : void NaClflProtectAndDesiredAccessMap(int prot,
308 : int is_private,
309 : int accmode,
310 : DWORD *out_flMappingProtect,
311 : DWORD *out_flProtect,
312 : DWORD *out_dwDesiredAccess,
313 3 : char const **out_msg) {
314 : #define M(mp,p,da,err) { mp, p, da, err, #mp, #p, #da }
315 : static struct {
316 : DWORD flMappingProtect;
317 : DWORD flProtect;
318 : DWORD dwDesiredAccess;
319 : char const *err;
320 : char const *flMappingProtect_str;
321 : char const *flProtect_str;
322 : char const *dwDesiredAccess_str;
323 : } table[] = {
324 : /* RDONLY */
325 : /* shared */
326 : /* PROT_NONE */
327 : M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
328 : /* PROT_READ */
329 : M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
330 : /* PROT_WRITE */
331 : M(0, 0, 0, "file open for read only; no shared write allowed"),
332 : /* PROT_READ | PROT_WRITE */
333 : M(0, 0, 0, "file open for read only; no shared read/write allowed"),
334 : /* PROT_EXEC */
335 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE,
336 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
337 : /* PROT_READ | PROT_EXEC */
338 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READ,
339 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
340 : /* PROT_WRITE | PROT_EXEC */
341 : M(0, 0, 0, "file open for read only; no shared write/exec allowed"),
342 : /* PROT_READ | PROT_WRITE | PROT_EXEC */
343 : M(0, 0, 0, "file open for read only; no shared read/write/exec allowed"),
344 :
345 : /* is_private */
346 : /* PROT_NONE */
347 : M(PAGE_EXECUTE_READ, PAGE_NOACCESS, FILE_MAP_READ, NULL),
348 : /* PROT_READ */
349 : M(PAGE_EXECUTE_READ, PAGE_READONLY, FILE_MAP_READ, NULL),
350 : /* PROT_WRITE */
351 : M(PAGE_EXECUTE_READ, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
352 : /* PROT_READ | PROT_WRITE */
353 : M(PAGE_EXECUTE_READ, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
354 :
355 : /*
356 : * NB: PAGE_EXECUTE_WRITECOPY is not supported on Server 2003 or
357 : * XP, which means that the mmap will fail. In this case we fallback
358 : * to PAGE_EXECUTE_READ.
359 : *
360 : * Even with PAGE_EXECUTE_WRITECOPY, the PROT_WRITE | PROT_EXEC
361 : * case where we are asking for FILE_MAP_COPY | FILE_MAP_EXECUTE
362 : * seems to always fail, with GetLastError() yielding 87
363 : * (ERROR_INVALID_PARAMETER). This may be due to antivirus.
364 : */
365 :
366 : /* PROT_EXEC */
367 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE,
368 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
369 : /* PROT_READ | PROT_EXEC */
370 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READ,
371 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
372 : /* PROT_WRITE | PROT_EXEC */
373 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY,
374 : FILE_MAP_COPY | FILE_MAP_EXECUTE, NULL),
375 : /* PROT_READ | PROT_WRITE | PROT_EXEC */
376 : M(PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_WRITECOPY,
377 : FILE_MAP_COPY | FILE_MAP_EXECUTE, NULL),
378 :
379 : /* RDWR */
380 : /* shared */
381 : /* PROT_NONE */
382 : M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
383 : /* PROT_READ */
384 : M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
385 : /* PROT_WRITE */
386 : M(PAGE_EXECUTE_READWRITE, PAGE_READWRITE, FILE_MAP_WRITE, NULL),
387 : /* PROT_READ | PROT_WRITE */
388 : M(PAGE_EXECUTE_READWRITE, PAGE_READWRITE, FILE_MAP_WRITE, NULL),
389 :
390 : /* PROT_EXEC */
391 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE,
392 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
393 : /* PROT_READ | PROT_EXEC */
394 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READ,
395 : FILE_MAP_READ | FILE_MAP_EXECUTE, NULL),
396 : /* PROT_WRITE | PROT_EXEC */
397 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE,
398 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
399 : /* PROT_READ | PROT_WRITE | PROT_EXEC */
400 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READWRITE,
401 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
402 :
403 : /* is_private */
404 : /* PROT_NONE */
405 : M(PAGE_EXECUTE_READWRITE, PAGE_NOACCESS, FILE_MAP_READ, NULL),
406 : /* PROT_READ */
407 : M(PAGE_EXECUTE_READWRITE, PAGE_READONLY, FILE_MAP_READ, NULL),
408 : /* PROT_WRITE */
409 : M(PAGE_EXECUTE_READWRITE, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
410 : /* PROT_READ | PROT_WRITE */
411 : M(PAGE_EXECUTE_READWRITE, PAGE_WRITECOPY, FILE_MAP_COPY, NULL),
412 :
413 : /* PROT_EXEC */
414 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE,
415 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
416 : /* PROT_READ | PROT_EXEC */
417 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_READ,
418 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
419 : /* PROT_WRITE | PROT_EXEC */
420 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY,
421 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
422 : /* PROT_READ | PROT_WRITE | PROT_EXEC */
423 : M(PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY,
424 : FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL),
425 : };
426 : #undef M
427 :
428 : size_t ix;
429 :
430 : NaClLog(3,
431 : "NaClflProtectAndDesiredAccessMap(prot 0x%x,"
432 : " priv 0x%x, accmode 0x%x, ...)\n",
433 3 : prot, is_private, accmode);
434 :
435 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_O_RDONLY == 0);
436 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_O_RDWR == 2);
437 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_NONE == 0);
438 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_READ == 1);
439 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_WRITE == 2);
440 3 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_EXEC == 4);
441 :
442 3 : CHECK(accmode != NACL_ABI_O_WRONLY);
443 :
444 : /*
445 : * NACL_ABI_O_RDONLY == 0, NACL_ABI_O_RDWR == 2, so multiplying by 8
446 : * yields a base separation of 8 for the 16 element subtable indexed
447 : * by the NACL_ABI_PROT_{READ|WRITE|EXEC} and is_private values.
448 : */
449 : ix = ((prot & NACL_ABI_PROT_MASK) +
450 : (is_private << 3) +
451 3 : ((accmode & NACL_ABI_O_ACCMODE) << 3));
452 :
453 3 : CHECK(ix < NACL_ARRAY_SIZE(table)); /* compiler should elide this */
454 :
455 3 : if (NULL != out_flMappingProtect) {
456 3 : *out_flMappingProtect = table[ix].flMappingProtect;
457 : }
458 3 : if (NULL != out_flProtect) {
459 3 : *out_flProtect = table[ix].flProtect;
460 : }
461 3 : if (NULL != out_dwDesiredAccess) {
462 3 : *out_dwDesiredAccess = table[ix].dwDesiredAccess;
463 : }
464 3 : if (NULL != out_msg) {
465 3 : *out_msg = table[ix].err;
466 : }
467 :
468 : NaClLog(3, "NaClflProtectAndDesiredAccessMap: %s %s %s\n",
469 : table[ix].flMappingProtect_str,
470 : table[ix].flProtect_str,
471 3 : table[ix].dwDesiredAccess_str);
472 3 : }
473 :
474 : /*
475 : * Returns flProtect flags for VirtualAlloc'd memory, file based
476 : * mappings should always use NaClflProtectAndDesiredAccessMap.
477 : */
478 0 : DWORD NaClflProtectMap(int prot) {
479 : #define M(p) { p, #p }
480 : static struct {
481 : DWORD flProtect;
482 : char const *flProtect_str;
483 : } table[] = {
484 : /* PROT_NONE */
485 : M(PAGE_NOACCESS),
486 : /* PROT_READ */
487 : M(PAGE_READONLY),
488 : /* PROT_WRITE */
489 : M(PAGE_READWRITE),
490 : /* PROT_READ | PROT_WRITE */
491 : M(PAGE_READWRITE),
492 :
493 : /* PROT_EXEC */
494 : M(PAGE_EXECUTE),
495 : /* PROT_READ | PROT_EXEC */
496 : M(PAGE_EXECUTE_READ),
497 : /* PROT_WRITE | PROT_EXEC */
498 : M(PAGE_EXECUTE_READWRITE),
499 : /* PROT_READ | PROT_WRITE | PROT_EXEC */
500 : M(PAGE_EXECUTE_READWRITE),
501 : };
502 : #undef M
503 :
504 : size_t ix;
505 :
506 0 : NaClLog(3, "NaClflProtectMap(prot 0x%x)\n", prot);
507 :
508 0 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_NONE == 0);
509 0 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_READ == 1);
510 0 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_WRITE == 2);
511 0 : NACL_COMPILE_TIME_ASSERT(NACL_ABI_PROT_EXEC == 4);
512 :
513 0 : ix = (prot & NACL_ABI_PROT_MASK);
514 0 : CHECK(ix < NACL_ARRAY_SIZE(table)); /* compiler should elide this */
515 :
516 0 : NaClLog(3, "NaClflProtectMap: %s\n", table[ix].flProtect_str);
517 :
518 0 : return table[ix].flProtect;
519 0 : }
520 :
521 : /*
522 : * Unfortunately, when the descriptor is imported via
523 : * NaClHostDescPosixTake or NaClHostDescPosixDup, the underlying file
524 : * handle may not have GENERIC_EXECUTE permission associated with it,
525 : * unlike the files open using NaClHostDescOpen. This means that the
526 : * CreateFileMapping with flMappingProtect that specifies PAGE_EXECUTE_*
527 : * will fail. Since we don't know whether GENERIC_EXECUTE without doing
528 : * a query, we instead lazily determine the need by detecting the
529 : * CreateFileMapping error and retrying using a fallback
530 : * flMappingProtect that does not have EXECUTE rights. We record this
531 : * in the descriptor so that the next time we do not have to try with
532 : * the PAGE_EXECUTE_* and have it deterministically fail.
533 : *
534 : * This function is also used when mapping in anonymous memory. We
535 : * assume that we never map anonymous executable memory -- mmap of
536 : * executable data is always from a file, and the page will be
537 : * non-writable -- and we ensure that anonymous memory is never
538 : * executable.
539 : */
540 2 : static DWORD NaClflProtectRemoveExecute(DWORD flProtect) {
541 2 : switch (flProtect) {
542 : case PAGE_EXECUTE:
543 0 : return PAGE_NOACCESS;
544 : case PAGE_EXECUTE_READ:
545 2 : return PAGE_READONLY;
546 : case PAGE_EXECUTE_READWRITE:
547 2 : return PAGE_READWRITE;
548 : case PAGE_EXECUTE_WRITECOPY:
549 0 : return PAGE_WRITECOPY;
550 : }
551 0 : return flProtect;
552 2 : }
553 :
554 : /* Check if flProtect has executable permission. */
555 2 : static int NaClflProtectHasExecute(DWORD flProtect) {
556 : return flProtect == PAGE_EXECUTE ||
557 : flProtect == PAGE_EXECUTE_READ ||
558 : flProtect == PAGE_EXECUTE_READWRITE ||
559 2 : flProtect == PAGE_EXECUTE_WRITECOPY;
560 2 : }
561 :
562 : /*
563 : * TODO(mseaborn): Reduce duplication between this function and
564 : * nacl::Map()/NaClMap().
565 : */
566 : uintptr_t NaClHostDescMap(struct NaClHostDesc *d,
567 : struct NaClDescEffector *effp,
568 : void *start_addr,
569 : size_t len,
570 : int prot,
571 : int flags,
572 3 : nacl_off64_t offset) {
573 : uintptr_t retval;
574 : uintptr_t addr;
575 : int desc_flags;
576 : HANDLE hFile;
577 : HANDLE hMap;
578 : int retry_fallback;
579 : DWORD flMappingProtect;
580 : DWORD dwDesiredAccess;
581 : DWORD flProtect;
582 : DWORD flOldProtect;
583 : char const *err_msg;
584 : DWORD dwMaximumSizeHigh;
585 : DWORD dwMaximumSizeLow;
586 : uintptr_t map_result;
587 : size_t chunk_offset;
588 : size_t chunk_size;
589 :
590 3 : if (NULL == d && 0 == (flags & NACL_ABI_MAP_ANONYMOUS)) {
591 0 : NaClLog(LOG_FATAL, "NaClHostDescMap: 'this' is NULL and not anon map\n");
592 : }
593 3 : if (NULL != d && -1 == d->d) {
594 0 : NaClLog(LOG_FATAL, "NaClHostDescMap: already closed\n");
595 : }
596 : if ((0 == (flags & NACL_ABI_MAP_SHARED)) ==
597 3 : (0 == (flags & NACL_ABI_MAP_PRIVATE))) {
598 : NaClLog(LOG_FATAL,
599 : "NaClHostDescMap: exactly one of NACL_ABI_MAP_SHARED"
600 0 : " and NACL_ABI_MAP_PRIVATE must be set.\n");
601 : }
602 3 : addr = (uintptr_t) start_addr;
603 3 : prot &= NACL_ABI_PROT_MASK;
604 :
605 : /*
606 : * Check that if FIXED, start_addr is not NULL.
607 : * Use platform free address space locator to set start_addr if NULL and
608 : * not FIXED.
609 : */
610 3 : if (0 == (flags & NACL_ABI_MAP_FIXED)) {
611 : /*
612 : * Not fixed, addr is a hint... which we ignore. We cannot just
613 : * let windows pick, since we are mapping memory in chunks of
614 : * 64-kB to permit piecewise unmapping.
615 : */
616 2 : if (!NaClFindAddressSpace(&addr, len)) {
617 : NaClLog(LOG_ERROR,
618 0 : "NaClHostDescMap: not fixed, and could not find space\n");
619 0 : return (uintptr_t) -NACL_ABI_ENOMEM;
620 : }
621 :
622 : NaClLog(4,
623 : "NaClHostDescMap: NOT FIXED, found space at %"NACL_PRIxPTR"\n",
624 2 : addr);
625 :
626 2 : start_addr = (void *) addr;
627 : }
628 :
629 3 : flProtect = 0;
630 3 : dwDesiredAccess = 0;
631 :
632 3 : if (0 != (flags & NACL_ABI_MAP_ANONYMOUS)) {
633 : /*
634 : * anonymous memory must be free'able later via VirtualFree
635 : */
636 0 : NaClLog(3, "NaClHostDescMap: anonymous mapping\n");
637 :
638 0 : flProtect = NaClflProtectMap(prot & (~PROT_EXEC));
639 0 : NaClLog(3, "NaClHostDescMap: flProtect 0x%x\n", flProtect);
640 :
641 : for (chunk_offset = 0;
642 : chunk_offset < len;
643 0 : chunk_offset += NACL_MAP_PAGESIZE) {
644 0 : uintptr_t chunk_addr = addr + chunk_offset;
645 :
646 0 : (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
647 :
648 : NaClLog(3,
649 : "NaClHostDescMap: VirtualAlloc(0x%08x,,%x,%x)\n",
650 : (void *) (addr + chunk_offset),
651 : MEM_COMMIT | MEM_RESERVE,
652 0 : flProtect);
653 : map_result = (uintptr_t) VirtualAlloc((void *) chunk_addr,
654 : NACL_MAP_PAGESIZE,
655 : MEM_COMMIT | MEM_RESERVE,
656 0 : flProtect);
657 0 : if (map_result != addr + chunk_offset) {
658 : NaClLog(LOG_FATAL,
659 : ("Could not VirtualAlloc anonymous memory at"
660 : " addr 0x%08x with prot %x\n"),
661 0 : addr + chunk_offset, flProtect);
662 : }
663 0 : }
664 : NaClLog(3, "NaClHostDescMap: (anon) returning 0x%08"NACL_PRIxPTR"\n",
665 0 : start_addr);
666 0 : return (uintptr_t) start_addr;
667 : }
668 :
669 3 : if (NULL == d) {
670 0 : desc_flags = NACL_ABI_O_RDWR;
671 0 : } else {
672 3 : desc_flags = d->flags;
673 : }
674 :
675 : if (0 != (desc_flags & NACL_ABI_O_APPEND) &&
676 3 : 0 != (prot & NACL_ABI_PROT_WRITE)) {
677 0 : return (uintptr_t) -NACL_ABI_EACCES;
678 : }
679 3 : if (NACL_ABI_O_WRONLY == (desc_flags & NACL_ABI_O_ACCMODE)) {
680 1 : return (uintptr_t) -NACL_ABI_EACCES;
681 : }
682 : NaClflProtectAndDesiredAccessMap(prot,
683 : 0 != (flags & NACL_ABI_MAP_PRIVATE),
684 : (desc_flags & NACL_ABI_O_ACCMODE),
685 : &flMappingProtect,
686 : &flProtect,
687 : &dwDesiredAccess,
688 3 : &err_msg);
689 3 : if (0 == flProtect) {
690 2 : NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
691 2 : return (uintptr_t) -NACL_ABI_EACCES;
692 : }
693 : NaClLog(3,
694 : "NaClHostDescMap: flMappingProtect 0x%x,"
695 : " dwDesiredAccess 0x%x, flProtect 0x%x\n",
696 3 : flMappingProtect, dwDesiredAccess, flProtect);
697 :
698 3 : hFile = (HANDLE) _get_osfhandle(d->d);
699 3 : dwMaximumSizeLow = 0;
700 3 : dwMaximumSizeHigh = 0;
701 :
702 : /*
703 : * Ensure consistency of the d->flMappingProtect access.
704 : */
705 3 : NaClFastMutexLock(&d->mu);
706 3 : if (0 != d->flMappingProtect) {
707 1 : flMappingProtect = d->flMappingProtect;
708 1 : retry_fallback = 0;
709 1 : } else {
710 3 : retry_fallback = 1;
711 : }
712 3 : NaClFastMutexUnlock(&d->mu);
713 :
714 : /*
715 : * Finite retry cycle. We can fallback from PAGE_EXECUTE_WRITECOPY to
716 : * PAGE_EXECUTE_READ and from having executable permissions to not having
717 : * them.
718 : */
719 3 : while (1) {
720 : /*
721 : * If hFile is INVALID_HANDLE_VALUE, the memory is backed by the
722 : * system paging file. Why does it returns NULL instead of
723 : * INVALID_HANDLE_VALUE when there is an error?
724 : */
725 : hMap = CreateFileMapping(hFile,
726 : NULL,
727 : flMappingProtect,
728 : dwMaximumSizeHigh,
729 : dwMaximumSizeLow,
730 3 : NULL);
731 3 : if (NULL == hMap && retry_fallback) {
732 : /*
733 : * PAGE_EXECUTE_WRITECOPY is not supported on Windows XP so we fallback
734 : * to PAGE_EXECUTE_READ.
735 : */
736 2 : if (PAGE_EXECUTE_WRITECOPY == flMappingProtect) {
737 : NaClLog(3,
738 : "NaClHostDescMap: CreateFileMapping failed, retrying with"
739 0 : " PAGE_EXECUTE_READ instead of PAGE_EXECUTE_WRITECOPY\n");
740 0 : flMappingProtect = PAGE_EXECUTE_READ;
741 0 : continue;
742 : }
743 : if (0 == (prot & NACL_ABI_PROT_EXEC) &&
744 2 : NaClflProtectHasExecute(flMappingProtect)) {
745 : NaClLog(3,
746 : "NaClHostDescMap: CreateFileMapping failed, retrying without"
747 : " execute permission. Original flMappingProtect 0x%x\n",
748 2 : flMappingProtect);
749 : NaClflProtectAndDesiredAccessMap(prot & (~PROT_EXEC),
750 : 0 != (flags & NACL_ABI_MAP_PRIVATE),
751 : (desc_flags & NACL_ABI_O_ACCMODE),
752 : &flMappingProtect,
753 : &flProtect,
754 : &dwDesiredAccess,
755 2 : &err_msg);
756 2 : if (0 == flProtect) {
757 0 : NaClLog(3, "NaClHostDescMap: %s\n", err_msg);
758 0 : return (uintptr_t) -NACL_ABI_EACCES;
759 : }
760 2 : flMappingProtect = NaClflProtectRemoveExecute(flMappingProtect);
761 : NaClLog(3,
762 : "NaClHostDescMap: fallback flMappingProtect 0x%x,"
763 : " dwDesiredAccess 0x%x, flProtect 0x%x\n",
764 2 : flMappingProtect, dwDesiredAccess, flProtect);
765 2 : continue;
766 : }
767 : NaClLog(3,
768 : "NaClHostDescMap: not retrying, since caller explicitly asked"
769 1 : " for NACL_ABI_PROT_EXEC\n");
770 1 : break;
771 : }
772 : /*
773 : * Remember successful flProtect used. Note that this just
774 : * ensures reads of d->flMappingProtect gets a consistent value;
775 : * we have a potential race where two threads perform mmap and in
776 : * parallel determine the replacement flProtect value. This is
777 : * okay, since those two threads should arrive at the same
778 : * replacement value. This could be replaced with an atomic
779 : * word.
780 : */
781 3 : NaClFastMutexLock(&d->mu);
782 3 : d->flMappingProtect = flMappingProtect;
783 3 : NaClFastMutexUnlock(&d->mu);
784 3 : break;
785 : }
786 :
787 3 : if (NULL == hMap) {
788 1 : DWORD err = GetLastError();
789 : NaClLog(LOG_INFO,
790 : "NaClHostDescMap: CreateFileMapping failed: %d\n",
791 1 : err);
792 1 : return -NaClXlateSystemError(err);
793 : }
794 3 : NaClLog(3, "NaClHostDescMap: CreateFileMapping got handle %d\n", (int) hMap);
795 3 : NaClLog(3, "NaClHostDescMap: dwDesiredAccess 0x%x\n", dwDesiredAccess);
796 :
797 3 : retval = (uintptr_t) -NACL_ABI_EINVAL;
798 :
799 : for (chunk_offset = 0;
800 : chunk_offset < len;
801 3 : chunk_offset += NACL_MAP_PAGESIZE) {
802 3 : uintptr_t chunk_addr = addr + chunk_offset;
803 : nacl_off64_t net_offset;
804 : uint32_t net_offset_high;
805 : uint32_t net_offset_low;
806 :
807 3 : (*effp->vtbl->UnmapMemory)(effp, chunk_addr, NACL_MAP_PAGESIZE);
808 :
809 3 : chunk_size = size_min(len - chunk_offset, NACL_MAP_PAGESIZE);
810 : /* in case MapViewOfFile cares that we exceed the file size */
811 3 : net_offset = offset + chunk_offset;
812 3 : net_offset_high = (uint32_t) (net_offset >> 32);
813 3 : net_offset_low = (uint32_t) net_offset;
814 : NaClLog(4,
815 : "NaClHostDescMap: MapViewOfFileEx(hMap=%d, dwDesiredAccess=0x%x,"
816 : " net_offset_high = 0x%08x, net_offset_low = 0x%08x,"
817 : " chunk_size = 0x%"NACL_PRIxS", chunk_addr = 0x%"NACL_PRIxPTR"\n",
818 : hMap, dwDesiredAccess, net_offset_high, net_offset_low,
819 3 : chunk_size, chunk_addr);
820 : map_result = (uintptr_t) MapViewOfFileEx(hMap,
821 : dwDesiredAccess,
822 : net_offset_high,
823 : net_offset_low,
824 : chunk_size,
825 3 : (void *) chunk_addr);
826 : NaClLog(3,
827 : "NaClHostDescMap: map_result %"NACL_PRIxPTR
828 : ", chunk_addr %"NACL_PRIxPTR
829 : ", addr + chunk_offset %"NACL_PRIxPTR"\n",
830 3 : map_result, chunk_addr, (addr + chunk_offset));
831 3 : if ((addr + chunk_offset) != map_result) {
832 : /*
833 : * MapViewOfFileEx() failed. If we are mapping into untrusted
834 : * address space, we opened an mmap hole. We didn't expect the
835 : * failure, and it's difficult to restore the old mappings that we
836 : * removed, so for safety we must abort with LOG_FATAL.
837 : *
838 : * Otherwise, if this is a trusted mapping, we can return an error
839 : * gracefully. NaClElfFileMapSegment() currently triggers errors
840 : * here by mapping beyond the file's extent: see
841 : * https://crbug.com/406632.
842 : */
843 : int log_type =
844 0 : effp == NaClDescEffectorTrustedMem() ? LOG_ERROR : LOG_FATAL;
845 0 : DWORD err = GetLastError();
846 : size_t unmap_offset;
847 : NaClLog(log_type,
848 : "MapViewOfFileEx failed at 0x%08"NACL_PRIxPTR
849 : ", got 0x%08"NACL_PRIxPTR", err %x\n",
850 : addr + chunk_offset,
851 : map_result,
852 0 : err);
853 : for (unmap_offset = 0;
854 : unmap_offset < chunk_offset;
855 0 : unmap_offset += NACL_MAP_PAGESIZE) {
856 0 : (void) UnmapViewOfFile((void *) (addr + unmap_offset));
857 0 : }
858 0 : retval = (uintptr_t) -NaClXlateSystemError(err);
859 0 : goto cleanup;
860 : }
861 : if (!VirtualProtect((void *) map_result,
862 : NaClRoundPage(chunk_size),
863 : flProtect,
864 3 : &flOldProtect)) {
865 0 : DWORD err = GetLastError();
866 : NaClLog(LOG_INFO,
867 : "VirtualProtect failed at 0x%08x, err %x\n",
868 0 : addr, err);
869 0 : retval = (uintptr_t) -NaClXlateSystemError(err);
870 0 : goto cleanup;
871 : }
872 3 : }
873 3 : retval = (uintptr_t) start_addr;
874 : cleanup:
875 3 : (void) CloseHandle(hMap);
876 3 : NaClLog(3, "NaClHostDescMap: returning %"NACL_PRIxPTR"\n", retval);
877 3 : return retval;
878 3 : }
879 :
880 : int NaClHostDescUnmapUnsafe(void *start_addr,
881 3 : size_t len) {
882 : uintptr_t addr;
883 : size_t off;
884 :
885 3 : addr = (uintptr_t) start_addr;
886 :
887 3 : for (off = 0; off < len; off += NACL_MAP_PAGESIZE) {
888 3 : if (!UnmapViewOfFile((void *) (addr + off))) {
889 : NaClLog(LOG_ERROR,
890 : "NaClHostDescUnmap: UnmapViewOfFile(0x%08x) failed\n",
891 0 : addr + off);
892 0 : return -NACL_ABI_EINVAL;
893 : }
894 3 : }
895 3 : return 0;
896 3 : }
897 :
898 : static void NaClHostDescCtorIntern(struct NaClHostDesc *hd,
899 : int posix_d,
900 6 : int flags) {
901 : nacl_host_stat_t stbuf;
902 :
903 6 : hd->d = posix_d;
904 6 : hd->flags = flags;
905 6 : hd->flMappingProtect = 0;
906 6 : if (_fstat64(posix_d, &stbuf) != 0) {
907 : /* inherited non-fstat'able are IPC channels, e.g., for bootstrap channel */
908 : NaClLog(4,
909 : "NaClHostDescCtorIntern: could not _fstat64,"
910 0 : " assuming non-seekable\n");
911 0 : hd->protect_filepos = 0;
912 0 : } else {
913 6 : int file_type = stbuf.st_mode & S_IFMT;
914 : /*
915 : * Inherited stdio are console handles and are not seekable.
916 : *
917 : * Posix descriptors (wrapping Windows HANDLES) opened for
918 : * O_WRONLY | O_APPEND cannot have byte range locks applied, which
919 : * is how the protect_filepos mechanism is implemented. Luckily,
920 : * this is only needed for O_RDWR | O_APPEND or non-append
921 : * descriptor.
922 : */
923 : hd->protect_filepos = (((S_IFREG == file_type) ||
924 : (S_IFDIR == file_type)) &&
925 : !((flags & NACL_ABI_O_APPEND) != 0 &&
926 : (flags & NACL_ABI_O_ACCMODE) ==
927 6 : NACL_ABI_O_WRONLY));
928 : }
929 6 : if (!NaClFastMutexCtor(&hd->mu)) {
930 0 : NaClLog(LOG_FATAL, "NaClHostDescCtorIntern: NaClFastMutexCtor failed\n");
931 : }
932 6 : }
933 :
934 : int NaClHostDescOpen(struct NaClHostDesc *d,
935 : char const *path,
936 : int flags,
937 4 : int perms) {
938 : DWORD dwDesiredAccess;
939 : DWORD dwCreationDisposition;
940 : DWORD dwFlagsAndAttributes;
941 : int oflags;
942 4 : int truncate_after_open = 0;
943 : HANDLE hFile;
944 : DWORD err;
945 : int fd;
946 :
947 4 : if (NULL == d) {
948 0 : NaClLog(LOG_FATAL, "NaClHostDescOpen: 'this' is NULL\n");
949 : }
950 :
951 : /*
952 : * Sanitize access flags.
953 : */
954 4 : if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
955 0 : return -NACL_ABI_EINVAL;
956 : }
957 :
958 4 : switch (flags & NACL_ABI_O_ACCMODE) {
959 : case NACL_ABI_O_RDONLY:
960 2 : dwDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
961 2 : oflags = _O_RDONLY | _O_BINARY;
962 2 : break;
963 : case NACL_ABI_O_RDWR:
964 3 : oflags = _O_RDWR | _O_BINARY;
965 3 : dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
966 3 : break;
967 : case NACL_ABI_O_WRONLY: /* Enforced in the Read call */
968 2 : oflags = _O_WRONLY | _O_BINARY;
969 2 : dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
970 2 : break;
971 : default:
972 : NaClLog(LOG_ERROR,
973 : "NaClHostDescOpen: bad access flags 0x%x.\n",
974 0 : flags);
975 0 : return -NACL_ABI_EINVAL;
976 : }
977 : /*
978 : * Possibly make the file read-only. The file attribute only
979 : * applies if the file is created; if it pre-exists, the attributes
980 : * from the file is combined with the FILE_FLAG_* values.
981 : */
982 4 : if (0 == (perms & NACL_ABI_S_IWUSR)) {
983 : dwFlagsAndAttributes = (FILE_ATTRIBUTE_READONLY |
984 2 : FILE_FLAG_POSIX_SEMANTICS);
985 2 : } else {
986 : dwFlagsAndAttributes = (FILE_ATTRIBUTE_NORMAL |
987 3 : FILE_FLAG_POSIX_SEMANTICS);
988 : }
989 : /*
990 : * Postcondition: flags & NACL_ABI_O_ACCMODE is one of the three
991 : * allowed values.
992 : */
993 4 : switch (flags & (NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC)) {
994 : case 0:
995 2 : dwCreationDisposition = OPEN_EXISTING;
996 2 : break;
997 : case NACL_ABI_O_CREAT:
998 2 : dwCreationDisposition = OPEN_ALWAYS;
999 2 : break;
1000 : case NACL_ABI_O_TRUNC:
1001 0 : dwCreationDisposition = TRUNCATE_EXISTING;
1002 0 : truncate_after_open = 1;
1003 0 : break;
1004 : case NACL_ABI_O_CREAT | NACL_ABI_O_TRUNC:
1005 2 : dwCreationDisposition = OPEN_ALWAYS;
1006 2 : truncate_after_open = 1;
1007 : }
1008 4 : if (0 != (flags & NACL_ABI_O_APPEND)) {
1009 1 : oflags |= _O_APPEND;
1010 : }
1011 :
1012 : NaClLog(1,
1013 : "NaClHostDescOpen: CreateFileA(path=%s, desired_access=0x%x,"
1014 : " share_mode=ALL, security_attributes=NULL, creation_disposition=%d,"
1015 : " flags_and_attributes=%d, template_file=NULL)\n",
1016 4 : path, dwDesiredAccess, dwCreationDisposition, dwFlagsAndAttributes);
1017 :
1018 : hFile = CreateFileA(path, dwDesiredAccess,
1019 : (FILE_SHARE_DELETE |
1020 : FILE_SHARE_READ |
1021 : FILE_SHARE_WRITE),
1022 : NULL,
1023 : dwCreationDisposition,
1024 : dwFlagsAndAttributes,
1025 4 : NULL);
1026 4 : if (INVALID_HANDLE_VALUE == hFile) {
1027 0 : err = GetLastError();
1028 0 : NaClLog(3, "NaClHostDescOpen: CreateFile failed %d\n", err);
1029 0 : return -NaClXlateSystemError(err);
1030 : }
1031 : if (truncate_after_open &&
1032 4 : NACL_ABI_O_RDONLY != (flags & NACL_ABI_O_ACCMODE)) {
1033 2 : NaClLog(4, "NaClHostDescOpen: Truncating file\n");
1034 2 : if (!SetEndOfFile(hFile)) {
1035 0 : err = GetLastError();
1036 : NaClLog(LOG_ERROR,
1037 : "NaClHostDescOpen: could not truncate file:"
1038 : " last error %d.\n",
1039 0 : err);
1040 0 : if (err == ERROR_USER_MAPPED_FILE) {
1041 : NaClLog(LOG_ERROR,
1042 : "NaClHostDescOpen: this is due to an existing mapping"
1043 0 : " of the same file.\n");
1044 : }
1045 : }
1046 : }
1047 4 : fd = _open_osfhandle((intptr_t) hFile, oflags);
1048 : /*
1049 : * oflags _O_APPEND, _O_RDONLY, and _O_TEXT are meaningful; unclear
1050 : * whether _O_RDWR, _O_WRONLY, etc has any effect.
1051 : */
1052 4 : if (-1 == fd) {
1053 : NaClLog(LOG_FATAL, "NaClHostDescOpen failed: err %d\n",
1054 0 : GetLastError());
1055 : }
1056 4 : NaClHostDescCtorIntern(d, fd, flags);
1057 4 : return 0;
1058 4 : }
1059 :
1060 : int NaClHostDescPosixDup(struct NaClHostDesc *d,
1061 : int posix_d,
1062 0 : int flags) {
1063 : int host_desc;
1064 :
1065 : NaClLog(3, "NaClHostDescPosixDup(0x%08x, %d, 0%o)\n",
1066 0 : (uintptr_t) d, posix_d, flags);
1067 0 : if (NULL == d) {
1068 0 : NaClLog(LOG_FATAL, "NaClHostDescPosixDup: 'this' is NULL\n");
1069 : }
1070 : /*
1071 : * Sanitize access flags.
1072 : */
1073 0 : if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1074 0 : return -NACL_ABI_EINVAL;
1075 : }
1076 0 : switch (flags & NACL_ABI_O_ACCMODE) {
1077 : case NACL_ABI_O_RDONLY:
1078 : case NACL_ABI_O_WRONLY:
1079 : case NACL_ABI_O_RDWR:
1080 0 : break;
1081 : default:
1082 : NaClLog(LOG_ERROR,
1083 : "NaClHostDescOpen: bad access flags 0x%x.\n",
1084 0 : flags);
1085 0 : return -NACL_ABI_EINVAL;
1086 : }
1087 :
1088 0 : host_desc = _dup(posix_d);
1089 0 : if (-1 == host_desc) {
1090 0 : return -GetErrno();
1091 : }
1092 0 : NaClHostDescCtorIntern(d, host_desc, flags);
1093 0 : return 0;
1094 0 : }
1095 :
1096 : int NaClHostDescPosixTake(struct NaClHostDesc *d,
1097 : int posix_d,
1098 3 : int flags) {
1099 3 : if (NULL == d) {
1100 0 : NaClLog(LOG_FATAL, "NaClHostDescPosixTake: 'this' is NULL\n");
1101 : }
1102 : /*
1103 : * Sanitize access flags.
1104 : */
1105 3 : if (0 != (flags & ~NACL_ALLOWED_OPEN_FLAGS)) {
1106 0 : return -NACL_ABI_EINVAL;
1107 : }
1108 3 : switch (flags & NACL_ABI_O_ACCMODE) {
1109 : case NACL_ABI_O_RDONLY:
1110 : case NACL_ABI_O_WRONLY:
1111 : case NACL_ABI_O_RDWR:
1112 3 : break;
1113 : default:
1114 : NaClLog(LOG_ERROR,
1115 : "NaClHostDescOpen: bad access flags 0x%x.\n",
1116 0 : flags);
1117 0 : return -NACL_ABI_EINVAL;
1118 : }
1119 3 : NaClHostDescCtorIntern(d, posix_d, flags);
1120 3 : return 0;
1121 3 : }
1122 :
1123 : ssize_t NaClHostDescRead(struct NaClHostDesc *d,
1124 : void *buf,
1125 2 : size_t len) {
1126 : /* Windows ReadFile only supports DWORD, so we need
1127 : * to clamp the length. */
1128 : unsigned int actual_len;
1129 : HANDLE fh;
1130 : DWORD bytes_received;
1131 : DWORD err;
1132 :
1133 2 : if (len < UINT_MAX) {
1134 2 : actual_len = (unsigned int) len;
1135 2 : } else {
1136 0 : actual_len = UINT_MAX;
1137 : }
1138 :
1139 2 : NaClHostDescCheckValidity("NaClHostDescRead", d);
1140 2 : if (NACL_ABI_O_WRONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1141 1 : NaClLog(3, "NaClHostDescRead: WRONLY file\n");
1142 1 : return -NACL_ABI_EBADF;
1143 : }
1144 : /*
1145 : * We drop into using Windows ReadFile rather than using _read from
1146 : * the POSIX compatibility layer here. The reason for this is
1147 : * because the pread/pwrite implementation uses ReadFile/WriteFile,
1148 : * it would be more consistent with the pread/pwrite implementation
1149 : * to just also use ReadFile/WriteFile directly here as well.
1150 : *
1151 : * NB: contrary to the documentation available on MSDN, operations
1152 : * on synchronous files with non-NULL LPOVERLAPPED arguments result
1153 : * in the *implicit* file position getting updated before the
1154 : * ReadFile/WriteFile returning, rather than the Offset/OffsetHigh
1155 : * members of the explicit OVERLAPPED structure (!). In order to
1156 : * support mixed read/pread syscall sequences (and similarly mixed
1157 : * write/pwrite sequences) we must effectively lock the file
1158 : * position from access by other threads and then read/write, so
1159 : * that when pread/pwrite mess up the implicit file position
1160 : * temporarily, it would not be visible.
1161 : */
1162 2 : fh = (HANDLE) _get_osfhandle(d->d);
1163 2 : CHECK(INVALID_HANDLE_VALUE != fh);
1164 :
1165 : /*
1166 : * Ensure that we do not corrupt shared implicit file position.
1167 : */
1168 2 : if (d->protect_filepos) {
1169 2 : NaClTakeFilePosLock(fh);
1170 : }
1171 2 : if (!ReadFile(fh, buf, actual_len, &bytes_received, NULL)) {
1172 0 : err = GetLastError();
1173 0 : if (ERROR_HANDLE_EOF == err) {
1174 0 : bytes_received = 0;
1175 0 : } else {
1176 0 : NaClLog(4, "NaClHostDescRead: ReadFile error %d\n", err);
1177 0 : bytes_received = -NaClXlateSystemError(err);
1178 : }
1179 : }
1180 2 : if (d->protect_filepos) {
1181 2 : NaClDropFilePosLock(fh);
1182 : }
1183 :
1184 2 : return bytes_received;
1185 2 : }
1186 :
1187 : ssize_t NaClHostDescWrite(struct NaClHostDesc *d,
1188 : void const *buf,
1189 4 : size_t len) {
1190 : /*
1191 : * Windows WriteFile only supports DWORD uint, so we need to clamp
1192 : * the length.
1193 : */
1194 : unsigned int actual_len;
1195 : HANDLE fh;
1196 : DWORD bytes_written;
1197 : DWORD err;
1198 : OVERLAPPED overlap;
1199 4 : OVERLAPPED *overlap_ptr = NULL;
1200 :
1201 4 : if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1202 1 : NaClLog(3, "NaClHostDescWrite: RDONLY file\n");
1203 1 : return -NACL_ABI_EBADF;
1204 : }
1205 4 : if (len < UINT_MAX) {
1206 4 : actual_len = (unsigned int) len;
1207 4 : } else {
1208 0 : actual_len = UINT_MAX;
1209 : }
1210 :
1211 4 : NaClHostDescCheckValidity("NaClHostDescWrite", d);
1212 : /*
1213 : * See discussion in NaClHostDescRead above wrt why we use WriteFile
1214 : * instead of _write below.
1215 : */
1216 4 : if (0 != (NACL_ABI_O_APPEND & d->flags)) {
1217 0 : nacl_off64_t offset = GG_LONGLONG(0xffffffffffffffff);
1218 0 : memset(&overlap, 0, sizeof overlap);
1219 0 : overlap.Offset = (DWORD) offset;
1220 0 : overlap.OffsetHigh = (DWORD) (offset >> 32);
1221 0 : overlap_ptr = &overlap;
1222 : }
1223 4 : fh = (HANDLE) _get_osfhandle(d->d);
1224 4 : CHECK(INVALID_HANDLE_VALUE != fh);
1225 : /*
1226 : * Ensure that we do not corrupt shared implicit file position.
1227 : */
1228 4 : if (d->protect_filepos) {
1229 4 : NaClTakeFilePosLock(fh);
1230 : }
1231 4 : if (!WriteFile(fh, buf, actual_len, &bytes_written, overlap_ptr)) {
1232 0 : err = GetLastError();
1233 0 : NaClLog(4, "NaClHostDescWrite: WriteFile error %d\n", err);
1234 :
1235 0 : bytes_written = -NaClXlateSystemError(err);
1236 : }
1237 4 : if (d->protect_filepos) {
1238 4 : NaClDropFilePosLock(fh);
1239 : }
1240 :
1241 4 : return bytes_written;
1242 4 : }
1243 :
1244 : nacl_off64_t NaClHostDescSeek(struct NaClHostDesc *d,
1245 : nacl_off64_t offset,
1246 4 : int whence) {
1247 : HANDLE hFile;
1248 : nacl_off64_t retval;
1249 :
1250 4 : NaClHostDescCheckValidity("NaClHostDescSeek", d);
1251 4 : hFile = (HANDLE) _get_osfhandle(d->d);
1252 4 : CHECK(INVALID_HANDLE_VALUE != hFile);
1253 4 : if (d->protect_filepos) {
1254 4 : NaClTakeFilePosLock(hFile);
1255 : }
1256 4 : retval = _lseeki64(d->d, offset, whence);
1257 4 : if (d->protect_filepos) {
1258 4 : NaClDropFilePosLock(hFile);
1259 : }
1260 4 : return (-1 == retval) ? -errno : retval;
1261 4 : }
1262 :
1263 : ssize_t NaClHostDescPRead(struct NaClHostDesc *d,
1264 : void *buf,
1265 : size_t len,
1266 1 : nacl_off64_t offset) {
1267 : HANDLE fh;
1268 : OVERLAPPED overlap;
1269 : DWORD bytes_received;
1270 : DWORD err;
1271 1 : nacl_off64_t orig_pos = 0;
1272 :
1273 1 : NaClHostDescCheckValidity("NaClHostDescPRead", d);
1274 1 : if (NACL_ABI_O_WRONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1275 1 : NaClLog(3, "NaClHostDescPRead: WRONLY file\n");
1276 1 : return -NACL_ABI_EBADF;
1277 : }
1278 1 : if (offset < 0) {
1279 1 : return -NACL_ABI_EINVAL;
1280 : }
1281 : /*
1282 : * There are reports of driver issues that may require clamping len
1283 : * to a megabyte or so, lest ReadFile returns an error with
1284 : * GetLastError() returning ERROR_INVALID_PARAMETER, but since we
1285 : * do not expect to ever read from / write to anything other than
1286 : * filesystem files, we do not clamp.
1287 : */
1288 1 : fh = (HANDLE) _get_osfhandle(d->d);
1289 1 : CHECK(INVALID_HANDLE_VALUE != fh);
1290 1 : memset(&overlap, 0, sizeof overlap);
1291 1 : overlap.Offset = (DWORD) offset;
1292 1 : overlap.OffsetHigh = (DWORD) (offset >> 32);
1293 1 : if (len > UINT_MAX) {
1294 0 : len = UINT_MAX;
1295 : }
1296 1 : if (d->protect_filepos) {
1297 1 : orig_pos = NaClLockAndGetCurrentFilePos(fh);
1298 : }
1299 1 : if (!ReadFile(fh, buf, (DWORD) len, &bytes_received, &overlap)) {
1300 0 : err = GetLastError();
1301 0 : if (ERROR_HANDLE_EOF == err) {
1302 0 : bytes_received = 0;
1303 : /* handle as if returned true. */
1304 0 : } else {
1305 0 : NaClLog(4, "NaClHostDescPRead: ReadFile failed, error %d\n", err);
1306 0 : NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1307 0 : bytes_received = -NaClXlateSystemError(err);
1308 : }
1309 : }
1310 1 : if (d->protect_filepos) {
1311 1 : NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1312 : }
1313 1 : return bytes_received;
1314 1 : }
1315 :
1316 : ssize_t NaClHostDescPWrite(struct NaClHostDesc *d,
1317 : void const *buf,
1318 : size_t len,
1319 1 : nacl_off64_t offset) {
1320 : HANDLE fh;
1321 : OVERLAPPED overlap;
1322 : DWORD bytes_sent;
1323 : DWORD err;
1324 1 : nacl_off64_t orig_pos = 0;
1325 :
1326 1 : NaClHostDescCheckValidity("NaClHostDescPWrite", d);
1327 1 : if (NACL_ABI_O_RDONLY == (d->flags & NACL_ABI_O_ACCMODE)) {
1328 1 : NaClLog(3, "NaClHostDescPWrite: RDONLY file\n");
1329 1 : return -NACL_ABI_EBADF;
1330 : }
1331 1 : if (offset < 0) {
1332 : /*
1333 : * This also avoids the case where having 0xffffffff in both
1334 : * overlap.Offset and overlap.OffsetHigh means append to the file.
1335 : * In Posix, offset does not permit special meanings being encoded
1336 : * like this.
1337 : */
1338 0 : return -NACL_ABI_EINVAL;
1339 : }
1340 1 : fh = (HANDLE) _get_osfhandle(d->d);
1341 1 : CHECK(INVALID_HANDLE_VALUE != fh);
1342 1 : memset(&overlap, 0, sizeof overlap);
1343 1 : overlap.Offset = (DWORD) offset;
1344 1 : overlap.OffsetHigh = (DWORD) (offset >> 32);
1345 1 : if (len > UINT_MAX) {
1346 0 : len = UINT_MAX;
1347 : }
1348 1 : if (d->protect_filepos) {
1349 1 : orig_pos = NaClLockAndGetCurrentFilePos(fh);
1350 : }
1351 1 : if (!WriteFile(fh, buf, (DWORD) len, &bytes_sent, &overlap)) {
1352 0 : err = GetLastError();
1353 0 : if (ERROR_HANDLE_EOF == err) {
1354 0 : bytes_sent = 0;
1355 : /* handle as if returned true. */
1356 0 : } else {
1357 : NaClLog(4,
1358 0 : "NaClHostDescPWrite: WriteFile failed, error %d\n", err);
1359 0 : bytes_sent = -NaClXlateSystemError(err);
1360 : }
1361 : }
1362 1 : if (d->protect_filepos) {
1363 1 : NaClSetCurrentFilePosAndUnlock(fh, orig_pos);
1364 : }
1365 1 : return bytes_sent;
1366 1 : }
1367 :
1368 : int NaClHostDescFstat(struct NaClHostDesc *d,
1369 1 : nacl_host_stat_t *nasp) {
1370 1 : NaClHostDescCheckValidity("NaClHostDescFstat", d);
1371 1 : if (NACL_HOST_FSTAT64(d->d, nasp) == -1) {
1372 0 : return -GetErrno();
1373 : }
1374 :
1375 1 : return 0;
1376 1 : }
1377 :
1378 0 : int NaClHostDescIsatty(struct NaClHostDesc *d) {
1379 : int retval;
1380 :
1381 0 : NaClHostDescCheckValidity("NaClHostDescIsatty", d);
1382 0 : retval = _isatty(d->d);
1383 : /* When windows _isatty fails it returns zero, but does not set errno. */
1384 0 : return (0 == retval) ? -NACL_ABI_ENOTTY : 1;
1385 0 : }
1386 :
1387 6 : int NaClHostDescClose(struct NaClHostDesc *d) {
1388 : int retval;
1389 :
1390 6 : NaClHostDescCheckValidity("NaClHostDescClose", d);
1391 6 : if (-1 != d->d) {
1392 6 : retval = _close(d->d);
1393 6 : if (-1 == retval) {
1394 0 : return -GetErrno();
1395 : }
1396 6 : d->d = -1;
1397 : }
1398 6 : NaClFastMutexDtor(&d->mu);
1399 6 : return 0;
1400 6 : }
1401 :
1402 : /*
1403 : * This is not a host descriptor function, but is closely related to
1404 : * fstat and should behave similarly.
1405 : */
1406 1 : int NaClHostDescStat(char const *path, nacl_host_stat_t *nhsp) {
1407 1 : if (NACL_HOST_STAT64(path, nhsp) == -1) {
1408 0 : return -GetErrno();
1409 : }
1410 :
1411 1 : return 0;
1412 1 : }
1413 :
1414 0 : int NaClHostDescMkdir(const char *path, int mode) {
1415 : UNREFERENCED_PARAMETER(mode);
1416 0 : if (_mkdir(path) != 0)
1417 0 : return -NaClXlateErrno(errno);
1418 0 : return 0;
1419 0 : }
1420 :
1421 0 : int NaClHostDescRmdir(const char *path) {
1422 0 : if (_rmdir(path) != 0)
1423 0 : return -NaClXlateErrno(errno);
1424 0 : return 0;
1425 0 : }
1426 :
1427 0 : int NaClHostDescChdir(const char *path) {
1428 0 : if (_chdir(path) != 0)
1429 0 : return -NaClXlateErrno(errno);
1430 0 : return 0;
1431 0 : }
1432 :
1433 0 : int NaClHostDescGetcwd(char *path, size_t len) {
1434 0 : if (_getcwd(path, (int) len) == NULL)
1435 0 : return -NaClXlateErrno(errno);
1436 0 : return 0;
1437 0 : }
1438 :
1439 0 : int NaClHostDescUnlink(const char *path) {
1440 : /*
1441 : * If the file exists and is not writable we make it writeable
1442 : * before calling _unlink() to match the POSIX semantics where
1443 : * unlink(2) can remove readonly files.
1444 : */
1445 0 : if (_access(path, WIN_F_OK) == 0 && _access(path, WIN_W_OK) != 0) {
1446 0 : if (_chmod(path, _S_IREAD | S_IWRITE) != 0) {
1447 : /* If _chmod fails just log it and contine on to call _unlink anyway */
1448 0 : NaClLog(3, "NaClHostDescUnlink: _chmod failed: %d\n", errno);
1449 : }
1450 : }
1451 :
1452 0 : if (_unlink(path) != 0)
1453 0 : return -NaClXlateErrno(errno);
1454 :
1455 0 : return 0;
1456 0 : }
1457 :
1458 0 : int NaClHostDescTruncate(char const *path, nacl_abi_off_t length) {
1459 : LARGE_INTEGER win_length;
1460 : DWORD err;
1461 :
1462 : HANDLE hfile = CreateFileA(path,
1463 : GENERIC_READ | GENERIC_WRITE,
1464 : FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1465 : NULL,
1466 : OPEN_EXISTING,
1467 : FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS,
1468 0 : NULL);
1469 :
1470 0 : if (INVALID_HANDLE_VALUE == hfile) {
1471 0 : err = GetLastError();
1472 0 : NaClLog(3, "NaClHostDescTruncate: CreateFile failed %d\n", err);
1473 0 : return -NaClXlateSystemError(err);
1474 : }
1475 :
1476 0 : win_length.QuadPart = length;
1477 0 : if (!SetFilePointerEx(hfile, win_length, NULL, FILE_BEGIN)) {
1478 0 : err = GetLastError();
1479 : NaClLog(LOG_ERROR,
1480 : "NaClHostDescTruncate: SetFilePointerEx failed:"
1481 0 : " last error %d.\n", err);
1482 0 : return -NaClXlateSystemError(err);
1483 : }
1484 :
1485 0 : if (!SetEndOfFile(hfile)) {
1486 0 : err = GetLastError();
1487 : NaClLog(LOG_ERROR,
1488 : "NaClHostDescTruncate: could not truncate file:"
1489 0 : " last error %d.\n", err);
1490 0 : if (err == ERROR_USER_MAPPED_FILE) {
1491 : NaClLog(LOG_ERROR,
1492 : "NaClHostDescTruncate: this is due to an existing"
1493 0 : " mapping of the same file.\n");
1494 : }
1495 0 : return -NaClXlateSystemError(err);
1496 : }
1497 :
1498 0 : return 0;
1499 0 : }
1500 :
1501 0 : int NaClHostDescLstat(char const *path, nacl_host_stat_t *nhsp) {
1502 : /*
1503 : * Since symlinks don't exist on windows, stat() and lstat()
1504 : * are equivalent.
1505 : */
1506 0 : return NaClHostDescStat(path, nhsp);
1507 0 : }
1508 :
1509 0 : int NaClHostDescLink(const char *oldpath, const char *newpath) {
1510 : /*
1511 : * Hard linking not implemented for win32
1512 : */
1513 0 : NaClLog(1, "NaClHostDescLink: hard linking not supported on windows.\n");
1514 0 : return -NACL_ABI_ENOSYS;
1515 0 : }
1516 :
1517 0 : int NaClHostDescRename(const char *oldpath, const char *newpath) {
1518 0 : if (rename(oldpath, newpath) != 0)
1519 0 : return -NaClXlateErrno(errno);
1520 0 : return 0;
1521 0 : }
1522 :
1523 0 : int NaClHostDescSymlink(const char *oldpath, const char *newpath) {
1524 : /*
1525 : * Symlinks are not supported on win32.
1526 : */
1527 0 : NaClLog(1, "NaClHostDescSymlink: symbolic links not supported on windows.\n");
1528 0 : return -NACL_ABI_ENOSYS;
1529 0 : }
1530 :
1531 0 : int NaClHostDescChmod(const char *path, nacl_abi_mode_t mode) {
1532 0 : if (_chmod(path, NaClMapMode(mode)) != 0)
1533 0 : return -NaClXlateErrno(errno);
1534 0 : return 0;
1535 0 : }
1536 :
1537 0 : int NaClHostDescAccess(const char *path, int amode) {
1538 0 : if (_access(path, NaClMapAccessMode(amode)) != 0)
1539 0 : return -NaClXlateErrno(errno);
1540 0 : return 0;
1541 0 : }
1542 :
1543 0 : int NaClHostDescReadlink(const char *path, char *buf, size_t bufsize) {
1544 : /*
1545 : * readlink(2) sets errno to EINVAL when the file in question is
1546 : * not a symlink. Since win32 does not support symlinks we simply
1547 : * return EINVAL in all cases here.
1548 : */
1549 : NaClLog(1,
1550 0 : "NaClHostDescReadlink: symbolic links not supported on Windows.\n");
1551 0 : return -NACL_ABI_EINVAL;
1552 0 : }
|