OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <dlfcn.h> |
5 #include <unistd.h> | 6 #include <unistd.h> |
6 #include <sys/epoll.h> | 7 #include <sys/epoll.h> |
7 #include <sys/types.h> | 8 #include <sys/types.h> |
8 #include <sys/socket.h> | 9 #include <sys/socket.h> |
9 #include <sys/signal.h> | 10 #include <sys/signal.h> |
10 #include <sys/prctl.h> | 11 #include <sys/prctl.h> |
11 #include <sys/wait.h> | 12 #include <sys/wait.h> |
12 | 13 |
13 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
14 #include "base/command_line.h" | 15 #include "base/command_line.h" |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 | 199 |
199 error: | 200 error: |
200 LOG(WARNING) << "Error parsing fork request from browser"; | 201 LOG(WARNING) << "Error parsing fork request from browser"; |
201 for (std::vector<int>::const_iterator | 202 for (std::vector<int>::const_iterator |
202 i = fds.begin(); i != fds.end(); ++i) | 203 i = fds.begin(); i != fds.end(); ++i) |
203 close(*i); | 204 close(*i); |
204 return false; | 205 return false; |
205 } | 206 } |
206 }; | 207 }; |
207 | 208 |
208 // Patched dynamic symbol wrapper functions... | 209 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, |
209 namespace sandbox_wrapper { | 210 char* timezone_out, |
210 | 211 size_t timezone_out_len) { |
211 void do_localtime(time_t input, struct tm* output, char* timezone_out, | |
212 size_t timezone_out_len) { | |
213 Pickle request; | 212 Pickle request; |
214 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); | 213 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); |
215 request.WriteString( | 214 request.WriteString( |
216 std::string(reinterpret_cast<char*>(&input), sizeof(input))); | 215 std::string(reinterpret_cast<char*>(&input), sizeof(input))); |
217 | 216 |
218 uint8_t reply_buf[512]; | 217 uint8_t reply_buf[512]; |
219 const ssize_t r = base::SendRecvMsg( | 218 const ssize_t r = base::SendRecvMsg( |
220 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request); | 219 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request); |
221 if (r == -1) { | 220 if (r == -1) { |
222 memset(output, 0, sizeof(struct tm)); | 221 memset(output, 0, sizeof(struct tm)); |
(...skipping 14 matching lines...) Expand all Loading... |
237 if (timezone_out_len) { | 236 if (timezone_out_len) { |
238 const size_t copy_len = std::min(timezone_out_len - 1, timezone.size()); | 237 const size_t copy_len = std::min(timezone_out_len - 1, timezone.size()); |
239 memcpy(timezone_out, timezone.data(), copy_len); | 238 memcpy(timezone_out, timezone.data(), copy_len); |
240 timezone_out[copy_len] = 0; | 239 timezone_out[copy_len] = 0; |
241 output->tm_zone = timezone_out; | 240 output->tm_zone = timezone_out; |
242 } else { | 241 } else { |
243 output->tm_zone = NULL; | 242 output->tm_zone = NULL; |
244 } | 243 } |
245 } | 244 } |
246 | 245 |
| 246 static bool g_am_zygote_or_renderer = false; |
| 247 |
| 248 // Sandbox interception of libc calls. |
| 249 // |
| 250 // Because we are running in a sandbox certain libc calls will fail (localtime |
| 251 // being the motivating example - it needs to read /etc/localtime). We need to |
| 252 // intercept these calls and proxy them to the browser. However, these calls |
| 253 // may come from us or from our libraries. In some cases we can't just change |
| 254 // our code. |
| 255 // |
| 256 // It's for these cases that we have the following setup: |
| 257 // |
| 258 // We define global functions for those functions which we wish to override. |
| 259 // Since we will be first in the dynamic resolution order, the dynamic linker |
| 260 // will point callers to our versions of these functions. However, we have the |
| 261 // same binary for both the browser and the renderers, which means that our |
| 262 // overrides will apply in the browser too. |
| 263 // |
| 264 // The global |g_am_zygote_or_renderer| is true iff we are in a zygote or |
| 265 // renderer process. It's set in ZygoteMain and inherited by the renderers when |
| 266 // they fork. (This means that it'll be incorrect for global constructor |
| 267 // functions and before ZygoteMain is called - beware). |
| 268 // |
| 269 // Our replacement functions can check this global and either proxy |
| 270 // the call to the browser over the sandbox IPC |
| 271 // (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use |
| 272 // dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the |
| 273 // current module. |
| 274 // |
| 275 // Other avenues: |
| 276 // |
| 277 // Our first attempt involved some assembly to patch the GOT of the current |
| 278 // module. This worked, but was platform specific and doesn't catch the case |
| 279 // where a library makes a call rather than current module. |
| 280 // |
| 281 // We also considered patching the function in place, but this would again by |
| 282 // platform specific and the above technique seems to work well enough. |
| 283 |
247 struct tm* localtime(const time_t* timep) { | 284 struct tm* localtime(const time_t* timep) { |
248 static struct tm time_struct; | 285 if (g_am_zygote_or_renderer) { |
249 static char timezone_string[64]; | 286 static struct tm time_struct; |
250 do_localtime(*timep, &time_struct, timezone_string, sizeof(timezone_string)); | 287 static char timezone_string[64]; |
251 return &time_struct; | 288 ProxyLocaltimeCallToBrowser(*timep, &time_struct, timezone_string, |
| 289 sizeof(timezone_string)); |
| 290 return &time_struct; |
| 291 } else { |
| 292 typedef struct tm* (*LocaltimeFunction)(const time_t* timep); |
| 293 static LocaltimeFunction libc_localtime; |
| 294 if (!libc_localtime) |
| 295 libc_localtime = (LocaltimeFunction) dlsym(RTLD_NEXT, "localtime"); |
| 296 |
| 297 return libc_localtime(timep); |
| 298 } |
252 } | 299 } |
253 | 300 |
254 struct tm* localtime_r(const time_t* timep, struct tm* result) { | 301 struct tm* localtime_r(const time_t* timep, struct tm* result) { |
255 do_localtime(*timep, result, NULL, 0); | 302 if (g_am_zygote_or_renderer) { |
256 return result; | 303 ProxyLocaltimeCallToBrowser(*timep, result, NULL, 0); |
| 304 return result; |
| 305 } else { |
| 306 typedef struct tm* (*LocaltimeRFunction)(const time_t* timep, |
| 307 struct tm* result); |
| 308 static LocaltimeRFunction libc_localtime_r; |
| 309 if (!libc_localtime_r) |
| 310 libc_localtime_r = (LocaltimeRFunction) dlsym(RTLD_NEXT, "localtime_r"); |
| 311 |
| 312 return libc_localtime_r(timep, result); |
| 313 } |
257 } | 314 } |
258 | 315 |
259 } // namespace sandbox_wrapper | |
260 | |
261 /* On IA-32, function calls which need to be resolved by the dynamic linker are | |
262 * directed to the producure linking table (PLT). Each PLT entry contains code | |
263 * which jumps (indirectly) via the global offset table (GOT): | |
264 * Dump of assembler code for function f@plt: | |
265 * 0x0804830c <f@plt+0>: jmp *0x804a004 # GOT indirect jump | |
266 * 0x08048312 <f@plt+6>: push $0x8 | |
267 * 0x08048317 <f@plt+11>: jmp 0x80482ec <_init+48> | |
268 * | |
269 * At the beginning of a process's lifetime, the GOT entry jumps back to | |
270 * <f@plt+6> end then enters the dynamic linker. Once the symbol has been | |
271 * resolved, the GOT entry is patched so that future calls go directly to the | |
272 * resolved function. | |
273 * | |
274 * This macro finds the PLT entry for a given symbol, |symbol|, and reads the | |
275 * GOT entry address from the first instruction. It then patches that address | |
276 * with the address of a replacement function, |replacement|. | |
277 */ | |
278 #define PATCH_GLOBAL_OFFSET_TABLE(symbol, replacement) \ | |
279 /* First, get the current instruction pointer since the PLT address */ \ | |
280 /* is IP relative */ \ | |
281 asm ("call 0f\n" \ | |
282 "0: pop %%ecx\n" \ | |
283 /* Move the IP relative address of the PLT entry into EAX */ \ | |
284 "mov $" #symbol "@plt,%%eax\n" \ | |
285 /* Add EAX to ECX to get an absolute entry */ \ | |
286 "add %%eax,%%ecx\n" \ | |
287 /* The value in ECX was relative to the add instruction, however, */ \ | |
288 /* the IP value was that of the pop. The pop and mov take 6 */ \ | |
289 /* bytes, so adding 6 gets us the correct address for the PLT. The */ \ | |
290 /* first instruction at the PLT is FF 25 <abs address>, so we skip 2 */ \ | |
291 /* bytes to get to the address. 6 + 2 = 8: */ \ | |
292 "movl 8(%%ecx),%%ecx\n" \ | |
293 /* Now ECX contains the address of the GOT entry, we poke our */ \ | |
294 /* replacement function in there: */ \ | |
295 "movl %0,(%%ecx)\n" \ | |
296 : /* no output */ \ | |
297 : "r" (replacement) \ | |
298 : "memory", "%eax", "%ecx"); | |
299 | |
300 static bool MaybeEnterChroot() { | 316 static bool MaybeEnterChroot() { |
301 const char* const sandbox_fd_string = getenv("SBX_D"); | 317 const char* const sandbox_fd_string = getenv("SBX_D"); |
302 if (sandbox_fd_string) { | 318 if (sandbox_fd_string) { |
303 // The SUID sandbox sets this environment variable to a file descriptor | 319 // The SUID sandbox sets this environment variable to a file descriptor |
304 // over which we can signal that we have completed our startup and can be | 320 // over which we can signal that we have completed our startup and can be |
305 // chrooted. | 321 // chrooted. |
306 | 322 |
307 char* endptr; | 323 char* endptr; |
308 const long fd_long = strtol(sandbox_fd_string, &endptr, 10); | 324 const long fd_long = strtol(sandbox_fd_string, &endptr, 10); |
309 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) | 325 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) |
310 return false; | 326 return false; |
311 const int fd = fd_long; | 327 const int fd = fd_long; |
312 | 328 |
313 // Before entering the sandbox, "prime" any systems that need to open | 329 // Before entering the sandbox, "prime" any systems that need to open |
314 // files and cache the results or the descriptors. | 330 // files and cache the results or the descriptors. |
315 base::RandUint64(); | 331 base::RandUint64(); |
316 | 332 |
317 base::SysInfo::MaxSharedMemorySize(); | 333 base::SysInfo::MaxSharedMemorySize(); |
318 | 334 |
319 // To make wcstombs/mbstowcs work in a renderer, setlocale() has to be | 335 // To make wcstombs/mbstowcs work in a renderer, setlocale() has to be |
320 // called before the sandbox is triggered. It's possible to avoid calling | 336 // called before the sandbox is triggered. It's possible to avoid calling |
321 // setlocale() by pulling out the conversion between FilePath and | 337 // setlocale() by pulling out the conversion between FilePath and |
322 // WebCore String out of the renderer and using string16 in place of | 338 // WebCore String out of the renderer and using string16 in place of |
323 // FilePath for IPC. | 339 // FilePath for IPC. |
324 const char* locale = setlocale(LC_ALL, ""); | 340 const char* locale = setlocale(LC_ALL, ""); |
325 LOG_IF(WARNING, locale == NULL) << "setlocale failed."; | 341 LOG_IF(WARNING, locale == NULL) << "setlocale failed."; |
326 | 342 |
327 #if defined(ARCH_CPU_X86) | |
328 PATCH_GLOBAL_OFFSET_TABLE(localtime, sandbox_wrapper::localtime); | |
329 PATCH_GLOBAL_OFFSET_TABLE(localtime_r, sandbox_wrapper::localtime_r); | |
330 #endif | |
331 | |
332 FilePath module_path; | 343 FilePath module_path; |
333 if (PathService::Get(base::DIR_MODULE, &module_path)) | 344 if (PathService::Get(base::DIR_MODULE, &module_path)) |
334 media::InitializeMediaLibrary(module_path); | 345 media::InitializeMediaLibrary(module_path); |
335 | 346 |
336 static const char kChrootMe = 'C'; | 347 static const char kChrootMe = 'C'; |
337 static const char kChrootMeSuccess = 'O'; | 348 static const char kChrootMeSuccess = 'O'; |
338 | 349 |
339 if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) { | 350 if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) { |
340 LOG(ERROR) << "Failed to write to chroot pipe: " << errno; | 351 LOG(ERROR) << "Failed to write to chroot pipe: " << errno; |
341 return false; | 352 return false; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 } | 394 } |
384 } | 395 } |
385 } else { | 396 } else { |
386 SkiaFontConfigUseDirectImplementation(); | 397 SkiaFontConfigUseDirectImplementation(); |
387 } | 398 } |
388 | 399 |
389 return true; | 400 return true; |
390 } | 401 } |
391 | 402 |
392 bool ZygoteMain(const MainFunctionParams& params) { | 403 bool ZygoteMain(const MainFunctionParams& params) { |
| 404 g_am_zygote_or_renderer = true; |
| 405 |
393 if (!MaybeEnterChroot()) { | 406 if (!MaybeEnterChroot()) { |
394 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " | 407 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " |
395 << errno << ")"; | 408 << errno << ")"; |
396 return false; | 409 return false; |
397 } | 410 } |
398 | 411 |
399 Zygote zygote; | 412 Zygote zygote; |
400 return zygote.ProcessRequests(); | 413 return zygote.ProcessRequests(); |
401 } | 414 } |
OLD | NEW |