1 : /*
2 : * Copyright (c) 2011 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 Simple/secure ELF loader (NaCl SEL).
9 : */
10 :
11 : #include <errno.h>
12 :
13 : #include "native_client/src/include/nacl_platform.h"
14 : #include "native_client/src/include/portability.h"
15 : #include "native_client/src/shared/platform/nacl_log.h"
16 : #include "native_client/src/trusted/service_runtime/sel_addrspace.h"
17 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
18 : #include "native_client/src/trusted/service_runtime/sel_memory.h"
19 : #include "native_client/src/trusted/service_runtime/sel_util.h"
20 :
21 :
22 4 : NaClErrorCode NaClAllocAddrSpace(struct NaClApp *nap) {
23 : void *mem;
24 : int rv;
25 : uintptr_t hole_start;
26 : size_t hole_size;
27 : uintptr_t stack_start;
28 :
29 4 : NaClLog(2,
30 : "NaClAllocAddrSpace: calling NaClAllocateSpace(*,0x%016"
31 : NACL_PRIxS")\n",
32 : ((size_t) 1 << nap->addr_bits));
33 :
34 4 : rv = NaClAllocateSpace(&mem, (uintptr_t) 1U << nap->addr_bits);
35 4 : if (LOAD_OK != rv) {
36 0 : return rv;
37 : }
38 :
39 4 : nap->mem_start = (uintptr_t) mem;
40 : /*
41 : * The following should not be NaClLog(2, ...) because logging with
42 : * any detail level higher than LOG_INFO is disabled in the release
43 : * builds. This was to reduce logging overhead, so as to eliminate
44 : * at least a function call as well as possibly a TLS/TSD read if
45 : * module-specific logging verbosity level comparisons are needed.
46 : */
47 4 : NaClLog(LOG_INFO,
48 : ("Native Client module will be loaded at"
49 : " base address 0x%016"NACL_PRIxPTR"\n"),
50 : nap->mem_start);
51 :
52 4 : hole_start = NaClRoundAllocPage(nap->data_end);
53 :
54 4 : if (nap->stack_size >= ((uintptr_t) 1U) << nap->addr_bits) {
55 0 : NaClLog(LOG_FATAL, "NaClAllocAddrSpace: stack too large!");
56 : }
57 4 : stack_start = (((uintptr_t) 1U) << nap->addr_bits) - nap->stack_size;
58 4 : stack_start = NaClTruncAllocPage(stack_start);
59 :
60 4 : if (stack_start < hole_start) {
61 1 : return LOAD_DATA_OVERLAPS_STACK_SECTION;
62 : }
63 :
64 3 : hole_size = stack_start - hole_start;
65 3 : hole_size = NaClTruncAllocPage(hole_size);
66 :
67 : /*
68 : * mprotect and madvise unused data space to "free" it up, but
69 : * retain mapping so no other memory can be mapped into those
70 : * addresses.
71 : */
72 3 : if (hole_size == 0) {
73 0 : NaClLog(2, ("NaClAllocAddrSpace: hole between end of data and"
74 : " the beginning of stack is zero size.\n"));
75 : } else {
76 3 : NaClLog(2,
77 : ("madvising 0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxS
78 : ", MADV_DONTNEED\n"),
79 : nap->mem_start + hole_start, hole_size);
80 3 : if (0 != NaCl_madvise((void *) (nap->mem_start + hole_start),
81 : hole_size,
82 : MADV_DONTNEED)) {
83 0 : NaClLog(1, "madvise, errno %d\n", errno);
84 0 : return LOAD_MADVISE_FAIL;
85 : }
86 3 : NaClLog(2,
87 : "mprotecting 0x%08"NACL_PRIxPTR", 0x%08"NACL_PRIxS", PROT_NONE\n",
88 : nap->mem_start + hole_start, hole_size);
89 3 : if (0 != NaCl_mprotect((void *) (nap->mem_start + hole_start),
90 : hole_size,
91 : PROT_NONE)) {
92 0 : NaClLog(1, "mprotect, errno %d\n", errno);
93 0 : return LOAD_MPROTECT_FAIL;
94 : }
95 : }
96 :
97 3 : return LOAD_OK;
98 : }
99 :
100 :
101 : /*
102 : * Apply memory protection to memory regions.
103 : */
104 3 : NaClErrorCode NaClMemoryProtection(struct NaClApp *nap) {
105 : uintptr_t start_addr;
106 : size_t region_size;
107 : int err;
108 :
109 : /*
110 : * The first NACL_SYSCALL_START_ADDR bytes are mapped as PROT_NONE.
111 : * This enables NULL pointer checking, and provides additional protection
112 : * against addr16/data16 prefixed operations being used for attacks.
113 : *
114 : * NaClMprotectGuards also sets up guard pages outside of the
115 : * virtual address space of the NaClApp -- for the ARM and x86-64
116 : * where data sandboxing only sandbox memory writes and not reads,
117 : * we need to ensure that certain addressing modes that might
118 : * otherwise allow the NaCl app to write outside its address space
119 : * (given how we using masking / base registers to implement data
120 : * write sandboxing) won't affect the trusted data structures.
121 : */
122 :
123 3 : NaClLog(3, "Protecting guard pages for 0x%08"NACL_PRIxPTR"\n",
124 : nap->mem_start);
125 3 : err = NaClMprotectGuards(nap);
126 3 : if (err != LOAD_OK) return err;
127 :
128 3 : start_addr = nap->mem_start + NACL_SYSCALL_START_ADDR;
129 : /*
130 : * The next pages up to NACL_TRAMPOLINE_END are the trampolines.
131 : * Immediately following that is the loaded text section.
132 : * These are collectively marked as PROT_READ | PROT_EXEC.
133 : */
134 3 : region_size = NaClRoundPage(nap->static_text_end - NACL_SYSCALL_START_ADDR);
135 3 : NaClLog(3,
136 : ("Trampoline/text region start 0x%08"NACL_PRIxPTR","
137 : " size 0x%08"NACL_PRIxS", end 0x%08"NACL_PRIxPTR"\n"),
138 : start_addr, region_size,
139 : start_addr + region_size);
140 3 : if (0 != (err = NaCl_mprotect((void *) start_addr,
141 : region_size,
142 : PROT_READ | PROT_EXEC))) {
143 0 : NaClLog(LOG_ERROR,
144 : ("NaClMemoryProtection: "
145 : "NaCl_mprotect(0x%08"NACL_PRIxPTR", "
146 : "0x%08"NACL_PRIxS", 0x%x) failed, "
147 : "error %d (trampoline)\n"),
148 : start_addr, region_size, PROT_READ | PROT_EXEC,
149 : err);
150 0 : return LOAD_MPROTECT_FAIL;
151 : }
152 3 : if (!NaClVmmapAdd(&nap->mem_map,
153 : NaClSysToUser(nap, start_addr) >> NACL_PAGESHIFT,
154 : region_size >> NACL_PAGESHIFT,
155 : PROT_READ | PROT_EXEC,
156 : NULL)) {
157 0 : NaClLog(LOG_ERROR, ("NaClMemoryProtection: NaClVmmapAdd failed"
158 : " (trampoline)\n"));
159 0 : return LOAD_MPROTECT_FAIL;
160 : }
161 :
162 3 : start_addr = NaClUserToSys(nap, nap->dynamic_text_start);
163 3 : region_size = nap->dynamic_text_end - nap->dynamic_text_start;
164 3 : NaClLog(3,
165 : ("shm txt region start 0x%08"NACL_PRIxPTR", size 0x%08"NACL_PRIxS","
166 : " end 0x%08"NACL_PRIxPTR"\n"),
167 : start_addr, region_size,
168 : start_addr + region_size);
169 3 : if (0 != region_size) {
170 : /*
171 : * Page protections for this region have already been set up by
172 : * nacl_text.c.
173 : *
174 : * We record the mapping for consistency with other fixed
175 : * mappings, but the record is not actually used. Overmapping is
176 : * prevented by a separate range check, which is done by
177 : * NaClSysCommonAddrRangeContainsExecutablePages_mu().
178 : */
179 3 : if (!NaClVmmapAdd(&nap->mem_map,
180 : NaClSysToUser(nap, start_addr) >> NACL_PAGESHIFT,
181 : region_size >> NACL_PAGESHIFT,
182 : PROT_READ | PROT_EXEC,
183 : NaClMemObjMake(nap->text_shm,
184 : region_size,
185 : 0))) {
186 0 : NaClLog(LOG_ERROR, ("NaClMemoryProtection: NaClVmmapAdd failed"
187 : " (data)\n"));
188 0 : return LOAD_MPROTECT_FAIL;
189 : }
190 : }
191 :
192 3 : if (0 != nap->rodata_start) {
193 : uintptr_t rodata_end;
194 : /*
195 : * TODO(mseaborn): Could reduce the number of cases by ensuring
196 : * that nap->data_start is always non-zero, even if
197 : * nap->rodata_start == nap->data_start == nap->break_addr.
198 : */
199 3 : if (0 != nap->data_start) {
200 3 : rodata_end = nap->data_start;
201 : }
202 : else {
203 0 : rodata_end = nap->break_addr;
204 : }
205 :
206 3 : start_addr = NaClUserToSys(nap, nap->rodata_start);
207 3 : region_size = NaClRoundPage(NaClRoundAllocPage(rodata_end)
208 : - NaClSysToUser(nap, start_addr));
209 3 : NaClLog(3,
210 : ("RO data region start 0x%08"NACL_PRIxPTR", size 0x%08"NACL_PRIxS","
211 : " end 0x%08"NACL_PRIxPTR"\n"),
212 : start_addr, region_size,
213 : start_addr + region_size);
214 3 : if (0 != (err = NaCl_mprotect((void *) start_addr,
215 : region_size,
216 : PROT_READ))) {
217 0 : NaClLog(LOG_ERROR,
218 : ("NaClMemoryProtection: "
219 : "NaCl_mprotect(0x%08"NACL_PRIxPTR", "
220 : "0x%08"NACL_PRIxS", 0x%x) failed, "
221 : "error %d (data)\n"),
222 : start_addr, region_size, PROT_READ,
223 : err);
224 0 : return LOAD_MPROTECT_FAIL;
225 : }
226 3 : if (!NaClVmmapAdd(&nap->mem_map,
227 : NaClSysToUser(nap, start_addr) >> NACL_PAGESHIFT,
228 : region_size >> NACL_PAGESHIFT,
229 : PROT_READ,
230 : (struct NaClMemObj *) NULL)) {
231 0 : NaClLog(LOG_ERROR, ("NaClMemoryProtection: NaClVmmapAdd failed"
232 : " (data)\n"));
233 0 : return LOAD_MPROTECT_FAIL;
234 : }
235 : }
236 :
237 : /*
238 : * data_end is max virtual addr seen, so start_addr <= data_end
239 : * must hold.
240 : */
241 :
242 3 : if (0 != nap->data_start) {
243 3 : start_addr = NaClUserToSys(nap, nap->data_start);
244 3 : region_size = NaClRoundPage(NaClRoundAllocPage(nap->data_end)
245 : - NaClSysToUser(nap, start_addr));
246 3 : NaClLog(3,
247 : ("RW data region start 0x%08"NACL_PRIxPTR", size 0x%08"NACL_PRIxS","
248 : " end 0x%08"NACL_PRIxPTR"\n"),
249 : start_addr, region_size,
250 : start_addr + region_size);
251 3 : if (0 != (err = NaCl_mprotect((void *) start_addr,
252 : region_size,
253 : PROT_READ | PROT_WRITE))) {
254 0 : NaClLog(LOG_ERROR,
255 : ("NaClMemoryProtection: "
256 : "NaCl_mprotect(0x%08"NACL_PRIxPTR", "
257 : "0x%08"NACL_PRIxS", 0x%x) failed, "
258 : "error %d (data)\n"),
259 : start_addr, region_size, PROT_READ | PROT_WRITE,
260 : err);
261 0 : return LOAD_MPROTECT_FAIL;
262 : }
263 3 : if (!NaClVmmapAdd(&nap->mem_map,
264 : NaClSysToUser(nap, start_addr) >> NACL_PAGESHIFT,
265 : region_size >> NACL_PAGESHIFT,
266 : PROT_READ | PROT_WRITE,
267 : (struct NaClMemObj *) NULL)) {
268 0 : NaClLog(LOG_ERROR, ("NaClMemoryProtection: NaClVmmapAdd failed"
269 : " (data)\n"));
270 0 : return LOAD_MPROTECT_FAIL;
271 : }
272 : }
273 :
274 : /* stack is read/write but not execute */
275 3 : region_size = nap->stack_size;
276 3 : start_addr = NaClUserToSys(nap,
277 : NaClTruncAllocPage(
278 : ((uintptr_t) 1U << nap->addr_bits)
279 : - nap->stack_size));
280 3 : NaClLog(3,
281 : ("RW stack region start 0x%08"NACL_PRIxPTR", size 0x%08"NACL_PRIxS","
282 : " end 0x%08"NACL_PRIxPTR"\n"),
283 : start_addr, region_size,
284 : start_addr + region_size);
285 3 : if (0 != (err = NaCl_mprotect((void *) start_addr,
286 : NaClRoundAllocPage(nap->stack_size),
287 : PROT_READ | PROT_WRITE))) {
288 0 : NaClLog(LOG_ERROR,
289 : ("NaClMemoryProtection: "
290 : "NaCl_mprotect(0x%08"NACL_PRIxPTR", "
291 : "0x%08"NACL_PRIxS", 0x%x) failed, "
292 : "error %d (stack)\n"),
293 : start_addr, region_size, PROT_READ | PROT_WRITE,
294 : err);
295 0 : return LOAD_MPROTECT_FAIL;
296 : }
297 :
298 3 : if (!NaClVmmapAdd(&nap->mem_map,
299 : NaClSysToUser(nap, start_addr) >> NACL_PAGESHIFT,
300 : nap->stack_size >> NACL_PAGESHIFT,
301 : PROT_READ | PROT_WRITE,
302 : (struct NaClMemObj *) NULL)) {
303 0 : NaClLog(LOG_ERROR, ("NaClMemoryProtection: NaClVmmapAdd failed"
304 : " (stack)\n"));
305 0 : return LOAD_MPROTECT_FAIL;
306 : }
307 3 : return LOAD_OK;
308 : }
|