| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 <dirent.h> | |
| 6 #include <map> | |
| 7 | |
| 8 #include "debug.h" | |
| 9 #include "sandbox_impl.h" | |
| 10 #include "syscall_table.h" | |
| 11 | |
| 12 namespace playground { | |
| 13 | |
| 14 struct SandboxPolicy g_policy; | |
| 15 | |
| 16 struct Thread { | |
| 17 int fdPub, fd; | |
| 18 SecureMem::Args* mem; | |
| 19 }; | |
| 20 | |
| 21 SecureMem::Args* Sandbox::getNewSecureMem() { | |
| 22 if (!secureMemPool_.empty()) { | |
| 23 SecureMem::Args* rc = secureMemPool_.back(); | |
| 24 secureMemPool_.pop_back(); | |
| 25 memset(rc->scratchPage, 0, sizeof(rc->scratchPage)); | |
| 26 return rc; | |
| 27 } | |
| 28 return NULL; | |
| 29 } | |
| 30 | |
| 31 void Sandbox::trustedProcess(int parentMapsFd, int processFdPub, int sandboxFd, | |
| 32 int cloneFd, SecureMem::Args* secureArena) { | |
| 33 // The trusted process doesn't have access to TLS. Zero out the segment | |
| 34 // registers so that we can later test that we are in the trusted process. | |
| 35 #if defined(__x86_64__) | |
| 36 asm volatile("mov %0, %%gs\n" : : "r"(0)); | |
| 37 #elif defined(__i386__) | |
| 38 asm volatile("mov %0, %%fs\n" : : "r"(0)); | |
| 39 #else | |
| 40 #error Unsupported target platform | |
| 41 #endif | |
| 42 | |
| 43 std::map<long long, struct Thread> threads; | |
| 44 SysCalls sys; | |
| 45 long long cookie = 0; | |
| 46 | |
| 47 // The very first entry in the secure memory arena has been assigned to the | |
| 48 // initial thread. The remaining entries are available for allocation. | |
| 49 SecureMem::Args* startAddress = secureArena; | |
| 50 SecureMem::Args* nextThread = startAddress; | |
| 51 for (int i = 0; i < kMaxThreads-1; i++) { | |
| 52 secureMemPool_.push_back(++startAddress); | |
| 53 } | |
| 54 | |
| 55 newThreadCreated: | |
| 56 // Receive information from newly created thread | |
| 57 Thread *newThread = &threads[++cookie]; | |
| 58 memset(newThread, 0, sizeof(Thread)); | |
| 59 struct { | |
| 60 SecureMem::Args* self; | |
| 61 int tid; | |
| 62 int fdPub; | |
| 63 } __attribute__((packed)) data; | |
| 64 | |
| 65 size_t dataLen = sizeof(data); | |
| 66 if (!getFd(cloneFd, &newThread->fdPub, &newThread->fd, &data, &dataLen) || | |
| 67 dataLen != sizeof(data)) { | |
| 68 // We get here either because the sandbox got corrupted, or because our | |
| 69 // parent process has terminated. | |
| 70 if (newThread->fdPub || dataLen) { | |
| 71 die("Failed to receive new thread information"); | |
| 72 } | |
| 73 die(); | |
| 74 } | |
| 75 if (data.self != nextThread) { | |
| 76 // The only potentially security critical information received from the | |
| 77 // newly created thread is "self". The "tid" is for informational purposes | |
| 78 // (and for use in the new thread's TLS), and "fdPub" is uncritical as all | |
| 79 // file descriptors are considered untrusted. | |
| 80 // Thus, we only use "self" for a sanity check, but don't actually trust | |
| 81 // it beyond that. | |
| 82 die("Received corrupted thread information"); | |
| 83 } | |
| 84 newThread->mem = nextThread; | |
| 85 | |
| 86 // Set up TLS area and let thread know that the data is now ready | |
| 87 nextThread->cookie = cookie; | |
| 88 nextThread->threadId = data.tid; | |
| 89 nextThread->threadFdPub = data.fdPub; | |
| 90 write(sys, newThread->fd, "", 1); | |
| 91 | |
| 92 // Dispatch system calls that have been forwarded from the trusted thread(s). | |
| 93 for (;;) { | |
| 94 struct { | |
| 95 unsigned int sysnum; | |
| 96 long long cookie; | |
| 97 } __attribute__((packed)) header; | |
| 98 | |
| 99 int rc; | |
| 100 if ((rc = read(sys, sandboxFd, &header, sizeof(header))) !=sizeof(header)){ | |
| 101 if (rc) { | |
| 102 die("Failed to read system call number and thread id"); | |
| 103 } | |
| 104 die(); | |
| 105 } | |
| 106 std::map<long long, struct Thread>::iterator iter = | |
| 107 threads.find(header.cookie); | |
| 108 if (iter == threads.end()) { | |
| 109 die("Received request from unknown thread"); | |
| 110 } | |
| 111 struct Thread* currentThread = &iter->second; | |
| 112 if (header.sysnum > maxSyscall || | |
| 113 !syscallTable[header.sysnum].trustedProcess) { | |
| 114 die("Trusted process encountered unexpected system call"); | |
| 115 } | |
| 116 | |
| 117 // Dispatch system call to handler function. Treat both exit() and clone() | |
| 118 // specially. | |
| 119 if (syscallTable[header.sysnum].trustedProcess(parentMapsFd, | |
| 120 sandboxFd, | |
| 121 currentThread->fdPub, | |
| 122 currentThread->fd, | |
| 123 currentThread->mem) && | |
| 124 header.sysnum == __NR_clone) { | |
| 125 nextThread = currentThread->mem->newSecureMem; | |
| 126 goto newThreadCreated; | |
| 127 } else if (header.sysnum == __NR_exit) { | |
| 128 NOINTR_SYS(sys.close(iter->second.fdPub)); | |
| 129 NOINTR_SYS(sys.close(iter->second.fd)); | |
| 130 SecureMem::Args* secureMem = currentThread->mem; | |
| 131 threads.erase(iter); | |
| 132 secureMemPool_.push_back(secureMem); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 int Sandbox::initializeProtectedMap(int fd) { | |
| 138 int mapsFd; | |
| 139 if (!getFd(fd, &mapsFd, NULL, NULL, NULL)) { | |
| 140 maps_failure: | |
| 141 die("Cannot access /proc/self/maps"); | |
| 142 } | |
| 143 | |
| 144 // Read the memory mappings as they were before the sandbox takes effect. | |
| 145 // These mappings cannot be changed by the sandboxed process. | |
| 146 char line[80]; | |
| 147 FILE *fp = fdopen(mapsFd, "r"); | |
| 148 for (bool truncated = false;;) { | |
| 149 if (fgets(line, sizeof(line), fp) == NULL) { | |
| 150 if (feof(fp) || errno != EINTR) { | |
| 151 break; | |
| 152 } | |
| 153 continue; | |
| 154 } | |
| 155 if (!truncated) { | |
| 156 unsigned long start, stop; | |
| 157 char *ptr = line; | |
| 158 errno = 0; | |
| 159 start = strtoul(ptr, &ptr, 16); | |
| 160 if (errno || *ptr++ != '-') { | |
| 161 parse_failure: | |
| 162 die("Failed to parse /proc/self/maps"); | |
| 163 } | |
| 164 stop = strtoul(ptr, &ptr, 16); | |
| 165 if (errno || *ptr++ != ' ') { | |
| 166 goto parse_failure; | |
| 167 } | |
| 168 protectedMap_[reinterpret_cast<void *>(start)] = stop - start; | |
| 169 } | |
| 170 truncated = strchr(line, '\n') == NULL; | |
| 171 } | |
| 172 | |
| 173 // Prevent low address memory allocations. Some buggy kernels allow those | |
| 174 if (protectedMap_[0] < (64 << 10)) { | |
| 175 protectedMap_[0] = 64 << 10; | |
| 176 } | |
| 177 | |
| 178 // Let the sandbox know that we are done parsing the memory map. | |
| 179 SysCalls sys; | |
| 180 if (write(sys, fd, &mapsFd, sizeof(mapsFd)) != sizeof(mapsFd)) { | |
| 181 goto maps_failure; | |
| 182 } | |
| 183 | |
| 184 return mapsFd; | |
| 185 } | |
| 186 | |
| 187 SecureMem::Args* Sandbox::createTrustedProcess(int processFdPub, int sandboxFd, | |
| 188 int cloneFdPub, int cloneFd) { | |
| 189 // Allocate memory that will be used by an arena for storing the secure | |
| 190 // memory. While we allow this memory area to be empty at times (e.g. when | |
| 191 // not all threads are in use), we make sure that it never gets overwritten | |
| 192 // by user-allocated memory. This happens in initializeProtectedMap() and | |
| 193 // snapshotMemoryMappings(). | |
| 194 SecureMem::Args* secureArena = reinterpret_cast<SecureMem::Args*>( | |
| 195 mmap(NULL, 8192*kMaxThreads, PROT_READ|PROT_WRITE, | |
| 196 MAP_SHARED|MAP_ANONYMOUS, -1, 0)); | |
| 197 if (secureArena == MAP_FAILED) { | |
| 198 die("Failed to allocate secure memory arena"); | |
| 199 } | |
| 200 | |
| 201 // Set up the mutex to be accessible from the trusted process and from | |
| 202 // children of the trusted thread(s) | |
| 203 if (mmap(&syscall_mutex_, 4096, PROT_READ|PROT_WRITE, | |
| 204 MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != &syscall_mutex_) { | |
| 205 die("Failed to initialize secure mutex"); | |
| 206 } | |
| 207 syscall_mutex_ = 0x80000000; | |
| 208 | |
| 209 | |
| 210 // Create a trusted process that can evaluate system call parameters and | |
| 211 // decide whether a system call should execute. This process runs outside of | |
| 212 // the seccomp sandbox. It communicates with the sandbox'd process through | |
| 213 // a socketpair() and through securely shared memory. | |
| 214 pid_t pid = fork(); | |
| 215 if (pid < 0) { | |
| 216 die("Failed to create trusted process"); | |
| 217 } | |
| 218 if (!pid) { | |
| 219 // Close all file handles except for sandboxFd, cloneFd, and stdio | |
| 220 DIR *dir = opendir("/proc/self/fd"); | |
| 221 if (dir == 0) { | |
| 222 // If we don't know the list of our open file handles, just try closing | |
| 223 // all valid ones. | |
| 224 for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) { | |
| 225 if (fd != sandboxFd && fd != cloneFd) { | |
| 226 close(fd); | |
| 227 } | |
| 228 } | |
| 229 } else { | |
| 230 // If available, if is much more efficient to just close the file | |
| 231 // handles that show up in /proc/self/fd/ | |
| 232 struct dirent de, *res; | |
| 233 while (!readdir_r(dir, &de, &res) && res) { | |
| 234 if (res->d_name[0] < '0') | |
| 235 continue; | |
| 236 int fd = atoi(res->d_name); | |
| 237 if (fd > 2 && | |
| 238 fd != sandboxFd && fd != cloneFd && fd != dirfd(dir)) { | |
| 239 close(fd); | |
| 240 } | |
| 241 } | |
| 242 closedir(dir); | |
| 243 } | |
| 244 | |
| 245 // Initialize secure memory used for threads | |
| 246 for (int i = 0; i < kMaxThreads; i++) { | |
| 247 SecureMem::Args* args = secureArena + i; | |
| 248 args->self = args; | |
| 249 #ifndef NDEBUG | |
| 250 args->allowAllSystemCalls= Debug::isEnabled(); | |
| 251 #endif | |
| 252 } | |
| 253 | |
| 254 int parentMapsFd = initializeProtectedMap(sandboxFd); | |
| 255 trustedProcess(parentMapsFd, processFdPub, sandboxFd, | |
| 256 cloneFd, secureArena); | |
| 257 die(); | |
| 258 } | |
| 259 | |
| 260 // We are still in the untrusted code. Deny access to restricted resources. | |
| 261 mprotect(secureArena, 8192*kMaxThreads, PROT_NONE); | |
| 262 mprotect(&syscall_mutex_, 4096, PROT_NONE); | |
| 263 close(sandboxFd); | |
| 264 | |
| 265 return secureArena; | |
| 266 } | |
| 267 | |
| 268 } // namespace | |
| OLD | NEW |