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
3 // found in the LICENSE file.
5 #include "chrome/app/nacl_fork_delegate_linux.h"
9 #include <sys/resource.h>
10 #include <sys/socket.h>
12 #include "base/basictypes.h"
13 #include "base/command_line.h"
14 #include "base/eintr_wrapper.h"
15 #include "base/logging.h"
16 #include "base/file_path.h"
17 #include "base/path_service.h"
18 #include "base/process_util.h"
19 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
20 #include "content/common/unix_domain_socket_posix.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/nacl_helper_linux.h"
25 NaClForkDelegate::NaClForkDelegate()
26 : status_(kNaClHelperUnused
),
31 * Note these need to match up with their counterparts in nacl_helper_linux.c
32 * and nacl_helper_bootstrap_linux.c.
34 const char kNaClHelperAtZero
[] = "--at-zero";
35 const char kNaClHelperRDebug
[] = "--r_debug=0xXXXXXXXXXXXXXXXX";
37 void NaClForkDelegate::Init(const bool sandboxed
,
38 const int browserdesc
,
39 const int sandboxdesc
) {
40 VLOG(1) << "NaClForkDelegate::Init()";
43 sandboxed_
= sandboxed
;
44 // Confirm a couple hard-wired assumptions.
45 // The NaCl constants are from chrome/nacl/nacl_linux_helper.h
46 DCHECK(kNaClBrowserDescriptor
== browserdesc
);
47 DCHECK(kNaClSandboxDescriptor
== sandboxdesc
);
49 CHECK(socketpair(PF_UNIX
, SOCK_SEQPACKET
, 0, fds
) == 0);
50 base::file_handle_mapping_vector fds_to_map
;
51 fds_to_map
.push_back(std::make_pair(fds
[1], kNaClZygoteDescriptor
));
52 fds_to_map
.push_back(std::make_pair(sandboxdesc
, kNaClSandboxDescriptor
));
54 status_
= kNaClHelperUnused
;
56 FilePath helper_bootstrap_exe
;
57 if (!PathService::Get(chrome::FILE_NACL_HELPER
, &helper_exe
)) {
58 status_
= kNaClHelperMissing
;
59 } else if (!PathService::Get(chrome::FILE_NACL_HELPER_BOOTSTRAP
,
60 &helper_bootstrap_exe
)) {
61 status_
= kNaClHelperBootstrapMissing
;
62 } else if (RunningOnValgrind()) {
63 status_
= kNaClHelperValgrind
;
65 CommandLine
cmd_line(helper_bootstrap_exe
);
66 cmd_line
.AppendArgPath(helper_exe
);
67 cmd_line
.AppendArgNative(kNaClHelperAtZero
);
68 cmd_line
.AppendArgNative(kNaClHelperRDebug
);
69 base::LaunchOptions options
;
70 options
.fds_to_remap
= &fds_to_map
;
71 options
.clone_flags
= CLONE_FS
| SIGCHLD
;
73 // The NaCl processes spawned may need to exceed the ambient soft limit
74 // on RLIMIT_AS to allocate the untrusted address space and its guard
75 // regions. The nacl_helper itself cannot just raise its own limit,
76 // because the existing limit may prevent the initial exec of
77 // nacl_helper_bootstrap from succeeding, with its large address space
79 std::set
<int> max_these_limits
;
80 max_these_limits
.insert(RLIMIT_AS
);
81 options
.maximize_rlimits
= &max_these_limits
;
83 if (!base::LaunchProcess(cmd_line
.argv(), options
, NULL
))
84 status_
= kNaClHelperLaunchFailed
;
85 // parent and error cases are handled below
87 if (HANDLE_EINTR(close(fds
[1])) != 0)
88 LOG(ERROR
) << "close(fds[1]) failed";
89 if (status_
== kNaClHelperUnused
) {
90 const ssize_t kExpectedLength
= strlen(kNaClHelperStartupAck
);
91 char buf
[kExpectedLength
];
93 // Wait for ack from nacl_helper, indicating it is ready to help
94 const ssize_t nread
= HANDLE_EINTR(read(fds
[0], buf
, sizeof(buf
)));
95 if (nread
== kExpectedLength
&&
96 memcmp(buf
, kNaClHelperStartupAck
, nread
) == 0) {
98 status_
= kNaClHelperSuccess
;
103 status_
= kNaClHelperAckFailed
;
104 LOG(ERROR
) << "Bad NaCl helper startup ack (" << nread
<< " bytes)";
106 // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper
107 // becomes the default.
109 if (HANDLE_EINTR(close(fds
[0])) != 0)
110 LOG(ERROR
) << "close(fds[0]) failed";
113 void NaClForkDelegate::InitialUMA(std::string
* uma_name
,
115 int* uma_boundary_value
) {
116 *uma_name
= "NaCl.Client.Helper.InitState";
117 *uma_sample
= status_
;
118 *uma_boundary_value
= kNaClHelperStatusBoundary
;
121 NaClForkDelegate::~NaClForkDelegate() {
122 // side effect of close: delegate process will terminate
123 if (status_
== kNaClHelperSuccess
) {
124 if (HANDLE_EINTR(close(fd_
)) != 0)
125 LOG(ERROR
) << "close(fd_) failed";
129 bool NaClForkDelegate::CanHelp(const std::string
& process_type
,
130 std::string
* uma_name
,
132 int* uma_boundary_value
) {
133 if (process_type
!= switches::kNaClLoaderProcess
)
135 *uma_name
= "NaCl.Client.Helper.StateOnFork";
136 *uma_sample
= status_
;
137 *uma_boundary_value
= kNaClHelperStatusBoundary
;
138 return status_
== kNaClHelperSuccess
;
141 pid_t
NaClForkDelegate::Fork(const std::vector
<int>& fds
) {
142 base::ProcessId naclchild
;
143 VLOG(1) << "NaClForkDelegate::Fork";
145 DCHECK(fds
.size() == kNaClParentFDIndex
+ 1);
146 if (!UnixDomainSocket::SendMsg(fd_
, kNaClForkRequest
,
147 strlen(kNaClForkRequest
), fds
)) {
148 LOG(ERROR
) << "NaClForkDelegate::Fork: SendMsg failed";
151 int nread
= HANDLE_EINTR(read(fd_
, &naclchild
, sizeof(naclchild
)));
152 if (nread
!= sizeof(naclchild
)) {
153 LOG(ERROR
) << "NaClForkDelegate::Fork: read failed";
156 VLOG(1) << "nacl_child is " << naclchild
<< " (" << nread
<< " bytes)";
160 bool NaClForkDelegate::AckChild(const int fd
,
161 const std::string
& channel_switch
) {
162 int nwritten
= HANDLE_EINTR(write(fd
, channel_switch
.c_str(),
163 channel_switch
.length()));
164 if (nwritten
!= static_cast<int>(channel_switch
.length())) {