Chromium Code Reviews| 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 |