OLD | NEW |
---|---|
(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 } | |
OLD | NEW |