blob: 4cfa8eb54ca074e4ca552c7cbd111b7d23fd4813 [file] [log] [blame]
jseward2886b0e2004-01-04 03:46:11 +00001
nethercotebb1c9912004-01-04 16:43:23 +00002/*--------------------------------------------------------------------*/
3/*--- User-mode execve() ume.c ---*/
4/*--------------------------------------------------------------------*/
5
jseward2886b0e2004-01-04 03:46:11 +00006/*
7 This file is part of Valgrind, an extensible x86 protected-mode
8 emulator for monitoring program execution on x86-Unixes.
9
10 Copyright (C) 2000-2004 Julian Seward
11 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
fitzhardinge7e343cd2003-12-16 02:14:00 +000031
32#define _GNU_SOURCE
33#define _FILE_OFFSET_BITS 64
34
35#include "vg_include.h"
36
37#include <stddef.h>
38#include <sys/mman.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <elf.h>
42#include <stdio.h>
43#include <string.h>
44#include <stdlib.h>
45#include <unistd.h>
fitzhardinge7e343cd2003-12-16 02:14:00 +000046#include <sys/stat.h>
fitzhardinge7e343cd2003-12-16 02:14:00 +000047#include <dlfcn.h>
48#include <assert.h>
49
50#include "ume.h"
fitzhardingeb50068f2004-02-24 23:42:55 +000051#include "vg_include.h"
fitzhardinge7e343cd2003-12-16 02:14:00 +000052
53static int padfile = -1;
54static struct stat padstat;
55
56extern int kickstart_base; /* linker created */
57
nethercotebfed1c82004-07-17 12:57:44 +000058void check_mmap(void* res, void* base, int len)
59{
60 if ((void*)-1 == res) {
61 fprintf(stderr, "valgrind: mmap(%p, %d) failed during startup.\n"
62 "valgrind: is there a hard virtual memory limit set?\n",
63 base, len);
64 exit(1);
65 }
66}
67
fitzhardinge7e343cd2003-12-16 02:14:00 +000068void foreach_map(int (*fn)(void *start, void *end,
69 const char *perm, off_t offset,
70 int maj, int min, int ino))
71{
72 static char buf[10240];
73 char *bufptr = buf;
74 int ret, fd;
75
76 fd = open("/proc/self/maps", O_RDONLY);
77
78 if (fd == -1) {
79 perror("open /proc/self/maps");
80 return;
81 }
82
83 ret = read(fd, buf, sizeof(buf));
84
85 if (ret == -1) {
86 perror("read /proc/self/maps");
87 close(fd);
88 return;
89 }
90 close(fd);
91
92 if (ret == sizeof(buf)) {
93 fprintf(stderr, "buf too small\n");
94 return;
95 }
96
97 while(bufptr && bufptr < buf+ret) {
98 char perm[5];
99 off_t offset;
100 int maj, min;
101 int ino;
102 void *segstart, *segend;
103
104 sscanf(bufptr, "%p-%p %s %Lx %x:%x %d",
105 &segstart, &segend, perm, &offset, &maj, &min, &ino);
106 bufptr = strchr(bufptr, '\n');
107 if (bufptr != NULL)
108 bufptr++; /* skip \n */
109
110 if (!(*fn)(segstart, segend, perm, offset, maj, min, ino))
111 break;
112 }
113}
114
thughes4ad52d02004-06-27 17:37:21 +0000115static char *fillgap_addr;
116static char *fillgap_end;
117
118static int fillgap(void *segstart, void *segend, const char *perm, off_t off,
119 int maj, int min, int ino) {
120 if ((char *)segstart >= fillgap_end)
121 return 0;
122
nethercotebfed1c82004-07-17 12:57:44 +0000123 if ((char *)segstart > fillgap_addr) {
124 void* res = mmap(fillgap_addr, (char *)segstart-fillgap_addr, PROT_NONE,
125 MAP_FIXED|MAP_PRIVATE, padfile, 0);
126 check_mmap(res, fillgap_addr, (char*)segstart - fillgap_addr);
127 }
thughes4ad52d02004-06-27 17:37:21 +0000128 fillgap_addr = segend;
129
130 return 1;
131}
132
fitzhardinge7e343cd2003-12-16 02:14:00 +0000133/* pad all the empty spaces in a range of address space to stop
134 interlopers */
135void as_pad(void *start, void *end)
136{
137 char buf[1024];
fitzhardinge7e343cd2003-12-16 02:14:00 +0000138
139 if (padfile == -1) {
140 int seq = 1;
141 do {
142 sprintf(buf, "/tmp/.pad.%d.%d", getpid(), seq++);
143 padfile = open(buf, O_RDWR|O_CREAT|O_EXCL, 0);
144 unlink(buf);
145 if (padfile == -1 && errno != EEXIST)
146 exit(44);
147 } while(padfile == -1);
148 fstat(padfile, &padstat);
149 }
150
thughes4ad52d02004-06-27 17:37:21 +0000151 fillgap_addr = start;
152 fillgap_end = end;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000153
154 foreach_map(fillgap);
155
nethercotebfed1c82004-07-17 12:57:44 +0000156 if (fillgap_addr < fillgap_end) {
157 void* res = mmap(fillgap_addr, fillgap_end-fillgap_addr, PROT_NONE,
158 MAP_FIXED|MAP_PRIVATE, padfile, 0);
159 check_mmap(res, fillgap_addr, fillgap_end - fillgap_addr);
160 }
thughes4ad52d02004-06-27 17:37:21 +0000161}
162
163static void *killpad_start;
164static void *killpad_end;
165
166static int killpad(void *segstart, void *segend, const char *perm, off_t off,
nethercotebfed1c82004-07-17 12:57:44 +0000167 int maj, int min, int ino)
168{
thughes4ad52d02004-06-27 17:37:21 +0000169 void *b, *e;
nethercotebfed1c82004-07-17 12:57:44 +0000170 int res;
thughes4ad52d02004-06-27 17:37:21 +0000171
172 if (padstat.st_dev != makedev(maj, min) || padstat.st_ino != ino)
173 return 1;
174
175 if (segend <= killpad_start || segstart >= killpad_end)
176 return 1;
177
178 if (segstart <= killpad_start)
179 b = killpad_start;
180 else
181 b = segstart;
182
183 if (segend >= killpad_end)
184 e = killpad_end;
185 else
186 e = segend;
187
nethercotebfed1c82004-07-17 12:57:44 +0000188 res = munmap(b, (char *)e-(char *)b);
189 assert(0 == res);
thughes4ad52d02004-06-27 17:37:21 +0000190
191 return 1;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000192}
193
194/* remove padding from a range of address space - padding is always a
195 mapping of padfile*/
196void as_unpad(void *start, void *end)
197{
fitzhardinge7e343cd2003-12-16 02:14:00 +0000198 if (padfile == -1) /* no padfile, no padding */
199 return;
thughes4ad52d02004-06-27 17:37:21 +0000200
201 killpad_start = start;
202 killpad_end = end;
203
fitzhardinge7e343cd2003-12-16 02:14:00 +0000204 foreach_map(killpad);
205}
206
207void as_closepadfile(void)
208{
209 /* don't unpad */
210 close(padfile);
211 padfile = -1;
212}
213
214int as_getpadfd(void)
215{
216 return padfile;
217}
218
219void as_setpadfd(int fd)
220{
221 as_closepadfile();
222 padfile = fd;
223 fstat(padfile, &padstat);
224}
225
226struct ume_auxv *find_auxv(int *esp)
227{
228 esp++; /* skip argc */
229
230 while(*esp != 0) /* skip argv */
231 esp++;
232 esp++;
233
234 while(*esp != 0) /* skip env */
235 esp++;
236 esp++;
237
238 return (struct ume_auxv *)esp;
239}
240
241
242struct elfinfo *readelf(int fd, const char *filename)
243{
244 struct elfinfo *e = malloc(sizeof(*e));
245 int phsz;
246
247 e->fd = fd;
248
249 if (pread(fd, &e->e, sizeof(e->e), 0) != sizeof(e->e)) {
250 fprintf(stderr, "%s: can't read elf header: %s\n",
251 filename, strerror(errno));
252 return NULL;
253 }
254
255 if (memcmp(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) {
256 fprintf(stderr, "%s: bad ELF magic\n",
257 filename);
258 return NULL;
259 }
260 if (e->e.e_ident[EI_CLASS] != ELFCLASS32) {
261 fprintf(stderr, "Can only handle 32-bit executables\n");
262 return NULL;
263 }
264 if (e->e.e_ident[EI_DATA] != ELFDATA2LSB) {
265 fprintf(stderr, "Expecting little-endian\n");
266 return NULL;
267 }
268 if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) {
269 fprintf(stderr, "need executable\n");
270 return NULL;
271 }
272
273 if (e->e.e_machine != EM_386) {
274 fprintf(stderr, "need x86\n");
275 return NULL;
276 }
277
278 if (e->e.e_phentsize != sizeof(ESZ(Phdr))) {
279 fprintf(stderr, "sizeof Phdr wrong\n");
280 return NULL;
281 }
282
283 phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum;
284 e->p = malloc(phsz);
285
286 if (pread(fd, e->p, phsz, e->e.e_phoff) != phsz) {
287 fprintf(stderr, "can't read phdr: %s\n", strerror(errno));
288 return NULL;
289 }
290
291 return e;
292}
293
294#define REMAINS(x, a) ((x) & ((a)-1))
295
296/* Map an ELF file. Returns the brk address. */
fitzhardingeb50068f2004-02-24 23:42:55 +0000297ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base)
fitzhardinge7e343cd2003-12-16 02:14:00 +0000298{
299 int i;
nethercotebfed1c82004-07-17 12:57:44 +0000300 void* res;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000301 ESZ(Addr) elfbrk = 0;
302
303 for(i = 0; i < e->e.e_phnum; i++) {
304 ESZ(Phdr) *ph = &e->p[i];
305 ESZ(Addr) addr, brkaddr;
306 ESZ(Word) memsz;
307
308 if (ph->p_type != PT_LOAD)
309 continue;
310
311 addr = ph->p_vaddr+base;
312 memsz = ph->p_memsz;
313 brkaddr = addr+memsz;
314
315 if (brkaddr > elfbrk)
316 elfbrk = brkaddr;
317 }
318
fitzhardinge7e343cd2003-12-16 02:14:00 +0000319 for(i = 0; i < e->e.e_phnum; i++) {
320 ESZ(Phdr) *ph = &e->p[i];
321 ESZ(Addr) addr, bss, brkaddr;
322 ESZ(Off) off;
323 ESZ(Word) filesz;
324 ESZ(Word) memsz;
325 ESZ(Word) align;
326 unsigned prot = 0;
327
328 if (ph->p_type != PT_LOAD)
329 continue;
330
331 if (ph->p_flags & PF_X)
332 prot |= PROT_EXEC;
333 if (ph->p_flags & PF_W)
334 prot |= PROT_WRITE;
335 if (ph->p_flags & PF_R)
336 prot |= PROT_READ;
337
338 align = ph->p_align;
339
340 addr = ph->p_vaddr+base;
341 off = ph->p_offset;
342 filesz = ph->p_filesz;
343 bss = addr+filesz;
344 memsz = ph->p_memsz;
345 brkaddr = addr+memsz;
346
nethercotebfed1c82004-07-17 12:57:44 +0000347 res = mmap((char *)ROUNDDN(addr, align),
348 ROUNDUP(bss, align)-ROUNDDN(addr, align),
349 prot, MAP_FIXED|MAP_PRIVATE, e->fd, ROUNDDN(off, align));
350 check_mmap(res, (char*)ROUNDDN(addr,align),
351 ROUNDUP(bss, align)-ROUNDDN(addr, align));
fitzhardinge7e343cd2003-12-16 02:14:00 +0000352
353 /* if memsz > filesz, then we need to fill the remainder with zeroed pages */
354 if (memsz > filesz) {
355 UInt bytes;
356
357 bytes = ROUNDUP(brkaddr, align)-ROUNDUP(bss, align);
nethercotebfed1c82004-07-17 12:57:44 +0000358 if (bytes > 0) {
359 res = mmap((char *)ROUNDUP(bss, align), bytes,
360 prot, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
361 check_mmap(res, (char*)ROUNDUP(bss,align), bytes);
362 }
fitzhardinge7e343cd2003-12-16 02:14:00 +0000363
364 bytes = bss & (VKI_BYTES_PER_PAGE - 1);
365 if (bytes > 0) {
366 bytes = VKI_BYTES_PER_PAGE - bytes;
367 memset((char *)bss, 0, bytes);
368 }
369 }
370 }
371
372 return elfbrk;
373}
374
375
376static int do_exec_inner(const char *exe, struct exeinfo *info);
377
378
379static int match_ELF(const char *hdr, int len)
380{
381 ESZ(Ehdr) *e = (ESZ(Ehdr) *)hdr;
382 return (len > sizeof(*e)) && memcmp(&e->e_ident[0], ELFMAG, SELFMAG) == 0;
383}
384
385static int load_ELF(char *hdr, int len, int fd, const char *name, struct exeinfo *info)
386{
387 struct elfinfo *e;
388 struct elfinfo *interp = NULL;
389 ESZ(Addr) minaddr = ~0;
390 ESZ(Addr) maxaddr = 0;
391 ESZ(Addr) interp_addr = 0;
392 ESZ(Word) interp_size = 0;
393 int i;
394 void *entry;
395
396 e = readelf(fd, name);
397
398 if (e == NULL)
399 return ENOEXEC;
400
401 info->phnum = e->e.e_phnum;
402 info->entry = e->e.e_entry;
403
404 for(i = 0; i < e->e.e_phnum; i++) {
405 ESZ(Phdr) *ph = &e->p[i];
406
407 switch(ph->p_type) {
408 case PT_PHDR:
409 info->phdr = ph->p_vaddr;
410 break;
411
412 case PT_LOAD:
413 if (ph->p_vaddr < minaddr)
414 minaddr = ph->p_vaddr;
415 if (ph->p_vaddr+ph->p_memsz > maxaddr)
416 maxaddr = ph->p_vaddr+ph->p_memsz;
417 break;
418
419 case PT_INTERP: {
420 char *buf = malloc(ph->p_filesz+1);
421 int j;
422 int intfd;
423 int baseaddr_set;
424
425 pread(fd, buf, ph->p_filesz, ph->p_offset);
426 buf[ph->p_filesz] = '\0';
427
428 intfd = open(buf, O_RDONLY);
429 if (intfd == -1) {
430 perror("open interp");
431 exit(1);
432 }
433
434 interp = readelf(intfd, buf);
435 if (interp == NULL) {
436 fprintf(stderr, "Can't read interpreter\n");
437 return 1;
438 }
439 free(buf);
440
441 baseaddr_set = 0;
442 for(j = 0; j < interp->e.e_phnum; j++) {
443 ESZ(Phdr) *iph = &interp->p[j];
444 ESZ(Addr) end;
445
446 if (iph->p_type != PT_LOAD)
447 continue;
448
449 if (!baseaddr_set) {
450 interp_addr = iph->p_vaddr;
451 baseaddr_set = 1;
452 }
453
454 /* assumes that all segments in the interp are close */
455 end = (iph->p_vaddr - interp_addr) + iph->p_memsz;
456
457 if (end > interp_size)
458 interp_size = end;
459 }
460 break;
461 }
462 }
463 }
464
465 if (info->exe_base != info->exe_end) {
466 if (minaddr >= maxaddr ||
467 (minaddr < info->exe_base ||
468 maxaddr > info->exe_end)) {
469 fprintf(stderr, "Executable is mapped outside of range %p-%p\n",
470 (void *)info->exe_base, (void *)info->exe_end);
471 return ENOMEM;
472 }
473 }
474
fitzhardingeb50068f2004-02-24 23:42:55 +0000475 info->brkbase = mapelf(e, 0); /* map the executable */
fitzhardinge7e343cd2003-12-16 02:14:00 +0000476
fitzhardinge92360792003-12-24 10:11:11 +0000477 if (info->brkbase == 0)
478 return ENOMEM;
479
fitzhardinge7e343cd2003-12-16 02:14:00 +0000480 if (interp != NULL) {
481 /* reserve a chunk of address space for interpreter */
nethercotebfed1c82004-07-17 12:57:44 +0000482 void* res;
483 char* base = (char *)info->exe_base;
484 char* baseoff;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000485 int flags = MAP_PRIVATE|MAP_ANONYMOUS;
486
487 if (info->map_base != 0) {
488 base = (char *)info->map_base;
489 flags |= MAP_FIXED;
490 }
491
nethercotebfed1c82004-07-17 12:57:44 +0000492 res = mmap(base, interp_size, PROT_NONE, flags, -1, 0);
493 check_mmap(res, base, interp_size);
494 base = res;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000495
496 baseoff = base - interp_addr;
497
fitzhardingeb50068f2004-02-24 23:42:55 +0000498 mapelf(interp, (ESZ(Addr))baseoff);
fitzhardinge7e343cd2003-12-16 02:14:00 +0000499
500 close(interp->fd);
501 free(interp);
502
503 entry = baseoff + interp->e.e_entry;
504 info->interp_base = (ESZ(Addr))base;
505 } else
506 entry = (void *)e->e.e_entry;
507
508 info->exe_base = minaddr;
509 info->exe_end = maxaddr;
510
511 info->init_eip = (addr_t)entry;
512
513 free(e);
514
515 return 0;
516}
517
518
519static int match_script(const char *hdr, Int len)
520{
521 return (len > 2) && memcmp(hdr, "#!", 2) == 0;
522}
523
524static int load_script(char *hdr, int len, int fd, const char *name, struct exeinfo *info)
525{
526 char *interp;
527 char *const end = hdr+len;
528 char *cp;
529 char *arg = NULL;
530 int eol;
531
532 interp = hdr + 2;
533 while(interp < end && (*interp == ' ' || *interp == '\t'))
534 interp++;
535
536 if (*interp != '/')
537 return ENOEXEC; /* absolute path only for interpreter */
538
539 /* skip over interpreter name */
540 for(cp = interp; cp < end && *cp != ' ' && *cp != '\t' && *cp != '\n'; cp++)
541 ;
542
543 eol = (*cp == '\n');
544
545 *cp++ = '\0';
546
547 if (!eol && cp < end) {
548 /* skip space before arg */
549 while (cp < end && (*cp == '\t' || *cp == ' '))
550 cp++;
551
552 /* arg is from here to eol */
553 arg = cp;
554 while (cp < end && *cp != '\n')
555 cp++;
556 *cp = '\0';
557 }
558
559 info->argv0 = strdup(interp);
nethercote71980f02004-01-24 18:18:54 +0000560 assert(NULL != info->argv0);
561 if (arg != NULL && *arg != '\0') {
fitzhardinge7e343cd2003-12-16 02:14:00 +0000562 info->argv1 = strdup(arg);
nethercote71980f02004-01-24 18:18:54 +0000563 assert(NULL != info->argv1);
564 }
fitzhardinge7e343cd2003-12-16 02:14:00 +0000565
566 if (info->argv && info->argv[0] != NULL)
567 info->argv[0] = (char *)name;
568
569 if (0)
570 printf("#! script: argv0=\"%s\" argv1=\"%s\"\n",
571 info->argv0, info->argv1);
572
573 return do_exec_inner(interp, info);
574}
575
576struct binfmt {
577 int (*match)(const char *hdr, int len);
578 int (*load) ( char *hdr, int len, int fd, const char *name, struct exeinfo *);
579};
580
581static const struct binfmt formats[] = {
582 { match_ELF, load_ELF },
583 { match_script, load_script },
584};
585
586
587static int do_exec_inner(const char *exe, struct exeinfo *info)
588{
589 int fd;
590 char buf[VKI_BYTES_PER_PAGE];
591 int bufsz;
592 int i;
593 int ret;
594 struct stat st;
595
596 fd = open(exe, O_RDONLY);
597 if (fd == -1) {
598 if (0)
599 fprintf(stderr, "Can't open executable %s: %s\n",
600 exe, strerror(errno));
601 return errno;
602 }
603
604 if (fstat(fd, &st) == -1)
605 return errno;
606 else {
607 uid_t uid = geteuid();
608 gid_t gid = getegid();
609 gid_t groups[32];
610 int ngrp = getgroups(32, groups);
611
fitzhardingea49f9b52003-12-16 22:26:45 +0000612 if (st.st_mode & (S_ISUID | S_ISGID)) {
613 fprintf(stderr, "Can't execute suid/sgid executable %s\n", exe);
614 return EACCES;
615 }
616
fitzhardinge6d5dafa2004-01-04 23:10:07 +0000617 if (uid == st.st_uid) {
618 if (!(st.st_mode & S_IXUSR))
fitzhardinge7e343cd2003-12-16 02:14:00 +0000619 return EACCES;
fitzhardinge6d5dafa2004-01-04 23:10:07 +0000620 } else {
621 int grpmatch = 0;
fitzhardinge7e343cd2003-12-16 02:14:00 +0000622
fitzhardinge6d5dafa2004-01-04 23:10:07 +0000623 if (gid == st.st_gid)
624 grpmatch = 1;
625 else
626 for(i = 0; i < ngrp; i++)
627 if (groups[i] == st.st_gid) {
628 grpmatch = 1;
629 break;
630 }
631
632 if (grpmatch) {
633 if (!(st.st_mode & S_IXGRP))
634 return EACCES;
635 } else if (!(st.st_mode & S_IXOTH))
636 return EACCES;
637 }
fitzhardinge7e343cd2003-12-16 02:14:00 +0000638 }
639
640 bufsz = pread(fd, buf, sizeof(buf), 0);
641 if (bufsz < 0) {
642 fprintf(stderr, "Can't read executable header: %s\n",
643 strerror(errno));
644 close(fd);
645 return errno;
646 }
647
648 ret = ENOEXEC;
649 for(i = 0; i < sizeof(formats)/sizeof(*formats); i++) {
650 if ((formats[i].match)(buf, bufsz)) {
651 ret = (formats[i].load)(buf, bufsz, fd, exe, info);
652 break;
653 }
654 }
655
656 close(fd);
657
658 return ret;
659}
660
661int do_exec(const char *exe, struct exeinfo *info)
662{
663 info->argv0 = NULL;
664 info->argv1 = NULL;
665
666 return do_exec_inner(exe, info);
667}
nethercotebb1c9912004-01-04 16:43:23 +0000668
669/*--------------------------------------------------------------------*/
670/*--- end ume.c ---*/
671/*--------------------------------------------------------------------*/