Index: sandbox/linux/seccomp/trusted_process.cc |
=================================================================== |
--- sandbox/linux/seccomp/trusted_process.cc (revision 57969) |
+++ sandbox/linux/seccomp/trusted_process.cc (working copy) |
@@ -1,268 +0,0 @@ |
-// Copyright (c) 2010 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 <dirent.h> |
-#include <map> |
- |
-#include "debug.h" |
-#include "sandbox_impl.h" |
-#include "syscall_table.h" |
- |
-namespace playground { |
- |
-struct SandboxPolicy g_policy; |
- |
-struct Thread { |
- int fdPub, fd; |
- SecureMem::Args* mem; |
-}; |
- |
-SecureMem::Args* Sandbox::getNewSecureMem() { |
- if (!secureMemPool_.empty()) { |
- SecureMem::Args* rc = secureMemPool_.back(); |
- secureMemPool_.pop_back(); |
- memset(rc->scratchPage, 0, sizeof(rc->scratchPage)); |
- return rc; |
- } |
- return NULL; |
-} |
- |
-void Sandbox::trustedProcess(int parentMapsFd, int processFdPub, int sandboxFd, |
- int cloneFd, SecureMem::Args* secureArena) { |
- // The trusted process doesn't have access to TLS. Zero out the segment |
- // registers so that we can later test that we are in the trusted process. |
- #if defined(__x86_64__) |
- asm volatile("mov %0, %%gs\n" : : "r"(0)); |
- #elif defined(__i386__) |
- asm volatile("mov %0, %%fs\n" : : "r"(0)); |
- #else |
- #error Unsupported target platform |
- #endif |
- |
- std::map<long long, struct Thread> threads; |
- SysCalls sys; |
- long long cookie = 0; |
- |
- // The very first entry in the secure memory arena has been assigned to the |
- // initial thread. The remaining entries are available for allocation. |
- SecureMem::Args* startAddress = secureArena; |
- SecureMem::Args* nextThread = startAddress; |
- for (int i = 0; i < kMaxThreads-1; i++) { |
- secureMemPool_.push_back(++startAddress); |
- } |
- |
-newThreadCreated: |
- // Receive information from newly created thread |
- Thread *newThread = &threads[++cookie]; |
- memset(newThread, 0, sizeof(Thread)); |
- struct { |
- SecureMem::Args* self; |
- int tid; |
- int fdPub; |
- } __attribute__((packed)) data; |
- |
- size_t dataLen = sizeof(data); |
- if (!getFd(cloneFd, &newThread->fdPub, &newThread->fd, &data, &dataLen) || |
- dataLen != sizeof(data)) { |
- // We get here either because the sandbox got corrupted, or because our |
- // parent process has terminated. |
- if (newThread->fdPub || dataLen) { |
- die("Failed to receive new thread information"); |
- } |
- die(); |
- } |
- if (data.self != nextThread) { |
- // The only potentially security critical information received from the |
- // newly created thread is "self". The "tid" is for informational purposes |
- // (and for use in the new thread's TLS), and "fdPub" is uncritical as all |
- // file descriptors are considered untrusted. |
- // Thus, we only use "self" for a sanity check, but don't actually trust |
- // it beyond that. |
- die("Received corrupted thread information"); |
- } |
- newThread->mem = nextThread; |
- |
- // Set up TLS area and let thread know that the data is now ready |
- nextThread->cookie = cookie; |
- nextThread->threadId = data.tid; |
- nextThread->threadFdPub = data.fdPub; |
- write(sys, newThread->fd, "", 1); |
- |
- // Dispatch system calls that have been forwarded from the trusted thread(s). |
- for (;;) { |
- struct { |
- unsigned int sysnum; |
- long long cookie; |
- } __attribute__((packed)) header; |
- |
- int rc; |
- if ((rc = read(sys, sandboxFd, &header, sizeof(header))) !=sizeof(header)){ |
- if (rc) { |
- die("Failed to read system call number and thread id"); |
- } |
- die(); |
- } |
- std::map<long long, struct Thread>::iterator iter = |
- threads.find(header.cookie); |
- if (iter == threads.end()) { |
- die("Received request from unknown thread"); |
- } |
- struct Thread* currentThread = &iter->second; |
- if (header.sysnum > maxSyscall || |
- !syscallTable[header.sysnum].trustedProcess) { |
- die("Trusted process encountered unexpected system call"); |
- } |
- |
- // Dispatch system call to handler function. Treat both exit() and clone() |
- // specially. |
- if (syscallTable[header.sysnum].trustedProcess(parentMapsFd, |
- sandboxFd, |
- currentThread->fdPub, |
- currentThread->fd, |
- currentThread->mem) && |
- header.sysnum == __NR_clone) { |
- nextThread = currentThread->mem->newSecureMem; |
- goto newThreadCreated; |
- } else if (header.sysnum == __NR_exit) { |
- NOINTR_SYS(sys.close(iter->second.fdPub)); |
- NOINTR_SYS(sys.close(iter->second.fd)); |
- SecureMem::Args* secureMem = currentThread->mem; |
- threads.erase(iter); |
- secureMemPool_.push_back(secureMem); |
- } |
- } |
-} |
- |
-int Sandbox::initializeProtectedMap(int fd) { |
- int mapsFd; |
- if (!getFd(fd, &mapsFd, NULL, NULL, NULL)) { |
- maps_failure: |
- die("Cannot access /proc/self/maps"); |
- } |
- |
- // Read the memory mappings as they were before the sandbox takes effect. |
- // These mappings cannot be changed by the sandboxed process. |
- char line[80]; |
- FILE *fp = fdopen(mapsFd, "r"); |
- for (bool truncated = false;;) { |
- if (fgets(line, sizeof(line), fp) == NULL) { |
- if (feof(fp) || errno != EINTR) { |
- break; |
- } |
- continue; |
- } |
- if (!truncated) { |
- unsigned long start, stop; |
- char *ptr = line; |
- errno = 0; |
- start = strtoul(ptr, &ptr, 16); |
- if (errno || *ptr++ != '-') { |
- parse_failure: |
- die("Failed to parse /proc/self/maps"); |
- } |
- stop = strtoul(ptr, &ptr, 16); |
- if (errno || *ptr++ != ' ') { |
- goto parse_failure; |
- } |
- protectedMap_[reinterpret_cast<void *>(start)] = stop - start; |
- } |
- truncated = strchr(line, '\n') == NULL; |
- } |
- |
- // Prevent low address memory allocations. Some buggy kernels allow those |
- if (protectedMap_[0] < (64 << 10)) { |
- protectedMap_[0] = 64 << 10; |
- } |
- |
- // Let the sandbox know that we are done parsing the memory map. |
- SysCalls sys; |
- if (write(sys, fd, &mapsFd, sizeof(mapsFd)) != sizeof(mapsFd)) { |
- goto maps_failure; |
- } |
- |
- return mapsFd; |
-} |
- |
-SecureMem::Args* Sandbox::createTrustedProcess(int processFdPub, int sandboxFd, |
- int cloneFdPub, int cloneFd) { |
- // Allocate memory that will be used by an arena for storing the secure |
- // memory. While we allow this memory area to be empty at times (e.g. when |
- // not all threads are in use), we make sure that it never gets overwritten |
- // by user-allocated memory. This happens in initializeProtectedMap() and |
- // snapshotMemoryMappings(). |
- SecureMem::Args* secureArena = reinterpret_cast<SecureMem::Args*>( |
- mmap(NULL, 8192*kMaxThreads, PROT_READ|PROT_WRITE, |
- MAP_SHARED|MAP_ANONYMOUS, -1, 0)); |
- if (secureArena == MAP_FAILED) { |
- die("Failed to allocate secure memory arena"); |
- } |
- |
- // Set up the mutex to be accessible from the trusted process and from |
- // children of the trusted thread(s) |
- if (mmap(&syscall_mutex_, 4096, PROT_READ|PROT_WRITE, |
- MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != &syscall_mutex_) { |
- die("Failed to initialize secure mutex"); |
- } |
- syscall_mutex_ = 0x80000000; |
- |
- |
- // Create a trusted process that can evaluate system call parameters and |
- // decide whether a system call should execute. This process runs outside of |
- // the seccomp sandbox. It communicates with the sandbox'd process through |
- // a socketpair() and through securely shared memory. |
- pid_t pid = fork(); |
- if (pid < 0) { |
- die("Failed to create trusted process"); |
- } |
- if (!pid) { |
- // Close all file handles except for sandboxFd, cloneFd, and stdio |
- DIR *dir = opendir("/proc/self/fd"); |
- if (dir == 0) { |
- // If we don't know the list of our open file handles, just try closing |
- // all valid ones. |
- for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) { |
- if (fd != sandboxFd && fd != cloneFd) { |
- close(fd); |
- } |
- } |
- } else { |
- // If available, if is much more efficient to just close the file |
- // handles that show up in /proc/self/fd/ |
- struct dirent de, *res; |
- while (!readdir_r(dir, &de, &res) && res) { |
- if (res->d_name[0] < '0') |
- continue; |
- int fd = atoi(res->d_name); |
- if (fd > 2 && |
- fd != sandboxFd && fd != cloneFd && fd != dirfd(dir)) { |
- close(fd); |
- } |
- } |
- closedir(dir); |
- } |
- |
- // Initialize secure memory used for threads |
- for (int i = 0; i < kMaxThreads; i++) { |
- SecureMem::Args* args = secureArena + i; |
- args->self = args; |
- #ifndef NDEBUG |
- args->allowAllSystemCalls= Debug::isEnabled(); |
- #endif |
- } |
- |
- int parentMapsFd = initializeProtectedMap(sandboxFd); |
- trustedProcess(parentMapsFd, processFdPub, sandboxFd, |
- cloneFd, secureArena); |
- die(); |
- } |
- |
- // We are still in the untrusted code. Deny access to restricted resources. |
- mprotect(secureArena, 8192*kMaxThreads, PROT_NONE); |
- mprotect(&syscall_mutex_, 4096, PROT_NONE); |
- close(sandboxFd); |
- |
- return secureArena; |
-} |
- |
-} // namespace |