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

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: Fixes + review changes Created 9 years, 1 month 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 # define ElfW_EXPECTED_MACHINE EM_X86_64
21 #elif defined(__i386__)
22 # define ElfW(name) Elf32_##name
23 # define ElfW_ELFCLASS ELFCLASS32
24 # define ElfW_EXPECTED_MACHINE EM_386
25 #else
26 # error Unsupported target platform
27 #endif
28
29
30 static uintptr_t PageSizeRoundDown(uintptr_t val) {
31 return val & ~(getpagesize() - 1);
32 }
33
34 static uintptr_t PageSizeRoundUp(uintptr_t val) {
35 return PageSizeRoundDown(val + getpagesize() - 1);
36 }
37
38 static ElfW(auxv_t) *FindAuxv(int argc, char **argv) {
39 char **ptr = argv + argc + 1;
40 // Skip over envp.
41 while (*ptr != NULL) {
42 ptr++;
43 }
44 ptr++;
45 return (ElfW(auxv_t) *) ptr;
46 }
47
48 static void SetAuxvField(ElfW(auxv_t) *auxv, unsigned type, uintptr_t value) {
49 for (; auxv->a_type != AT_NULL; auxv++) {
50 if (auxv->a_type == type) {
51 auxv->a_un.a_val = value;
52 return;
53 }
54 }
55 }
56
57 static void JumpToElfEntryPoint(void *stack, void *entry_point,
58 void *atexit_func) {
59 #if defined(__x86_64__)
60 asm("mov %0, %%rsp\n"
61 "jmp *%1\n"
62 // %edx is registered as an atexit handler if non-zero.
63 : : "r"(stack), "r"(entry_point), "d"(atexit_func));
64 #elif defined(__i386__)
65 asm("mov %0, %%esp\n"
66 "jmp *%1\n"
67 // %rdx is registered as an atexit handler if non-zero.
68 : : "r"(stack), "r"(entry_point), "d"(atexit_func));
69 #else
70 # error Unsupported target platform
71 #endif
72 }
73
74 static void *LoadElfObject(int fd, ElfW(auxv_t) *auxv) {
75 // Load and check headers.
76 ElfW(Ehdr) elf_header;
77 if (pread(fd, &elf_header, sizeof(elf_header), 0) != sizeof(elf_header)) {
78 Sandbox::die("Failed to read ELF header");
79 }
80 if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
81 Sandbox::die("Not an ELF file");
82 }
83 if (elf_header.e_ident[EI_CLASS] != ElfW_ELFCLASS) {
84 Sandbox::die("Unexpected ELFCLASS");
85 }
86 if (elf_header.e_machine != ElfW_EXPECTED_MACHINE) {
87 Sandbox::die("Unexpected ELF machine type");
88 }
89 if (elf_header.e_phentsize != sizeof(ElfW(Phdr))) {
90 Sandbox::die("Unexpected ELF program header entry size");
91 }
92 if (elf_header.e_phnum >= 20) {
93 // We impose an arbitrary limit as a sanity check and to avoid
94 // overflowing the stack.
95 Sandbox::die("Too many ELF program headers");
96 }
97 ElfW(Phdr) phdrs[elf_header.e_phnum];
98 if (pread(fd, phdrs, sizeof(phdrs), elf_header.e_phoff)
99 != (ssize_t) sizeof(phdrs)) {
100 Sandbox::die("Failed to read ELF program headers");
101 }
102
103 // Scan program headers to find the overall size of the ELF object.
104 // Find the first and last PT_LOAD segments. ELF requires that
105 // PT_LOAD segments be in ascending order of p_vaddr, so we can use
106 // the last one to calculate the whole address span of the image.
107 size_t index = 0;
108 while (index < elf_header.e_phnum && phdrs[index].p_type != PT_LOAD) {
109 index++;
110 }
111 if (index == elf_header.e_phnum) {
112 Sandbox::die("ELF object contains no PT_LOAD headers");
113 }
114 ElfW(Phdr) *first_segment = &phdrs[index];
115 ElfW(Phdr) *last_segment = &phdrs[elf_header.e_phnum - 1];
116 while (last_segment > first_segment && last_segment->p_type != PT_LOAD) {
117 last_segment--;
118 }
119 uintptr_t overall_start = PageSizeRoundDown(first_segment->p_vaddr);
120 uintptr_t overall_end = PageSizeRoundUp(last_segment->p_vaddr
121 + last_segment->p_memsz);
122 uintptr_t overall_size = overall_end - overall_start;
123
124 // Reserve address space.
125 // Executables that must be loaded at a fixed address have an e_type
126 // of ET_EXEC. For these, we could use MAP_FIXED, but if the
127 // address range is already occupied then that will clobber the
128 // existing mappings without warning, which is bad. Instead, use an
129 // address hint and check that we got the expected address.
130 // Executables that can be loaded at any address have an e_type of
131 // ET_DYN.
132 char *required_start =
133 elf_header.e_type == ET_EXEC ? (char *) overall_start : NULL;
134 char *base_addr = (char *) mmap(required_start, overall_size, PROT_NONE,
135 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
136 if (base_addr == MAP_FAILED) {
137 Sandbox::die("Failed to reserve address space");
138 }
139 if (elf_header.e_type == ET_EXEC && base_addr != required_start) {
140 Sandbox::die("Failed to reserve address space at fixed address");
141 }
142
143 char *load_offset = (char *) (base_addr - required_start);
144 char *entry_point = load_offset + elf_header.e_entry;
145 SetAuxvField(auxv, AT_ENTRY, (uintptr_t) entry_point);
146 SetAuxvField(auxv, AT_BASE, (uintptr_t) load_offset);
147 SetAuxvField(auxv, AT_PHNUM, elf_header.e_phnum);
148 SetAuxvField(auxv, AT_PHENT, elf_header.e_phentsize);
149 // Note that this assumes that the program headers are included in a
150 // PT_LOAD segment for which the file offsets matches the mapping
151 // offset, but Linux assumes this too when setting AT_PHDR.
152 SetAuxvField(auxv, AT_PHDR, (uintptr_t) base_addr + elf_header.e_phoff);
153
154 for (ElfW(Phdr) *segment = first_segment;
155 segment <= last_segment;
156 segment++) {
157 if (segment->p_type == PT_LOAD) {
158 uintptr_t segment_start = PageSizeRoundDown(segment->p_vaddr);
159 uintptr_t segment_end = PageSizeRoundUp(segment->p_vaddr
160 + segment->p_memsz);
161 int prot = 0;
162 if ((segment->p_flags & PF_R) != 0)
163 prot |= PROT_READ;
164 if ((segment->p_flags & PF_W) != 0)
165 prot |= PROT_WRITE;
166 if ((segment->p_flags & PF_X) != 0)
167 prot |= PROT_EXEC;
168 void *result = mmap(load_offset + segment_start,
169 segment_end - segment_start,
170 prot, MAP_PRIVATE | MAP_FIXED, fd,
171 PageSizeRoundDown(segment->p_offset));
172 if (result == MAP_FAILED) {
173 Sandbox::die("Failed to map ELF segment");
174 }
175 // TODO(mseaborn): Support a BSS that goes beyond the file's extent.
176 if ((segment->p_flags & PF_W) != 0) {
177 // Zero the BSS to the end of the page. ld.so and other
178 // programs use the rest of this page as part of the brk()
179 // heap and assume that it has been zeroed.
180 uintptr_t bss_start = segment->p_vaddr + segment->p_filesz;
181 memset(load_offset + bss_start, 0, segment_end - bss_start);
182 }
183 }
184 }
185 if (close(fd) != 0) {
186 Sandbox::die("close() failed");
187 }
188 return entry_point;
189 }
190
191 int main(int argc, char **argv) {
192 if (argc < 2) {
193 fprintf(stderr, "Usage: %s executable args...\n", argv[0]);
194 return 1;
195 }
196
197 const char *executable_filename = argv[1];
198 int executable_fd = open(executable_filename, O_RDONLY);
199 if (executable_fd < 0) {
200 fprintf(stderr, "Failed to open executable %s: %s\n",
201 executable_filename, strerror(errno));
202 return 1;
203 }
204
205 playground::g_policy.allow_file_namespace = true;
206 playground::AddTlsSetupSyscall();
207 StartSeccompSandbox();
208
209 ElfW(auxv_t) *auxv = FindAuxv(argc, argv);
210 SetAuxvField(auxv, AT_SYSINFO, (uintptr_t) syscallEntryPointNoFrame);
211 char **stack = argv;
212 *(long *) stack = argc - 1;
213 void *entry_point = LoadElfObject(executable_fd, auxv);
214 JumpToElfEntryPoint(stack, entry_point, 0);
215 }
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