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 : * 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() { 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 24 : 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 24 : int sofar = 0;
52 : int nread;
53 24 : char *cbuf = (char *) buf;
54 :
55 24 : 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 24 : nread = read(fd, &cbuf[sofar], sz - sofar);
66 24 : if (nread <= 0) {
67 0 : ncf->error_fn("readat: read failed\n");
68 0 : return -1;
69 : }
70 24 : sofar += nread;
71 24 : } while (sz != sofar);
72 24 : return nread;
73 : }
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 : }
84 :
85 6 : 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 6 : nread = readat(ncf, fd, &h, sizeof(h), 0);
103 6 : 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 6 : 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 6 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS32) {
132 6 : phoff = h.h32.e_phoff;
133 6 : shoff = h.h32.e_shoff;
134 6 : phnum = h.h32.e_phnum;
135 6 : shnum = h.h32.e_shnum;
136 : } else {
137 0 : ncf->error_fn("nc_load(%s): bad EI CLASS %d %s\n", ncf->fname,
138 : h.h32.e_ident[EI_CLASS],
139 : GetEiClassName(h.h32.e_ident[EI_CLASS]));
140 0 : return -1;
141 : }
142 : }
143 :
144 : /* We now support only 32-byte bundle alignment. */
145 6 : ncf->ncalign = 32;
146 :
147 : /* Read the program header table */
148 12 : if (phnum <= 0 || phnum > kMaxPhnum) {
149 0 : ncf->error_fn("nc_load(%s): e_phnum %d > kMaxPhnum %d\n",
150 : ncf->fname, phnum, kMaxPhnum);
151 0 : return -1;
152 : }
153 6 : ncf->phnum = phnum;
154 6 : ncf->pheaders = (Elf_Phdr *)calloc(phnum, sizeof(Elf_Phdr));
155 6 : if (NULL == ncf->pheaders) {
156 0 : ncf->error_fn("nc_load(%s): calloc(%d, %"NACL_PRIdS") failed\n",
157 : ncf->fname, phnum, sizeof(Elf_Phdr));
158 0 : return -1;
159 : }
160 6 : phsize = phnum * sizeof(*ncf->pheaders);
161 : #if NACL_TARGET_SUBARCH == 64
162 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
163 : /*
164 : * Read 64-bit program headers and convert them.
165 : */
166 : Elf64_Phdr phdr64[kMaxPhnum];
167 : nread = readat(ncf, fd, phdr64, (off_t) (phnum * sizeof(phdr64[0])),
168 : (off_t) phoff);
169 : if (nread < 0 || (size_t) nread < phsize) return -1;
170 : for (i = 0; i < phnum; ++i) {
171 : if (phdr64[i].p_offset > 0xffffffffU ||
172 : phdr64[i].p_vaddr > 0xffffffffU ||
173 : phdr64[i].p_paddr > 0xffffffffU ||
174 : phdr64[i].p_filesz > 0xffffffffU ||
175 : phdr64[i].p_memsz > 0xffffffffU ||
176 : phdr64[i].p_align > 0xffffffffU) {
177 : ncf->error_fn("nc_load(%s): phdr[%d] fields overflow 32 bits\n",
178 : ncf->fname, i);
179 : return -1;
180 : }
181 : ncf->pheaders[i].p_type = phdr64[i].p_type;
182 : ncf->pheaders[i].p_flags = phdr64[i].p_flags;
183 : ncf->pheaders[i].p_offset = (Elf32_Off) phdr64[i].p_offset;
184 : ncf->pheaders[i].p_vaddr = (Elf32_Addr) phdr64[i].p_vaddr;
185 : ncf->pheaders[i].p_paddr = (Elf32_Addr) phdr64[i].p_paddr;
186 : ncf->pheaders[i].p_filesz = (Elf32_Word) phdr64[i].p_filesz;
187 : ncf->pheaders[i].p_memsz = (Elf32_Word) phdr64[i].p_memsz;
188 : ncf->pheaders[i].p_align = (Elf32_Word) phdr64[i].p_align;
189 : }
190 : } else
191 : #endif
192 : {
193 : /* TODO(karl) Remove the cast to size_t, or verify size. */
194 6 : nread = readat(ncf, fd, ncf->pheaders, (off_t) phsize, (off_t) phoff);
195 6 : if (nread < 0 || (size_t) nread < phsize) return -1;
196 : }
197 :
198 : /* Iterate through the program headers to find the virtual */
199 : /* size of loaded text. */
200 6 : vmemlo = MAX_ELF_ADDR;
201 6 : vmemhi = MIN_ELF_ADDR;
202 30 : for (i = 0; i < phnum; i++) {
203 24 : if (ncf->pheaders[i].p_type != PT_LOAD) continue;
204 18 : if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
205 : /* This is executable text. Check low and high addrs */
206 6 : if (vmemlo > ncf->pheaders[i].p_vaddr) vmemlo = ncf->pheaders[i].p_vaddr;
207 6 : if (vmemhi < ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz) {
208 6 : vmemhi = ncf->pheaders[i].p_vaddr + ncf->pheaders[i].p_memsz;
209 : }
210 : }
211 6 : ncf->size = vmemhi - vmemlo;
212 6 : ncf->vbase = vmemlo;
213 : /* TODO(karl) Remove the cast to size_t, or verify size. */
214 6 : ncf->data = (uint8_t *)calloc(1, (size_t) ncf->size);
215 6 : if (NULL == ncf->data) {
216 0 : ncf->error_fn("nc_load(%s): calloc(1, %d) failed\n",
217 : ncf->fname, (int)ncf->size);
218 0 : return -1;
219 : }
220 :
221 : /* Load program text segments */
222 30 : for (i = 0; i < phnum; i++) {
223 24 : const Elf_Phdr *p = &ncf->pheaders[i];
224 24 : if (p->p_type != PT_LOAD) continue;
225 18 : if (0 == (ncf->pheaders[i].p_flags & PF_X)) continue;
226 :
227 : /* TODO(karl) Remove the cast to off_t, or verify value in range. */
228 6 : nread = readat(ncf, fd, &(ncf->data[p->p_vaddr - ncf->vbase]),
229 : (off_t) p->p_filesz, (off_t) p->p_offset);
230 6 : if (nread < 0 || (size_t) nread < p->p_filesz) {
231 0 : ncf->error_fn(
232 : "nc_load(%s): could not read segment %d (%d < %"
233 : NACL_PRIuElf_Xword")\n",
234 : ncf->fname, i, (int)nread, p->p_filesz);
235 0 : return -1;
236 : }
237 : }
238 :
239 : /* load the section headers */
240 6 : ncf->shnum = shnum;
241 6 : shsize = ncf->shnum * sizeof(*ncf->sheaders);
242 6 : ncf->sheaders = (Elf_Shdr *)calloc(1, shsize);
243 6 : if (NULL == ncf->sheaders) {
244 0 : ncf->error_fn("nc_load(%s): calloc(1, %"NACL_PRIdS") failed\n",
245 : ncf->fname, shsize);
246 0 : return -1;
247 : }
248 : #if NACL_TARGET_SUBARCH == 64
249 : if (h.h32.e_ident[EI_CLASS] == ELFCLASS64) {
250 : /*
251 : * Read 64-bit section headers and convert them.
252 : */
253 : Elf64_Shdr *shdr64 = (Elf64_Shdr *)calloc(shnum, sizeof(shdr64[0]));
254 : if (NULL == shdr64) {
255 : ncf->error_fn(
256 : "nc_load(%s): calloc(%"NACL_PRIdS", %"NACL_PRIdS") failed\n",
257 : ncf->fname, (size_t) shnum, sizeof(shdr64[0]));
258 : return -1;
259 : }
260 : shsize = ncf->shnum * sizeof(shdr64[0]);
261 : nread = readat(ncf, fd, shdr64, (off_t) shsize, (off_t) shoff);
262 : if (nread < 0 || (size_t) nread < shsize) {
263 : ncf->error_fn("nc_load(%s): could not read section headers\n",
264 : ncf->fname);
265 : return -1;
266 : }
267 : for (i = 0; i < shnum; ++i) {
268 : if (shdr64[i].sh_flags > 0xffffffffU ||
269 : shdr64[i].sh_size > 0xffffffffU ||
270 : shdr64[i].sh_addralign > 0xffffffffU ||
271 : shdr64[i].sh_entsize > 0xffffffffU) {
272 : ncf->error_fn("nc_load(%s): shdr[%d] fields overflow 32 bits\n",
273 : ncf->fname, i);
274 : return -1;
275 : }
276 : ncf->sheaders[i].sh_name = shdr64[i].sh_name;
277 : ncf->sheaders[i].sh_type = shdr64[i].sh_type;
278 : ncf->sheaders[i].sh_flags = (Elf32_Word) shdr64[i].sh_flags;
279 : ncf->sheaders[i].sh_addr = (Elf32_Addr) shdr64[i].sh_addr;
280 : ncf->sheaders[i].sh_offset = (Elf32_Off) shdr64[i].sh_offset;
281 : ncf->sheaders[i].sh_size = (Elf32_Word) shdr64[i].sh_size;
282 : ncf->sheaders[i].sh_link = shdr64[i].sh_link;
283 : ncf->sheaders[i].sh_info = shdr64[i].sh_info;
284 : ncf->sheaders[i].sh_addralign = (Elf32_Word) shdr64[i].sh_addralign;
285 : ncf->sheaders[i].sh_entsize = (Elf32_Word) shdr64[i].sh_entsize;
286 : }
287 : free(shdr64);
288 : } else
289 : #endif
290 : {
291 : /* TODO(karl) Remove the cast to size_t, or verify value in range. */
292 6 : nread = readat(ncf, fd, ncf->sheaders, (off_t) shsize, (off_t) shoff);
293 6 : if (nread < 0 || (size_t) nread < shsize) {
294 0 : ncf->error_fn("nc_load(%s): could not read section headers\n",
295 : ncf->fname);
296 0 : return -1;
297 : }
298 : }
299 :
300 : /* success! */
301 6 : return 0;
302 : }
303 :
304 : ncfile *nc_loadfile_depending(const char *filename,
305 6 : nc_loadfile_error_fn error_fn) {
306 : ncfile *ncf;
307 : int fd;
308 6 : int rdflags = O_RDONLY | _O_BINARY;
309 6 : fd = OPEN(filename, rdflags);
310 6 : if (fd < 0) return NULL;
311 :
312 : /* Allocate the ncfile structure */
313 6 : ncf = calloc(1, sizeof(ncfile));
314 6 : if (ncf == NULL) return NULL;
315 6 : ncf->size = 0;
316 6 : ncf->data = NULL;
317 6 : ncf->fname = filename;
318 6 : if (error_fn == NULL) {
319 6 : ncf->error_fn = NcLoadFilePrintError;
320 : } else {
321 0 : ncf->error_fn = error_fn;
322 : }
323 :
324 6 : if (nc_load(ncf, fd) < 0) {
325 0 : close(fd);
326 0 : free(ncf);
327 0 : return NULL;
328 : }
329 6 : close(fd);
330 6 : return ncf;
331 : }
332 :
333 6 : ncfile *nc_loadfile(const char *filename) {
334 6 : return nc_loadfile_depending(filename, NULL);
335 : }
336 :
337 : ncfile *nc_loadfile_with_error_fn(const char *filename,
338 0 : nc_loadfile_error_fn error_fn) {
339 0 : return nc_loadfile_depending(filename, error_fn);
340 : }
341 :
342 :
343 6 : void nc_freefile(ncfile *ncf) {
344 6 : if (ncf->data != NULL) free(ncf->data);
345 6 : free(ncf);
346 6 : }
347 :
348 : /***********************************************************************/
349 :
350 : void GetVBaseAndLimit(ncfile *ncf, NaClPcAddress *vbase,
351 6 : NaClPcAddress *vlimit) {
352 : int ii;
353 : /* TODO(karl) - Define so constant applies to 64-bit pc address. */
354 6 : NaClPcAddress base = 0xffffffff;
355 6 : NaClPcAddress limit = 0;
356 :
357 145 : for (ii = 0; ii < ncf->shnum; ii++) {
358 139 : if ((ncf->sheaders[ii].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR) {
359 14 : if (ncf->sheaders[ii].sh_addr < base) base = ncf->sheaders[ii].sh_addr;
360 14 : if (ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size > limit)
361 14 : limit = ncf->sheaders[ii].sh_addr + ncf->sheaders[ii].sh_size;
362 : }
363 : }
364 6 : *vbase = base;
365 6 : *vlimit = limit;
366 6 : }
|