Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: elf_loader.cc

Issue 7634024: Add an ELF loader that can load and run an executable in the sandbox (Closed) Base URL: https://seccompsandbox.googlecode.com/svn/trunk
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | makefile » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <elf.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11
12 #include "sandbox_impl.h"
13 #include "syscall_entrypoint.h"
14 #include "tls_setup.h"
15
16
17 #if defined(__x86_64__)
18 # define ElfW(name) Elf64_##name
19 # define ElfW_ELFCLASS ELFCLASS64
20 #elif defined(__i386__)
21 # define ElfW(name) Elf32_##name
22 # define ElfW_ELFCLASS ELFCLASS32
23 #else
24 # error "Unsupported target platform"
Markus (顧孟勤) 2011/08/15 22:05:23 I keep making this same mistake all the time. Ther
25 #endif
26
27
28 uintptr_t PageSizeRoundDown(uintptr_t val) {
Markus (顧孟勤) 2011/08/15 22:05:23 Are you deliberately not making these functions "s
29 return val & ~(getpagesize() - 1);
Markus (顧孟勤) 2011/08/15 22:05:23 You can also write: val & -getpagesize() Not su
30 }
31
32 uintptr_t PageSizeRoundUp(uintptr_t val) {
33 return (val + getpagesize() - 1) & ~(getpagesize() - 1);
Markus (顧孟勤) 2011/08/15 22:05:23 Maybe: return PageSizeRoundDown(val + getpagesi
34 }
35
36 static ElfW(auxv_t) *FindAuxv(int argc, char **argv) {
37 char **ptr = argv + argc + 1;
38 // Skip over envp.
39 while (*ptr != NULL)
40 ptr++;
Markus (顧孟勤) 2011/08/15 22:05:23 I'd prefer "{ }" around this statement. But maybe,
41 ptr++;
42 return (ElfW(auxv_t) *) ptr;
43 }
44
45 static void SetAuxvField(ElfW(auxv_t) *auxv, unsigned type, uintptr_t value) {
46 for (; auxv->a_type != AT_NULL; auxv++) {
47 if (auxv->a_type == type) {
48 auxv->a_un.a_val = value;
49 return;
50 }
51 }
Markus (顧孟勤) 2011/08/15 22:05:23 Is it OK, if we fail to find the matching type? Sh
52 }
53
54 static void JumpToElfEntryPoint(void *stack, void *entry_point,
55 void *atexit_func) {
Markus (顧孟勤) 2011/08/15 22:05:23 You might consider declaring this function as __at
56 #if defined(__x86_64__)
57 asm("mov %0, %%rsp\n"
58 "jmp *%1\n"
59 // %edx is registered as an atexit handler if non-zero.
60 : : "r"(stack), "r"(entry_point), "d"(atexit_func));
61 #elif defined(__i386__)
62 asm("mov %0, %%esp\n"
63 "jmp *%1\n"
64 // %rdx is registered as an atexit handler if non-zero.
65 : : "r"(stack), "r"(entry_point), "d"(atexit_func));
66 #else
67 # error "Unsupported target platform"
68 #endif
69 }
70
71 static void *LoadElfObject(int fd, ElfW(auxv_t) *auxv) {
72 // Load and check headers.
73 ElfW(Ehdr) elf_header;
74 if (pread(fd, &elf_header, sizeof(elf_header), 0) != sizeof(elf_header)) {
75 Sandbox::die("Failed to read ELF header");
76 }
77 if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
78 Sandbox::die("Not an ELF file");
79 }
80 if (elf_header.e_ident[EI_CLASS] != ElfW_ELFCLASS) {
81 Sandbox::die("Unexpected ELFCLASS");
82 }
83 ssize_t phdrs_size = elf_header.e_phentsize * elf_header.e_phnum;
Markus (顧孟勤) 2011/08/15 22:05:23 Are we worried about malicious binaries overflowin
84 char *phdrs = (char *) alloca(phdrs_size);
Markus (顧孟勤) 2011/08/15 22:05:23 You should check that the alloca() request succeed
85 if (pread(fd, phdrs, phdrs_size, elf_header.e_phoff) != phdrs_size) {
86 Sandbox::die("Failed to read ELF program headers");
87 }
88
89 // Scan program headers to find the overall size of the ELF object.
90 size_t load_size = 0;
91 char *required_start = 0;
92 for (int index = 0; index < elf_header.e_phnum; index++) {
93 ElfW(Phdr) *segment =
94 (ElfW(Phdr) *) (phdrs + elf_header.e_phentsize * index);
95 if (segment->p_type == PT_LOAD) {
96 size_t segment_end = PageSizeRoundUp(segment->p_vaddr + segment->p_memsz);
97 if (load_size < segment_end) {
98 load_size = segment_end;
99 }
100 if (elf_header.e_type == ET_EXEC) {
Markus (顧孟勤) 2011/08/15 22:05:23 I take it, we should see no or exactly one ET_EXEC
101 required_start = (char *) PageSizeRoundDown(segment->p_vaddr);
102 }
103 }
104 }
105
Markus (顧孟勤) 2011/08/15 22:05:23 You might want to add a sanity check that "load_si
106 // Reserve address space.
107 // For executables that must be loaded at a fixed address, we could
108 // use MAP_FIXED, but if the address range is already occupied then
109 // that will clobber the existing mappings without warning, which is
110 // bad. Instead, use an address hint and check that we got the
111 // expected address.
112 char *base_addr = (char *) mmap(required_start, load_size, PROT_NONE,
113 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
114 if (base_addr == MAP_FAILED) {
115 Sandbox::die("Failed to reserve address space");
116 }
117 if (required_start != NULL && base_addr != required_start) {
118 Sandbox::die("Failed to reserve address space at fixed address");
119 }
120
121 char *load_offset = (char *) (base_addr - required_start);
122 char *entry_point = load_offset + elf_header.e_entry;
123 SetAuxvField(auxv, AT_ENTRY, (uintptr_t) entry_point);
124 SetAuxvField(auxv, AT_BASE, (uintptr_t) load_offset);
125 SetAuxvField(auxv, AT_PHNUM, elf_header.e_phnum);
126 SetAuxvField(auxv, AT_PHENT, elf_header.e_phentsize);
127 // Note that this assumes that the program headers are included in a
128 // PT_LOAD segment for which the file offsets matches the mapping
129 // offset, but Linux assumes this too when setting AT_PHDR.
Markus (顧孟勤) 2011/08/15 22:05:23 Any chance that we could actually test this assump
130 SetAuxvField(auxv, AT_PHDR, (uintptr_t) base_addr + elf_header.e_phoff);
131
132 for (int index = 0; index < elf_header.e_phnum; index++) {
133 ElfW(Phdr) *segment =
134 (ElfW(Phdr) *) (phdrs + elf_header.e_phentsize * index);
135 if (segment->p_type == PT_LOAD) {
136 uintptr_t segment_start = PageSizeRoundDown(segment->p_vaddr);
137 size_t segment_end = PageSizeRoundUp(segment->p_vaddr + segment->p_memsz);
Markus (顧孟勤) 2011/08/15 22:05:23 Again, do we worry about overflows?
138 int prot = 0;
139 if ((segment->p_flags & PF_R) != 0)
140 prot |= PROT_READ;
141 if ((segment->p_flags & PF_W) != 0)
142 prot |= PROT_WRITE;
143 if ((segment->p_flags & PF_X) != 0)
144 prot |= PROT_EXEC;
145 void *result = mmap(load_offset + segment_start,
Markus (顧孟勤) 2011/08/15 22:05:23 Don't we have the same problem as for the other ca
146 segment_end - segment_start,
147 prot, MAP_PRIVATE | MAP_FIXED, fd,
148 PageSizeRoundDown(segment->p_offset));
149 if (result == MAP_FAILED) {
150 Sandbox::die("Failed to map ELF segment");
151 }
152 // TODO(mseaborn): Support a BSS that goes beyond the file's extent.
Markus (顧孟勤) 2011/08/15 22:05:23 What happens if we encounter something like that?
153 if ((segment->p_flags & PF_W) != 0) {
154 // Zero the BSS to the end of the page.
155 uintptr_t bss_start = segment->p_vaddr + segment->p_filesz;
Markus (顧孟勤) 2011/08/15 22:05:23 Do we need sanity checks that we don't zero out so
156 memset(load_offset + bss_start, 0, segment_end - bss_start);
157 }
158 }
159 }
160 if (close(fd) != 0) {
161 Sandbox::die("close() failed");
162 }
163 return entry_point;
164 }
165
166 int main(int argc, char **argv) {
167 if (argc < 2) {
168 fprintf(stderr, "Usage: %s executable args...\n", argv[0]);
169 return 1;
170 }
171
172 const char *executable_filename = argv[1];
173 int executable_fd = open(executable_filename, O_RDONLY);
174 if (executable_fd < 0) {
175 fprintf(stderr, "Failed to open executable %s: %s\n",
176 executable_filename, strerror(errno));
177 return 1;
178 }
179
180 playground::g_policy.allow_file_namespace = true;
Markus (顧孟勤) 2011/08/15 22:05:23 We probably want a more fine-grained policy at som
181 playground::AddTlsSetupSyscall();
182 StartSeccompSandbox();
183
184 ElfW(auxv_t) *auxv = FindAuxv(argc, argv);
185 SetAuxvField(auxv, AT_SYSINFO, (uintptr_t) syscallEntryPointNoFrame);
Markus (顧孟勤) 2011/08/15 22:05:23 Does this work on both x86-32 and x86-64. I vaguel
186 char **stack = argv;
187 *(long *) stack = argc - 1;
Markus (顧孟勤) 2011/08/15 22:05:23 Are there alignment requirements that we need to w
188 void *entry_point = LoadElfObject(executable_fd, auxv);
189 JumpToElfEntryPoint(stack, entry_point, 0);
190 }
OLDNEW
« no previous file with comments | « no previous file | makefile » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698