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 |