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 : * ncfileutil.c - open an executable file. FOR TESTING ONLY.
9 : */
10 :
11 : #include "native_client/src/trusted/validator/ncfileutil.h"
12 :
13 : #include "native_client/src/include/portability.h"
14 :
15 : #include <stdarg.h>
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 : #include <assert.h>
20 : #include <errno.h>
21 : #include <fcntl.h>
22 : #include <sys/types.h>
23 :
24 : #include "native_client/src/include/portability_io.h"
25 :
26 : /* This module is intended for testing use only, not for production use */
27 : /* in sel_ldr. To prevent unintended production usage, define a symbol */
28 : /* that will cause a load-time error for sel_ldr. */
29 : int gNaClValidateImage_foo = 0;
30 0 : void NaClValidateImage(void) { gNaClValidateImage_foo += 1; }
31 :
32 : static void NcLoadFilePrintError(const char* format, ...)
33 : ATTRIBUTE_FORMAT_PRINTF(1, 2);
34 :
35 : /* Define the default print error function to use for this module. */
36 0 : static void NcLoadFilePrintError(const char* format, ...) {
37 : va_list ap;
38 0 : va_start(ap, format);
39 0 : vfprintf(stderr, format, ap);
40 0 : va_end(ap);
41 0 : }
42 :
43 : /***********************************************************************/
44 : /* THIS ROUTINE IS FOR DEBUGGING/TESTING ONLY, NOT FOR SECURE RUNTIME */
45 : /* ALL PAGES ARE LEFT WRITEABLE BY THIS LOADER. */
46 : /***********************************************************************/
47 : /* Loading a NC executable from a host file */
48 : static off_t readat(ncfile* ncf, const int fd,
49 0 : void *buf, const off_t sz, const off_t at) {
50 : /* TODO(karl) fix types for off_t and size_t so that the work for 64-bits */
51 0 : int sofar = 0;
52 : int nread;
53 0 : char *cbuf = (char *) buf;
54 :
55 0 : if (0 > lseek(fd, (long) at, SEEK_SET)) {
56 0 : ncf->error_fn("readat: lseek failed\n");
57 0 : return -1;
58 : }
59 :
60 : /* TODO(robertm) Figure out if O_BINARY flag fixes this. */
61 : /* Strangely this loop is needed on Windows. It seems the read() */
62 : /* implementation doesn't always return as many bytes as requested */
63 : /* so you have to keep on trying. */
64 : do {
65 0 : nread = read(fd, &cbuf[sofar], sz - sofar);
66 0 : if (nread <= 0) {
67 0 : ncf->error_fn("readat: read failed\n");
68 0 : return -1;
69 : }
70 0 : sofar += nread;
71 0 : } while (sz != sofar);
72 0 : return nread;
73 0 : }
74 :
75 0 : static const char* GetEiClassName(unsigned char c) {
76 0 : if (c == ELFCLASS32) {
77 0 : return "(32 bit executable)";
78 0 : } else if (c == ELFCLASS64) {
79 0 : return "(64 bit executable)";
80 : } else {
81 0 : return "(invalid class)";
82 : }
83 0 : }
84 :
85 0 : static int nc_load(ncfile *ncf, int fd) {
86 : union {
87 : Elf32_Ehdr h32;
88 : #if NACL_TARGET_SUBARCH == 64
89 : Elf64_Ehdr h64;
90 : #endif
91 : } h;
92 : Elf_Half phnum;
93 : Elf_Half shnum;
94 : Elf_Off phoff;
95 : Elf_Off shoff;
96 : ssize_t nread;
97 : Elf_Addr vmemlo, vmemhi;
98 : size_t shsize, phsize;
99 : int i;
100 :
101 : /* Read and check the ELF header */
102 0 : nread = readat(ncf, fd, &h, sizeof(h), 0);
103 0 : if (nread < 0 || (size_t) nread < sizeof(h)) {
104 0 : ncf->error_fn("nc_load(%s): could not read ELF header", ncf->fname);
105 0 : return -1;
106 : }
107 :
108 : /* do a bunch of sanity checks */
109 0 : if (memcmp(h.h32.e_ident, ELFMAG, SELFMAG)) {
110 0 : ncf->error_fn("nc_load(%s): bad magic number", ncf->fname);
111 0 : return -1;
112 : }
113 :
114 : #if NACL_TARGET_SUBARCH == 64
115 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
116 : if (h.h64.e_phoff > 0xffffffffU) {
117 : ncf->error_fn("nc_load(%s): e_phoff overflows 32 bits\n", ncf->fname);
118 : return -1;
119 : }
120 : if (h.h64.e_shoff > 0xffffffffU) {
121 : ncf->error_fn("nc_load(%s): e_shoff overflows 32 bits\n", ncf->fname);
122 : return -1;
123 : }
124 : phoff = (Elf32_Off) h.h64.e_phoff;
125 : shoff = (Elf32_Off) h.h64.e_shoff;
126 : phnum = h.h64.e_phnum;
127 : shnum = h.h64.e_shnum;
128 : } else
129 : #endif
130 : {
131 0 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS32) {
132 0 : phoff = h.h32.e_phoff;
133 0 : shoff = h.h32.e_shoff;
134 0 : phnum = h.h32.e_phnum;
135 0 : shnum = h.h32.e_shnum;
136 0 : } else {
137 : ncf->error_fn("nc_load(%s): bad EI CLASS %d %s\n", ncf->fname,
138 : h.h32.e_ident[EI_CLASS],
139 0 : GetEiClassName(h.h32.e_ident[EI_CLASS]));
140 0 : return -1;
141 : }
142 : }
143 :
144 : /* Read the program header table */
145 0 : if (phnum <= 0 || phnum > kMaxPhnum) {
146 : ncf->error_fn("nc_load(%s): e_phnum %d > kMaxPhnum %d\n",
147 0 : ncf->fname, phnum, kMaxPhnum);
148 0 : return -1;
149 : }
150 0 : ncf->phnum = phnum;
151 0 : ncf->pheaders = (Elf_Phdr *)calloc(phnum, sizeof(Elf_Phdr));
152 0 : if (NULL == ncf->pheaders) {
153 : ncf->error_fn("nc_load(%s): calloc(%d, %"NACL_PRIuS") failed\n",
154 0 : ncf->fname, phnum, sizeof(Elf_Phdr));
155 0 : return -1;
156 : }
157 0 : phsize = phnum * sizeof(*ncf->pheaders);
158 : #if NACL_TARGET_SUBARCH == 64
159 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
160 : /*
161 : * Read 64-bit program headers and convert them.
162 : */
163 : Elf64_Phdr phdr64[kMaxPhnum];
164 : nread = readat(ncf, fd, phdr64, (off_t) (phnum * sizeof(phdr64[0])),
165 : (off_t) phoff);
166 : if (nread < 0 || (size_t) nread < phsize) return -1;
167 : for (i = 0; i < phnum; ++i) {
168 : if (phdr64[i].p_offset > 0xffffffffU ||
169 : phdr64[i].p_vaddr > 0xffffffffU ||
170 : phdr64[i].p_paddr > 0xffffffffU ||
171 : phdr64[i].p_filesz > 0xffffffffU ||
172 : phdr64[i].p_memsz > 0xffffffffU ||
173 : phdr64[i].p_align > 0xffffffffU) {
174 : ncf->error_fn("nc_load(%s): phdr[%d] fields overflow 32 bits\n",
175 : ncf->fname, i);
176 : return -1;
177 : }
178 : ncf->pheaders[i].p_type = phdr64[i].p_type;
179 : ncf->pheaders[i].p_flags = phdr64[i].p_flags;
180 : ncf->pheaders[i].p_offset = (Elf32_Off) phdr64[i].p_offset;
181 : ncf->pheaders[i].p_vaddr = (Elf32_Addr) phdr64[i].p_vaddr;
182 : ncf->pheaders[i].p_paddr = (Elf32_Addr) phdr64[i].p_paddr;
183 : ncf->pheaders[i].p_filesz = (Elf32_Word) phdr64[i].p_filesz;
184 : ncf->pheaders[i].p_memsz = (Elf32_Word) phdr64[i].p_memsz;
185 : ncf->pheaders[i].p_align = (Elf32_Word) phdr64[i].p_align;
186 : }
187 : } else
188 : #endif
189 : {
190 : /* TODO(karl) Remove the cast to size_t, or verify size. */
191 0 : nread = readat(ncf, fd, ncf->pheaders, (off_t) phsize, (off_t) phoff);
192 0 : if (nread < 0 || (size_t) nread < phsize) return -1;
193 : }
194 :
195 : /* Iterate through the program headers to find the virtual */
196 : /* size of loaded text. */
197 0 : vmemlo = MAX_ELF_ADDR;
198 0 : vmemhi = MIN_ELF_ADDR;
199 0 : for (i = 0; i < phnum; i++) {
200 0 : if (ncf->pheaders[i].p_type != PT_LOAD) continue;
201 0 : if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
202 : /* This is executable text. Check low and high addrs */
203 0 : if (vmemlo > ncf->pheaders[i].p_vaddr) vmemlo = ncf->pheaders[i].p_vaddr;
204 0 : if (vmemhi < ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz) {
205 0 : vmemhi = ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz;
206 : }
207 0 : }
208 0 : ncf->size = vmemhi - vmemlo;
209 0 : ncf->vbase = vmemlo;
210 : /* TODO(karl) Remove the cast to size_t, or verify size. */
211 0 : ncf->data = (uint8_t *)calloc(1, (size_t) ncf->size);
212 0 : if (NULL == ncf->data) {
213 : ncf->error_fn("nc_load(%s): calloc(1, %d) failed\n",
214 0 : ncf->fname, (int)ncf->size);
215 0 : return -1;
216 : }
217 :
218 : /* Load program text segments */
219 0 : for (i = 0; i < phnum; i++) {
220 0 : const Elf_Phdr *p = &ncf->pheaders[i];
221 0 : if (p->p_type != PT_LOAD) continue;
222 0 : if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
223 :
224 : /* TODO(karl) Remove the cast to off_t, or verify value in range. */
225 : nread = readat(ncf, fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
226 0 : (off_t) p->p_filesz, (off_t) p->p_offset);
227 0 : if (nread < 0 || (size_t) nread < p->p_filesz) {
228 : ncf->error_fn(
229 : "nc_load(%s): could not read segment %d (%d < %"
230 : NACL_PRIuElf_Xword")\n",
231 0 : ncf->fname, i, (int)nread, p->p_filesz);
232 0 : return -1;
233 : }
234 0 : }
235 :
236 : /* load the section headers */
237 0 : ncf->shnum = shnum;
238 0 : shsize = ncf->shnum * sizeof(*ncf->sheaders);
239 0 : ncf->sheaders = (Elf_Shdr *)calloc(1, shsize);
240 0 : if (NULL == ncf->sheaders) {
241 : ncf->error_fn("nc_load(%s): calloc(1, %"NACL_PRIuS") failed\n",
242 0 : ncf->fname, shsize);
243 0 : return -1;
244 : }
245 : #if NACL_TARGET_SUBARCH == 64
246 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
247 : /*
248 : * Read 64-bit section headers and convert them.
249 : */
250 : Elf64_Shdr *shdr64 = (Elf64_Shdr *)calloc(shnum, sizeof(shdr64[0]));
251 : if (NULL == shdr64) {
252 : ncf->error_fn(
253 : "nc_load(%s): calloc(%"NACL_PRIdS", %"NACL_PRIdS") failed\n",
254 : ncf->fname, (size_t) shnum, sizeof(shdr64[0]));
255 : return -1;
256 : }
257 : shsize = ncf->shnum * sizeof(shdr64[0]);
258 : nread = readat(ncf, fd, shdr64, (off_t) shsize, (off_t) shoff);
259 : if (nread < 0 || (size_t) nread < shsize) {
260 : ncf->error_fn("nc_load(%s): could not read section headers\n",
261 : ncf->fname);
262 : return -1;
263 : }
264 : for (i = 0; i < shnum; ++i) {
265 : if (shdr64[i].sh_flags > 0xffffffffU ||
266 : shdr64[i].sh_size > 0xffffffffU ||
267 : shdr64[i].sh_addralign > 0xffffffffU ||
268 : shdr64[i].sh_entsize > 0xffffffffU) {
269 : ncf->error_fn("nc_load(%s): shdr[%d] fields overflow 32 bits\n",
270 : ncf->fname, i);
271 : return -1;
272 : }
273 : ncf->sheaders[i].sh_name = shdr64[i].sh_name;
274 : ncf->sheaders[i].sh_type = shdr64[i].sh_type;
275 : ncf->sheaders[i].sh_flags = (Elf32_Word) shdr64[i].sh_flags;
276 : ncf->sheaders[i].sh_addr = (Elf32_Addr) shdr64[i].sh_addr;
277 : ncf->sheaders[i].sh_offset = (Elf32_Off) shdr64[i].sh_offset;
278 : ncf->sheaders[i].sh_size = (Elf32_Word) shdr64[i].sh_size;
279 : ncf->sheaders[i].sh_link = shdr64[i].sh_link;
280 : ncf->sheaders[i].sh_info = shdr64[i].sh_info;
281 : ncf->sheaders[i].sh_addralign = (Elf32_Word) shdr64[i].sh_addralign;
282 : ncf->sheaders[i].sh_entsize = (Elf32_Word) shdr64[i].sh_entsize;
283 : }
284 : free(shdr64);
285 : } else
286 : #endif
287 : {
288 : /* TODO(karl) Remove the cast to size_t, or verify value in range. */
289 0 : nread = readat(ncf, fd, ncf->sheaders, (off_t) shsize, (off_t) shoff);
290 0 : if (nread < 0 || (size_t) nread < shsize) {
291 : ncf->error_fn("nc_load(%s): could not read section headers\n",
292 0 : ncf->fname);
293 0 : return -1;
294 : }
295 : }
296 :
297 : /* success! */
298 0 : return 0;
299 0 : }
300 :
301 : ncfile *nc_loadfile_depending(const char *filename,
302 0 : nc_loadfile_error_fn error_fn) {
303 : ncfile *ncf;
304 : int fd;
305 0 : int rdflags = O_RDONLY | _O_BINARY;
306 0 : fd = OPEN(filename, rdflags);
307 0 : if (fd < 0) return NULL;
308 :
309 : /* Allocate the ncfile structure */
310 0 : ncf = calloc(1, sizeof(ncfile));
311 0 : if (ncf == NULL) return NULL;
312 0 : ncf->size = 0;
313 0 : ncf->data = NULL;
314 0 : ncf->fname = filename;
315 0 : if (error_fn == NULL) {
316 0 : ncf->error_fn = NcLoadFilePrintError;
317 0 : } else {
318 0 : ncf->error_fn = error_fn;
319 : }
320 :
321 0 : if (nc_load(ncf, fd) < 0) {
322 0 : close(fd);
323 0 : free(ncf);
324 0 : return NULL;
325 : }
326 0 : close(fd);
327 0 : return ncf;
328 0 : }
329 :
330 0 : ncfile *nc_loadfile(const char *filename) {
331 0 : return nc_loadfile_depending(filename, NULL);
332 0 : }
333 :
334 : ncfile *nc_loadfile_with_error_fn(const char *filename,
335 0 : nc_loadfile_error_fn error_fn) {
336 0 : return nc_loadfile_depending(filename, error_fn);
337 0 : }
338 :
339 :
340 0 : void nc_freefile(ncfile *ncf) {
341 0 : if (ncf->data != NULL) free(ncf->data);
342 0 : free(ncf);
343 0 : }
344 :
345 : /***********************************************************************/
346 :
347 : void GetVBaseAndLimit(ncfile *ncf, NaClPcAddress *vbase,
348 0 : NaClPcAddress *vlimit) {
349 : int ii;
350 : /* TODO(karl) - Define so constant applies to 64-bit pc address. */
351 0 : NaClPcAddress base = 0xffffffff;
352 0 : NaClPcAddress limit = 0;
353 :
354 0 : for (ii = 0; ii < ncf->shnum; ii++) {
355 0 : if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
356 0 : if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
357 0 : if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
358 0 : limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
359 : }
360 0 : }
361 0 : *vbase = base;
362 0 : *vlimit = limit;
363 0 : }
|