Fix file permissions: set executable bit
[nativeclient.git] / service_runtime / win / sel_memory.c
blobbeca63645262cc6bdc7c6931a78c086e48a79a4f
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * NaCl Service Runtime memory allocation code
35 #include "native_client/include/portability.h"
37 #include <errno.h>
38 #include <windows.h>
39 #include <string.h>
41 #include "native_client/service_runtime/win/mman.h"
43 #include "native_client/service_runtime/nacl_config.h"
44 #include "native_client/service_runtime/nacl_log.h"
45 #include "native_client/service_runtime/sel_memory.h"
46 #include "native_client/service_runtime/sel_util.h"
47 #include "native_client/service_runtime/win/xlate_system_error.h"
50 * NaCl_page_free: free pages allocated with NaCl_page_alloc.
51 * Must start at allocation granularity (NACL_MAP_PAGESIZE) and
52 * number of bytes must be a multiple of allocation granularity.
54 void NaCl_page_free(void *p,
55 size_t num_bytes)
57 void *end_addr;
59 end_addr = (void *) (((char *) p) + num_bytes);
60 while (p < end_addr) {
61 if (!VirtualFree(p, 0, MEM_RELEASE)) {
62 NaClLog(0,
63 "NaCl_page_free: VirtualFree(0x%08x, 0, MEM_RELEASE) failed\n",
64 (uintptr_t) p);
66 p = (void *) (((char *) p) + NACL_MAP_PAGESIZE);
70 int NaCl_page_alloc(void **p,
71 size_t num_bytes)
73 SYSTEM_INFO sys_info;
75 int attempt_count;
77 void *addr;
78 void *end_addr;
79 void *chunk;
80 void *unroll;
83 * We have to allocate every 64KB -- the windows allocation
84 * granularity -- because VirtualFree will only accept an address
85 * that was returned by a call to VirtualAlloc. NB: memory pages
86 * put into the address space via MapViewOfFile{,Ex} must be
87 * released by UnmapViewOfFile. Thus, in order for us to open up a
88 * hole in the NaCl application's address space to map in a file, we
89 * must allocate the entire address space in 64KB chunks, so we can
90 * later pick an arbitrary range of addresses (in 64KB chunks) to
91 * free up and map in a file later.
93 * First, we verify via GetSystemInfo that the allocation
94 * granularity matches NACL_MAP_PAGESIZE.
96 * Next, we VirtualAlloc the entire chunk desired. This essentially
97 * asks the kernel where there is space in the virtual address
98 * space. Then, we free this back, and repeat the allocation
99 * starting at the returned address, but in 64KB chunks. If any of
100 * these smaller allocations fail, we roll back and try again.
103 NaClLog(3, "NaCl_page_alloc(*, 0x%x)\n", num_bytes);
104 GetSystemInfo(&sys_info);
105 if (NACL_PAGESIZE != sys_info.dwPageSize) {
106 NaClLog(2, "page size is 0x%x; expected 0x%x\n",
107 sys_info.dwPageSize,
108 NACL_PAGESIZE);
110 if (NACL_MAP_PAGESIZE != sys_info.dwAllocationGranularity) {
111 NaClLog(0, "allocation granularity is 0x%x; expected 0x%x\n",
112 sys_info.dwAllocationGranularity,
113 NACL_MAP_PAGESIZE);
117 * Round allocation request up to next NACL_MAP_PAGESIZE. This is
118 * assumed to have taken place in NaCl_page_free.
120 num_bytes = NaClRoundAllocPage(num_bytes);
122 for (attempt_count = 0;
123 attempt_count < NACL_MEMORY_ALLOC_RETRY_MAX;
124 ++attempt_count) {
126 addr = VirtualAlloc(NULL,
127 num_bytes,
128 MEM_RESERVE,
129 PAGE_EXECUTE_READWRITE);
130 if (addr == NULL) {
131 NaClLog(0,
132 ("NaCl_page_alloc:"
133 " VirtualAlloc(*,0x%x,MEM_COMMIT,PAGE_EXECUTE_READWRITE)"
134 " failed\n"),
135 num_bytes);
136 return -ENOMEM;
138 NaClLog(3,
139 ("NaCl_page_alloc:"
140 " VirtualAlloc(*,0x%x,MEM_COMMIT,PAGE_EXECUTE_READWRITE)"
141 " succeeded, 0x%08x,"
142 " releasing and re-allocating in 64K chunks....\n"),
143 num_bytes, addr);
144 (void) VirtualFree(addr, 0, MEM_RELEASE);
146 * We now know [addr, addr + num_bytes) is available in our addr space.
147 * Get that memory again, but in 64KB chunks.
149 end_addr = (void *) (((char *) addr) + num_bytes);
150 for (chunk = addr;
151 chunk < end_addr;
152 chunk = (void *) (((char *) chunk) + NACL_MAP_PAGESIZE)) {
153 if (NULL == VirtualAlloc(chunk,
154 NACL_MAP_PAGESIZE,
155 MEM_COMMIT | MEM_RESERVE,
156 PAGE_EXECUTE_READWRITE)) {
157 NaClLog(0, ("NaCl_page_alloc: re-allocation failed at 0x%08x,"
158 " error %d\n"),
159 chunk, GetLastError());
160 for (unroll = addr;
161 unroll < chunk;
162 unroll = (void *) (((char *) unroll) + NACL_MAP_PAGESIZE)) {
163 (void) VirtualFree(unroll, 0, MEM_RELEASE);
165 goto retry;
166 /* double break */
169 NaClLog(2, "NaCl_page_alloc: *p = 0x%08x, returning success.\n", addr);
170 *p = addr;
171 return 0;
172 retry:
176 return -ENOMEM;
179 uintptr_t NaClProtectChunkSize(uintptr_t start,
180 uintptr_t end)
182 uintptr_t chunk_end;
184 chunk_end = NaClRoundAllocPage(start + 1);
185 if (chunk_end > end) {
186 chunk_end = end;
188 return chunk_end - start;
192 * This is critical to make the text region non-writable, and the data
193 * region read/write but no exec. Of course, some kernels do not
194 * respect the lack of PROT_EXEC.
196 int NaCl_mprotect(void *addr,
197 size_t len,
198 int prot)
200 uintptr_t start_addr;
201 uintptr_t end_addr;
202 uintptr_t cur_addr;
203 uintptr_t cur_chunk_size;
204 DWORD newProtection, oldProtection;
205 BOOL res;
207 NaClLog(2, "NaCl_mprotect(0x%08x, 0x%x, 0x%x)\n",
208 (uintptr_t) addr, len, prot);
210 if (len == 0) {
211 return 0;
214 start_addr = (uintptr_t) addr;
215 if (!NaClIsPageMultiple(start_addr)) {
216 NaClLog(2, "NaCl_mprotect: start address not a multiple of page size\n");
217 return -EINVAL;
219 if (!NaClIsPageMultiple(len)) {
220 NaClLog(2, "NaCl_mprotect: length not a multiple of page size\n");
221 return -EINVAL;
223 end_addr = start_addr + len;
225 switch (prot)
227 case PROT_EXEC:
229 newProtection = PAGE_EXECUTE;
230 break;
232 case PROT_EXEC|PROT_READ:
234 newProtection = PAGE_EXECUTE_READ;
235 break;
237 case PROT_EXEC|PROT_READ|PROT_WRITE:
239 newProtection = PAGE_EXECUTE_READWRITE;
240 break;
242 case PROT_READ:
244 newProtection = PAGE_READONLY;
245 break;
247 case PROT_READ|PROT_WRITE:
249 newProtection = PAGE_READWRITE;
250 break;
252 case PROT_NONE:
254 newProtection = PAGE_NOACCESS;
255 break;
257 default:
259 NaClLog(2, "NaCl_mprotect: invalid protection mode\n");
260 return -EINVAL;
263 NaClLog(2, "NaCl_mprotect: newProtection = 0x%x\n", newProtection);
265 * VirtualProtect region cannot span allocations: all addresses from
266 * [lpAddress, lpAddress+dwSize) must be in one region of memory
267 * returned from VirtualAlloc or VirtualAllocEx
269 for (cur_addr = start_addr,
270 cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr);
271 cur_addr < end_addr;
272 cur_addr += cur_chunk_size,
273 cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr)) {
274 NaClLog(4, "NaCl_mprotect: VirtualProtect(0x%08x, 0x%x, 0x%x, *)\n",
275 cur_addr, cur_chunk_size, newProtection);
276 res = VirtualProtect((void*) cur_addr, cur_chunk_size,
277 newProtection, &oldProtection);
278 if (!res) {
279 NaClLog(2, "NaCl_mprotect: ... failed\n");
280 return -NaClXlateSystemError(GetLastError());
283 NaClLog(2, "NaCl_mprotect: done\n");
284 return 0;
287 int NaCl_madvise(void *start,
288 size_t length,
289 int advice)
291 int err;
292 uintptr_t start_addr;
293 uintptr_t end_addr;
294 uintptr_t cur_addr;
295 uintptr_t cur_chunk_size;
298 * MADV_DONTNEED and MADV_NORMAL are needed
300 NaClLog(2, "NaCl_madvise(0x%08x, 0x%x, 0x%x)\n",
301 (uintptr_t) start, length, advice);
302 switch (advice) {
303 case MADV_DONTNEED:
304 start_addr = (uintptr_t) start;
305 if (!NaClIsPageMultiple(start_addr)) {
306 NaClLog(2,
307 "NaCl_madvise: start address not a multiple of page size\n");
308 return -EINVAL;
310 if (!NaClIsPageMultiple(length)) {
311 NaClLog(2, "NaCl_madvise: length not a multiple of page size\n");
312 return -EINVAL;
314 end_addr = start_addr + length;
315 for (cur_addr = start_addr,
316 cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr);
317 cur_addr < end_addr;
318 cur_addr += cur_chunk_size,
319 cur_chunk_size = NaClProtectChunkSize(cur_addr, end_addr)) {
320 NaClLog(4,
321 ("NaCl_madvise: MADV_DONTNEED"
322 " -> VirtualAlloc(0x%08x, 0x%x, MEM_RESET, PAGE_NOACCESS)\n"),
323 cur_addr, cur_chunk_size);
324 if (NULL == VirtualAlloc((void*) cur_addr, cur_chunk_size, MEM_RESET,
325 PAGE_NOACCESS)) {
326 err = NaClXlateSystemError(GetLastError());
327 NaClLog(2, "NaCl_madvise: VirtualAlloc failed: 0x%x\n", err);
328 return -err;
331 break;
332 case MADV_NORMAL:
333 memset(start, 0, length);
334 break;
335 default:
336 return -EINVAL;
338 NaClLog(2, "NaCl_madvise: done\n");
339 return 0;