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

Side by Side Diff: content/browser/zygote_main_linux.cc

Issue 6995121: New NaCl zygote implementation 2 (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: NaCl helper for Chrome Linux zygote Created 9 years, 6 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 <dlfcn.h>
6 #include <fcntl.h> 6 #include <fcntl.h>
7 #include <pthread.h> 7 #include <pthread.h>
8 #include <sys/epoll.h> 8 #include <sys/epoll.h>
9 #include <sys/prctl.h> 9 #include <sys/prctl.h>
10 #include <sys/signal.h> 10 #include <sys/signal.h>
(...skipping 20 matching lines...) Expand all
31 #include "base/memory/scoped_ptr.h" 31 #include "base/memory/scoped_ptr.h"
32 #include "base/path_service.h" 32 #include "base/path_service.h"
33 #include "base/pickle.h" 33 #include "base/pickle.h"
34 #include "base/process_util.h" 34 #include "base/process_util.h"
35 #include "base/rand_util.h" 35 #include "base/rand_util.h"
36 #include "base/sys_info.h" 36 #include "base/sys_info.h"
37 #include "build/build_config.h" 37 #include "build/build_config.h"
38 #include "crypto/nss_util.h" 38 #include "crypto/nss_util.h"
39 #include "chrome/common/chrome_paths.h" 39 #include "chrome/common/chrome_paths.h"
40 #include "chrome/common/chrome_switches.h" 40 #include "chrome/common/chrome_switches.h"
41 #include "chrome/nacl/nacl_helper_linux.h"
41 #include "content/common/chrome_descriptors.h" 42 #include "content/common/chrome_descriptors.h"
42 #include "content/common/font_config_ipc_linux.h" 43 #include "content/common/font_config_ipc_linux.h"
43 #include "content/common/main_function_params.h" 44 #include "content/common/main_function_params.h"
44 #include "content/common/pepper_plugin_registry.h" 45 #include "content/common/pepper_plugin_registry.h"
45 #include "content/common/process_watcher.h" 46 #include "content/common/process_watcher.h"
46 #include "content/common/result_codes.h" 47 #include "content/common/result_codes.h"
47 #include "content/common/sandbox_methods_linux.h" 48 #include "content/common/sandbox_methods_linux.h"
48 #include "content/common/set_process_title.h" 49 #include "content/common/set_process_title.h"
49 #include "content/common/unix_domain_socket_posix.h" 50 #include "content/common/unix_domain_socket_posix.h"
50 #include "media/base/media.h" 51 #include "media/base/media.h"
(...skipping 10 matching lines...) Expand all
61 62
62 // http://code.google.com/p/chromium/wiki/LinuxZygote 63 // http://code.google.com/p/chromium/wiki/LinuxZygote
63 64
64 static const int kBrowserDescriptor = 3; 65 static const int kBrowserDescriptor = 3;
65 static const int kMagicSandboxIPCDescriptor = 5; 66 static const int kMagicSandboxIPCDescriptor = 5;
66 static const int kZygoteIdDescriptor = 7; 67 static const int kZygoteIdDescriptor = 7;
67 static bool g_suid_sandbox_active = false; 68 static bool g_suid_sandbox_active = false;
68 #if defined(SECCOMP_SANDBOX) 69 #if defined(SECCOMP_SANDBOX)
69 // |g_proc_fd| is used only by the seccomp sandbox. 70 // |g_proc_fd| is used only by the seccomp sandbox.
70 static int g_proc_fd = -1; 71 static int g_proc_fd = -1;
72 static int g_nacl_helper_fd = -1;
73 static base::ProcessId g_naclpid = -1;
71 #endif 74 #endif
72 75
73 #if defined(CHROMIUM_SELINUX) 76 #if defined(CHROMIUM_SELINUX)
74 static void SELinuxTransitionToTypeOrDie(const char* type) { 77 static void SELinuxTransitionToTypeOrDie(const char* type) {
75 security_context_t security_context; 78 security_context_t security_context;
76 if (getcon(&security_context)) 79 if (getcon(&security_context))
77 LOG(FATAL) << "Cannot get SELinux context"; 80 LOG(FATAL) << "Cannot get SELinux context";
78 81
79 context_t context = context_new(security_context); 82 context_t context = context_new(security_context);
80 context_type_set(context, type); 83 context_type_set(context, type);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 return false; 157 return false;
155 } 158 }
156 159
157 Pickle pickle(buf, len); 160 Pickle pickle(buf, len);
158 void* iter = NULL; 161 void* iter = NULL;
159 162
160 int kind; 163 int kind;
161 if (pickle.ReadInt(&iter, &kind)) { 164 if (pickle.ReadInt(&iter, &kind)) {
162 switch (kind) { 165 switch (kind) {
163 case ZygoteHost::kCmdFork: 166 case ZygoteHost::kCmdFork:
167 case ZygoteHost::kCmdNaClFork:
164 // This function call can return multiple times, once per fork(). 168 // This function call can return multiple times, once per fork().
165 return HandleForkRequest(fd, pickle, iter, fds); 169 return HandleForkRequest(fd, pickle, iter, fds,
170 kind == ZygoteHost::kCmdNaClFork);
166 case ZygoteHost::kCmdReap: 171 case ZygoteHost::kCmdReap:
167 if (!fds.empty()) 172 if (!fds.empty())
168 break; 173 break;
169 HandleReapRequest(fd, pickle, iter); 174 HandleReapRequest(fd, pickle, iter);
170 return false; 175 return false;
171 case ZygoteHost::kCmdGetTerminationStatus: 176 case ZygoteHost::kCmdGetTerminationStatus:
172 if (!fds.empty()) 177 if (!fds.empty())
173 break; 178 break;
174 HandleGetTerminationStatus(fd, pickle, iter); 179 HandleGetTerminationStatus(fd, pickle, iter);
175 return false; 180 return false;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 239
235 Pickle write_pickle; 240 Pickle write_pickle;
236 write_pickle.WriteInt(static_cast<int>(status)); 241 write_pickle.WriteInt(static_cast<int>(status));
237 write_pickle.WriteInt(exit_code); 242 write_pickle.WriteInt(exit_code);
238 ssize_t written = 243 ssize_t written =
239 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); 244 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
240 if (written != static_cast<ssize_t>(write_pickle.size())) 245 if (written != static_cast<ssize_t>(write_pickle.size()))
241 PLOG(ERROR) << "write"; 246 PLOG(ERROR) << "write";
242 } 247 }
243 248
249 base::ProcessId ForkNaClLauncher(std::vector<int>& fds) {
250 base::ProcessId naclchild;
251 DLOG(INFO) << "ForkNaClLauncher";
252
253 if (!UnixDomainSocket::SendMsg(g_nacl_helper_fd, kNaClForkRequest,
254 sizeof(kNaClForkRequest), fds)) {
255 perror("ForkNaClLauncher: send failed");
256 return -1;
257 }
258 if (read(g_nacl_helper_fd, &naclchild, sizeof(naclchild))
259 != sizeof(naclchild)) {
260 perror("ForkNaClLauncher: read failed");
261 return -1;
262 }
263 DLOG(INFO) << "nacl_child is " << naclchild;
264 return naclchild;
265 }
266
244 // This is equivalent to fork(), except that, when using the SUID 267 // This is equivalent to fork(), except that, when using the SUID
245 // sandbox, it returns the real PID of the child process as it 268 // sandbox, it returns the real PID of the child process as it
246 // appears outside the sandbox, rather than returning the PID inside 269 // appears outside the sandbox, rather than returning the PID inside
247 // the sandbox. 270 // the sandbox.
248 int ForkWithRealPid() { 271 int ForkWithRealPid(const bool naclfork, std::vector<int>& fds) {
249 if (!g_suid_sandbox_active) 272 // If we didn't start a nacl helper, don't use it to fork.
250 return fork(); 273 // TODO(bradchen): remove this once nacl helper is debugged
274 bool isnaclfork = naclfork && (g_naclpid != -1);
275
276 if (!g_suid_sandbox_active) {
277 DLOG(INFO) << "suid sandbox NOT active";
agl 2011/06/10 17:28:03 This debugging should probably be removed before l
Brad Chen 2011/06/14 00:16:01 Is there a way to leave some code on longer-term f
agl 2011/06/14 14:38:36 Yes, VLOG(1) is suitable for this. See base/loggin
Brad Chen 2011/06/14 22:14:01 Done.
278 DLOG(INFO) << "fds.size() is " << fds.size();
279 if (isnaclfork) {
280 return ForkNaClLauncher(fds);
281 } else {
282 return fork();
283 }
284 }
251 285
252 int dummy_fd; 286 int dummy_fd;
253 ino_t dummy_inode; 287 ino_t dummy_inode;
254 int pipe_fds[2] = { -1, -1 }; 288 int pipe_fds[2] = { -1, -1 };
255 base::ProcessId pid = 0; 289 base::ProcessId pid = 0;
256 290
257 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); 291 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
258 if (dummy_fd < 0) { 292 if (dummy_fd < 0) {
259 LOG(ERROR) << "Failed to create dummy FD"; 293 LOG(ERROR) << "Failed to create dummy FD";
260 goto error; 294 goto error;
261 } 295 }
262 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { 296 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) {
263 LOG(ERROR) << "Failed to get inode for dummy FD"; 297 LOG(ERROR) << "Failed to get inode for dummy FD";
264 goto error; 298 goto error;
265 } 299 }
266 if (pipe(pipe_fds) != 0) { 300 if (pipe(pipe_fds) != 0) {
267 LOG(ERROR) << "Failed to create pipe"; 301 LOG(ERROR) << "Failed to create pipe";
268 goto error; 302 goto error;
269 } 303 }
270 304
271 pid = fork(); 305 if (isnaclfork) {
306 fds.push_back(dummy_fd);
307 fds.push_back(pipe_fds[0]);
308 pid = ForkNaClLauncher(fds);
309 } else {
310 pid = fork();
311 }
272 if (pid < 0) { 312 if (pid < 0) {
273 goto error; 313 goto error;
274 } else if (pid == 0) { 314 } else if (pid == 0) {
275 // In the child process. 315 // In the child process.
276 close(pipe_fds[1]); 316 close(pipe_fds[1]);
277 char buffer[1]; 317 char buffer[1];
278 // Wait until the parent process has discovered our PID. We 318 // Wait until the parent process has discovered our PID. We
279 // should not fork any child processes (which the seccomp 319 // should not fork any child processes (which the seccomp
280 // sandbox does) until then, because that can interfere with the 320 // sandbox does) until then, because that can interfere with the
281 // parent's discovery of our PID. 321 // parent's discovery of our PID.
282 if (HANDLE_EINTR(read(pipe_fds[0], buffer, 1)) != 1 || 322 if (HANDLE_EINTR(read(pipe_fds[0], buffer, 1)) != 1 ||
283 buffer[0] != 'x') { 323 buffer[0] != 'x') {
284 LOG(FATAL) << "Failed to synchronise with parent zygote process"; 324 LOG(FATAL) << "Failed to synchronise with parent zygote process";
285 } 325 }
286 close(pipe_fds[0]); 326 close(pipe_fds[0]);
287 close(dummy_fd); 327 close(dummy_fd);
288 return 0; 328 return 0;
289 } else { 329 } else {
290 // In the parent process. 330 // In the parent process.
291 close(dummy_fd); 331 close(dummy_fd);
292 dummy_fd = -1; 332 dummy_fd = -1;
293 close(pipe_fds[0]); 333 close(pipe_fds[0]);
294 pipe_fds[0] = -1; 334 pipe_fds[0] = -1;
335
295 uint8_t reply_buf[512]; 336 uint8_t reply_buf[512];
296 Pickle request; 337 Pickle request;
297 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); 338 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE);
298 request.WriteUInt64(dummy_inode); 339 request.WriteUInt64(dummy_inode);
299 340
300 const ssize_t r = UnixDomainSocket::SendRecvMsg( 341 const ssize_t r = UnixDomainSocket::SendRecvMsg(
301 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, 342 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL,
302 request); 343 request);
303 if (r == -1) { 344 if (r == -1) {
304 LOG(ERROR) << "Failed to get child process's real PID"; 345 LOG(ERROR) << "Failed to get child process's real PID";
(...skipping 29 matching lines...) Expand all
334 if (pipe_fds[0] >= 0) 375 if (pipe_fds[0] >= 0)
335 close(pipe_fds[0]); 376 close(pipe_fds[0]);
336 if (pipe_fds[1] >= 0) 377 if (pipe_fds[1] >= 0)
337 close(pipe_fds[1]); 378 close(pipe_fds[1]);
338 return -1; 379 return -1;
339 } 380 }
340 381
341 // Handle a 'fork' request from the browser: this means that the browser 382 // Handle a 'fork' request from the browser: this means that the browser
342 // wishes to start a new renderer. 383 // wishes to start a new renderer.
343 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, 384 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter,
344 std::vector<int>& fds) { 385 std::vector<int>& fds, const bool isnaclfork) {
345 std::vector<std::string> args; 386 std::vector<std::string> args;
346 int argc, numfds; 387 int argc, numfds;
347 base::GlobalDescriptors::Mapping mapping; 388 base::GlobalDescriptors::Mapping mapping;
348 base::ProcessId child; 389 base::ProcessId child;
349 390
350 if (!pickle.ReadInt(&iter, &argc)) 391 if (!pickle.ReadInt(&iter, &argc))
351 goto error; 392 goto error;
352 393
353 for (int i = 0; i < argc; ++i) { 394 for (int i = 0; i < argc; ++i) {
354 std::string arg; 395 std::string arg;
(...skipping 10 matching lines...) Expand all
365 for (int i = 0; i < numfds; ++i) { 406 for (int i = 0; i < numfds; ++i) {
366 base::GlobalDescriptors::Key key; 407 base::GlobalDescriptors::Key key;
367 if (!pickle.ReadUInt32(&iter, &key)) 408 if (!pickle.ReadUInt32(&iter, &key))
368 goto error; 409 goto error;
369 mapping.push_back(std::make_pair(key, fds[i])); 410 mapping.push_back(std::make_pair(key, fds[i]));
370 } 411 }
371 412
372 mapping.push_back(std::make_pair( 413 mapping.push_back(std::make_pair(
373 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); 414 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
374 415
375 child = ForkWithRealPid(); 416 child = ForkWithRealPid(isnaclfork, fds);
376 417
377 if (!child) { 418 if (!child) {
378 #if defined(SECCOMP_SANDBOX) 419 #if defined(SECCOMP_SANDBOX)
379 // Try to open /proc/self/maps as the seccomp sandbox needs access to it 420 // Try to open /proc/self/maps as the seccomp sandbox needs access to it
380 if (g_proc_fd >= 0) { 421 if (g_proc_fd >= 0) {
381 int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY); 422 int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY);
382 if (proc_self_maps >= 0) { 423 if (proc_self_maps >= 0) {
383 SeccompSandboxSetProcSelfMaps(proc_self_maps); 424 SeccompSandboxSetProcSelfMaps(proc_self_maps);
384 } 425 }
385 close(g_proc_fd); 426 close(g_proc_fd);
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 481
441 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs 482 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs
442 // fork() returns are not the real PIDs, so we need to map the Real PIDS 483 // fork() returns are not the real PIDs, so we need to map the Real PIDS
443 // into the sandbox PID namespace. 484 // into the sandbox PID namespace.
444 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; 485 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap;
445 ProcessMap real_pids_to_sandbox_pids; 486 ProcessMap real_pids_to_sandbox_pids;
446 487
447 const int sandbox_flags_; 488 const int sandbox_flags_;
448 }; 489 };
449 490
491 static void LaunchNaClHelper() {
492 DLOG(INFO) << "LaunchNaClHelper";
agl 2011/06/10 17:28:03 This debugging should probably be removed before l
Brad Chen 2011/06/14 22:14:01 Replaced with VLOG(1) On 2011/06/10 17:28:03, agl
493 int fds[2];
494
495 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
496 base::file_handle_mapping_vector fds_to_map;
497 fds_to_map.push_back(std::make_pair(fds[1], 3));
498
499 char* nacl_zygote_exe = getenv("NACL_NEW_ZYGOTE");
agl 2011/06/10 17:28:03 const
Brad Chen 2011/06/14 00:16:01 Done.
500 g_naclpid = -1;
501 if (NULL != nacl_zygote_exe) {
502 CommandLine::StringVector argv = CommandLine::ForCurrentProcess()->argv();
503 argv[0] = nacl_zygote_exe;
504 base::LaunchAppWithClone(argv, fds_to_map, false, &g_naclpid,
505 CLONE_FS | SIGCHLD);
506 }
507 close(fds[1]);
508 if (g_naclpid > 0) {
509 const int kExpectedLength = sizeof(kNaClHelperMagic);
510 char buf[kExpectedLength];
511 int nread;
512
513 DLOG(INFO) << "NaCl Launcher PID is " << g_naclpid;
agl 2011/06/10 17:28:03 ditto.
Brad Chen 2011/06/14 22:14:01 Replaced with VLOG(1) On 2011/06/10 17:28:03, agl
514 nread = read(fds[0], buf, sizeof(buf));
agl 2011/06/10 17:28:03 nread can be declared here.
Brad Chen 2011/06/14 00:16:01 Done.
515 DCHECK(nread == kExpectedLength) << "Incorrect NaCl helper magic length";
516 DCHECK(0 == strcmp(buf, kNaClHelperMagic)) << "Incorrect nacl helper magic";
517 // all is well
518 g_nacl_helper_fd = fds[0];
519 return;
520 }
521 DLOG(INFO) << "Could not launch NaCl helper";
522 g_naclpid = -1;
523 g_nacl_helper_fd = -1;
524 close(fds[0]);
525
526 return;
527 }
528
450 // With SELinux we can carve out a precise sandbox, so we don't have to play 529 // With SELinux we can carve out a precise sandbox, so we don't have to play
451 // with intercepting libc calls. 530 // with intercepting libc calls.
452 #if !defined(CHROMIUM_SELINUX) 531 #if !defined(CHROMIUM_SELINUX)
453 532
454 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, 533 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output,
455 char* timezone_out, 534 char* timezone_out,
456 size_t timezone_out_len) { 535 size_t timezone_out_len) {
457 Pickle request; 536 Pickle request;
458 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); 537 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME);
459 request.WriteString( 538 request.WriteString(
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 if (CommandLine::ForCurrentProcess()->HasSwitch( 800 if (CommandLine::ForCurrentProcess()->HasSwitch(
722 switches::kEnableSeccompSandbox)) { 801 switches::kEnableSeccompSandbox)) {
723 g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY); 802 g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY);
724 if (g_proc_fd < 0) { 803 if (g_proc_fd < 0) {
725 LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp " 804 LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp "
726 "sandboxing."; 805 "sandboxing.";
727 } 806 }
728 } 807 }
729 #endif // SECCOMP_SANDBOX 808 #endif // SECCOMP_SANDBOX
730 809
810 // launch Native Client helper before entering the sandbox
811 LaunchNaClHelper();
812
731 // Turn on the SELinux or SUID sandbox 813 // Turn on the SELinux or SUID sandbox
732 if (!EnterSandbox()) { 814 if (!EnterSandbox()) {
733 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " 815 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: "
734 << errno << ")"; 816 << errno << ")";
735 return false; 817 return false;
736 } 818 }
737 819
738 int sandbox_flags = 0; 820 int sandbox_flags = 0;
739 if (getenv("SBX_D")) 821 if (getenv("SBX_D"))
740 sandbox_flags |= ZygoteHost::kSandboxSUID; 822 sandbox_flags |= ZygoteHost::kSandboxSUID;
(...skipping 20 matching lines...) Expand all
761 VLOG(1) << "Enabling experimental Seccomp sandbox."; 843 VLOG(1) << "Enabling experimental Seccomp sandbox.";
762 sandbox_flags |= ZygoteHost::kSandboxSeccomp; 844 sandbox_flags |= ZygoteHost::kSandboxSeccomp;
763 } 845 }
764 } 846 }
765 #endif // SECCOMP_SANDBOX 847 #endif // SECCOMP_SANDBOX
766 848
767 Zygote zygote(sandbox_flags); 849 Zygote zygote(sandbox_flags);
768 // This function call can return multiple times, once per fork(). 850 // This function call can return multiple times, once per fork().
769 return zygote.ProcessRequests(); 851 return zygote.ProcessRequests();
770 } 852 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698