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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | makefile » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: elf_loader.cc
diff --git a/elf_loader.cc b/elf_loader.cc
new file mode 100644
index 0000000000000000000000000000000000000000..21109e5abf36cb10fa26af9c2d2dc81a4a5f67ee
--- /dev/null
+++ b/elf_loader.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <elf.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "sandbox_impl.h"
+#include "syscall_entrypoint.h"
+#include "tls_setup.h"
+
+
+#if defined(__x86_64__)
+# define ElfW(name) Elf64_##name
+# define ElfW_ELFCLASS ELFCLASS64
+#elif defined(__i386__)
+# define ElfW(name) Elf32_##name
+# define ElfW_ELFCLASS ELFCLASS32
+#else
+# error "Unsupported target platform"
Markus (顧孟勤) 2011/08/15 22:05:23 I keep making this same mistake all the time. Ther
+#endif
+
+
+uintptr_t PageSizeRoundDown(uintptr_t val) {
Markus (顧孟勤) 2011/08/15 22:05:23 Are you deliberately not making these functions "s
+ return val & ~(getpagesize() - 1);
Markus (顧孟勤) 2011/08/15 22:05:23 You can also write: val & -getpagesize() Not su
+}
+
+uintptr_t PageSizeRoundUp(uintptr_t val) {
+ return (val + getpagesize() - 1) & ~(getpagesize() - 1);
Markus (顧孟勤) 2011/08/15 22:05:23 Maybe: return PageSizeRoundDown(val + getpagesi
+}
+
+static ElfW(auxv_t) *FindAuxv(int argc, char **argv) {
+ char **ptr = argv + argc + 1;
+ // Skip over envp.
+ while (*ptr != NULL)
+ ptr++;
Markus (顧孟勤) 2011/08/15 22:05:23 I'd prefer "{ }" around this statement. But maybe,
+ ptr++;
+ return (ElfW(auxv_t) *) ptr;
+}
+
+static void SetAuxvField(ElfW(auxv_t) *auxv, unsigned type, uintptr_t value) {
+ for (; auxv->a_type != AT_NULL; auxv++) {
+ if (auxv->a_type == type) {
+ auxv->a_un.a_val = value;
+ return;
+ }
+ }
Markus (顧孟勤) 2011/08/15 22:05:23 Is it OK, if we fail to find the matching type? Sh
+}
+
+static void JumpToElfEntryPoint(void *stack, void *entry_point,
+ void *atexit_func) {
Markus (顧孟勤) 2011/08/15 22:05:23 You might consider declaring this function as __at
+#if defined(__x86_64__)
+ asm("mov %0, %%rsp\n"
+ "jmp *%1\n"
+ // %edx is registered as an atexit handler if non-zero.
+ : : "r"(stack), "r"(entry_point), "d"(atexit_func));
+#elif defined(__i386__)
+ asm("mov %0, %%esp\n"
+ "jmp *%1\n"
+ // %rdx is registered as an atexit handler if non-zero.
+ : : "r"(stack), "r"(entry_point), "d"(atexit_func));
+#else
+# error "Unsupported target platform"
+#endif
+}
+
+static void *LoadElfObject(int fd, ElfW(auxv_t) *auxv) {
+ // Load and check headers.
+ ElfW(Ehdr) elf_header;
+ if (pread(fd, &elf_header, sizeof(elf_header), 0) != sizeof(elf_header)) {
+ Sandbox::die("Failed to read ELF header");
+ }
+ if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
+ Sandbox::die("Not an ELF file");
+ }
+ if (elf_header.e_ident[EI_CLASS] != ElfW_ELFCLASS) {
+ Sandbox::die("Unexpected ELFCLASS");
+ }
+ 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
+ char *phdrs = (char *) alloca(phdrs_size);
Markus (顧孟勤) 2011/08/15 22:05:23 You should check that the alloca() request succeed
+ if (pread(fd, phdrs, phdrs_size, elf_header.e_phoff) != phdrs_size) {
+ Sandbox::die("Failed to read ELF program headers");
+ }
+
+ // Scan program headers to find the overall size of the ELF object.
+ size_t load_size = 0;
+ char *required_start = 0;
+ for (int index = 0; index < elf_header.e_phnum; index++) {
+ ElfW(Phdr) *segment =
+ (ElfW(Phdr) *) (phdrs + elf_header.e_phentsize * index);
+ if (segment->p_type == PT_LOAD) {
+ size_t segment_end = PageSizeRoundUp(segment->p_vaddr + segment->p_memsz);
+ if (load_size < segment_end) {
+ load_size = segment_end;
+ }
+ 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
+ required_start = (char *) PageSizeRoundDown(segment->p_vaddr);
+ }
+ }
+ }
+
Markus (顧孟勤) 2011/08/15 22:05:23 You might want to add a sanity check that "load_si
+ // Reserve address space.
+ // For executables that must be loaded at a fixed address, we could
+ // use MAP_FIXED, but if the address range is already occupied then
+ // that will clobber the existing mappings without warning, which is
+ // bad. Instead, use an address hint and check that we got the
+ // expected address.
+ char *base_addr = (char *) mmap(required_start, load_size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (base_addr == MAP_FAILED) {
+ Sandbox::die("Failed to reserve address space");
+ }
+ if (required_start != NULL && base_addr != required_start) {
+ Sandbox::die("Failed to reserve address space at fixed address");
+ }
+
+ char *load_offset = (char *) (base_addr - required_start);
+ char *entry_point = load_offset + elf_header.e_entry;
+ SetAuxvField(auxv, AT_ENTRY, (uintptr_t) entry_point);
+ SetAuxvField(auxv, AT_BASE, (uintptr_t) load_offset);
+ SetAuxvField(auxv, AT_PHNUM, elf_header.e_phnum);
+ SetAuxvField(auxv, AT_PHENT, elf_header.e_phentsize);
+ // Note that this assumes that the program headers are included in a
+ // PT_LOAD segment for which the file offsets matches the mapping
+ // 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
+ SetAuxvField(auxv, AT_PHDR, (uintptr_t) base_addr + elf_header.e_phoff);
+
+ for (int index = 0; index < elf_header.e_phnum; index++) {
+ ElfW(Phdr) *segment =
+ (ElfW(Phdr) *) (phdrs + elf_header.e_phentsize * index);
+ if (segment->p_type == PT_LOAD) {
+ uintptr_t segment_start = PageSizeRoundDown(segment->p_vaddr);
+ size_t segment_end = PageSizeRoundUp(segment->p_vaddr + segment->p_memsz);
Markus (顧孟勤) 2011/08/15 22:05:23 Again, do we worry about overflows?
+ int prot = 0;
+ if ((segment->p_flags & PF_R) != 0)
+ prot |= PROT_READ;
+ if ((segment->p_flags & PF_W) != 0)
+ prot |= PROT_WRITE;
+ if ((segment->p_flags & PF_X) != 0)
+ prot |= PROT_EXEC;
+ 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
+ segment_end - segment_start,
+ prot, MAP_PRIVATE | MAP_FIXED, fd,
+ PageSizeRoundDown(segment->p_offset));
+ if (result == MAP_FAILED) {
+ Sandbox::die("Failed to map ELF segment");
+ }
+ // 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?
+ if ((segment->p_flags & PF_W) != 0) {
+ // Zero the BSS to the end of the page.
+ 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
+ memset(load_offset + bss_start, 0, segment_end - bss_start);
+ }
+ }
+ }
+ if (close(fd) != 0) {
+ Sandbox::die("close() failed");
+ }
+ return entry_point;
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s executable args...\n", argv[0]);
+ return 1;
+ }
+
+ const char *executable_filename = argv[1];
+ int executable_fd = open(executable_filename, O_RDONLY);
+ if (executable_fd < 0) {
+ fprintf(stderr, "Failed to open executable %s: %s\n",
+ executable_filename, strerror(errno));
+ return 1;
+ }
+
+ playground::g_policy.allow_file_namespace = true;
Markus (顧孟勤) 2011/08/15 22:05:23 We probably want a more fine-grained policy at som
+ playground::AddTlsSetupSyscall();
+ StartSeccompSandbox();
+
+ ElfW(auxv_t) *auxv = FindAuxv(argc, argv);
+ 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
+ char **stack = argv;
+ *(long *) stack = argc - 1;
Markus (顧孟勤) 2011/08/15 22:05:23 Are there alignment requirements that we need to w
+ void *entry_point = LoadElfObject(executable_fd, auxv);
+ JumpToElfEntryPoint(stack, entry_point, 0);
+}
« no previous file with comments | « no previous file | makefile » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698