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

Side by Side Diff: components/nacl/loader/nacl_helper_linux.cc

Issue 258543006: Change UnixDomainSocket::RecvMsg to return ScopedVector<base::ScopedFD> (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove fds->{empty,reserve}() per brettw@ feedback Created 6 years, 7 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 // A mini-zygote specifically for Native Client. 5 // A mini-zygote specifically for Native Client.
6 6
7 #include "components/nacl/loader/nacl_helper_linux.h" 7 #include "components/nacl/loader/nacl_helper_linux.h"
8 8
9 #include <errno.h> 9 #include <errno.h>
10 #include <fcntl.h> 10 #include <fcntl.h>
11 #include <link.h> 11 #include <link.h>
12 #include <signal.h> 12 #include <signal.h>
13 #include <stdio.h> 13 #include <stdio.h>
14 #include <stdlib.h> 14 #include <stdlib.h>
15 #include <sys/socket.h> 15 #include <sys/socket.h>
16 #include <sys/stat.h> 16 #include <sys/stat.h>
17 #include <sys/types.h> 17 #include <sys/types.h>
18 18
19 #include <string> 19 #include <string>
20 #include <vector> 20 #include <vector>
21 21
22 #include "base/at_exit.h" 22 #include "base/at_exit.h"
23 #include "base/command_line.h" 23 #include "base/command_line.h"
24 #include "base/files/scoped_file.h"
24 #include "base/logging.h" 25 #include "base/logging.h"
25 #include "base/memory/scoped_ptr.h" 26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/scoped_vector.h"
26 #include "base/message_loop/message_loop.h" 28 #include "base/message_loop/message_loop.h"
27 #include "base/posix/eintr_wrapper.h" 29 #include "base/posix/eintr_wrapper.h"
28 #include "base/posix/global_descriptors.h" 30 #include "base/posix/global_descriptors.h"
29 #include "base/posix/unix_domain_socket_linux.h" 31 #include "base/posix/unix_domain_socket_linux.h"
30 #include "base/process/kill.h" 32 #include "base/process/kill.h"
31 #include "base/process/process_handle.h" 33 #include "base/process/process_handle.h"
32 #include "base/rand_util.h" 34 #include "base/rand_util.h"
33 #include "components/nacl/common/nacl_switches.h" 35 #include "components/nacl/common/nacl_switches.h"
34 #include "components/nacl/loader/nacl_listener.h" 36 #include "components/nacl/loader/nacl_listener.h"
35 #include "components/nacl/loader/nacl_sandbox_linux.h" 37 #include "components/nacl/loader/nacl_sandbox_linux.h"
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 int pipefd[2]; 123 int pipefd[2];
122 PCHECK(0 == pipe(pipefd)); 124 PCHECK(0 == pipe(pipefd));
123 PCHECK(-1 != dup2(pipefd[0], file_descriptor)); 125 PCHECK(-1 != dup2(pipefd[0], file_descriptor));
124 PCHECK(0 == IGNORE_EINTR(close(pipefd[0]))); 126 PCHECK(0 == IGNORE_EINTR(close(pipefd[0])));
125 PCHECK(0 == IGNORE_EINTR(close(pipefd[1]))); 127 PCHECK(0 == IGNORE_EINTR(close(pipefd[1])));
126 } 128 }
127 129
128 // The child must mimic the behavior of zygote_main_linux.cc on the child 130 // The child must mimic the behavior of zygote_main_linux.cc on the child
129 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from 131 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from
130 // if (!child) { 132 // if (!child) {
131 void BecomeNaClLoader(const std::vector<int>& child_fds, 133 void BecomeNaClLoader(base::ScopedFD browser_fd,
132 const NaClLoaderSystemInfo& system_info, 134 const NaClLoaderSystemInfo& system_info,
133 bool uses_nonsfi_mode) { 135 bool uses_nonsfi_mode) {
134 VLOG(1) << "NaCl loader: setting up IPC descriptor"; 136 VLOG(1) << "NaCl loader: setting up IPC descriptor";
135 // Close or shutdown IPC channels that we don't need anymore. 137 // Close or shutdown IPC channels that we don't need anymore.
136 PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor))); 138 PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor)));
137 // In Non-SFI mode, it's important to close any non-expected IPC channels. 139 // In Non-SFI mode, it's important to close any non-expected IPC channels.
138 if (uses_nonsfi_mode) { 140 if (uses_nonsfi_mode) {
139 // The low-level kSandboxIPCChannel is used by renderers and NaCl for 141 // The low-level kSandboxIPCChannel is used by renderers and NaCl for
140 // various operations. See the LinuxSandbox::METHOD_* methods. NaCl uses 142 // various operations. See the LinuxSandbox::METHOD_* methods. NaCl uses
141 // LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT in SFI mode, so this 143 // LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT in SFI mode, so this
142 // should only be closed in Non-SFI mode. 144 // should only be closed in Non-SFI mode.
143 // This file descriptor is insidiously used by a number of APIs. Closing it 145 // This file descriptor is insidiously used by a number of APIs. Closing it
144 // could lead to difficult to debug issues. Instead of closing it, replace 146 // could lead to difficult to debug issues. Instead of closing it, replace
145 // it with a dummy. 147 // it with a dummy.
146 const int sandbox_ipc_channel = 148 const int sandbox_ipc_channel =
147 base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel; 149 base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel;
148 150
149 ReplaceFDWithDummy(sandbox_ipc_channel); 151 ReplaceFDWithDummy(sandbox_ipc_channel);
150 } 152 }
151 153
152 InitializeLayerTwoSandbox(uses_nonsfi_mode); 154 InitializeLayerTwoSandbox(uses_nonsfi_mode);
153 base::GlobalDescriptors::GetInstance()->Set( 155 base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel,
154 kPrimaryIPCChannel, 156 browser_fd.release());
155 child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]);
156 157
157 base::MessageLoopForIO main_message_loop; 158 base::MessageLoopForIO main_message_loop;
158 NaClListener listener; 159 NaClListener listener;
159 listener.set_uses_nonsfi_mode(uses_nonsfi_mode); 160 listener.set_uses_nonsfi_mode(uses_nonsfi_mode);
160 listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size); 161 listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size);
161 listener.set_number_of_cores(system_info.number_of_cores); 162 listener.set_number_of_cores(system_info.number_of_cores);
162 listener.Listen(); 163 listener.Listen();
163 _exit(0); 164 _exit(0);
164 } 165 }
165 166
166 // Start the NaCl loader in a child created by the NaCl loader Zygote. 167 // Start the NaCl loader in a child created by the NaCl loader Zygote.
167 void ChildNaClLoaderInit(const std::vector<int>& child_fds, 168 void ChildNaClLoaderInit(ScopedVector<base::ScopedFD> child_fds,
168 const NaClLoaderSystemInfo& system_info, 169 const NaClLoaderSystemInfo& system_info,
169 bool uses_nonsfi_mode, 170 bool uses_nonsfi_mode,
170 const std::string& channel_id) { 171 const std::string& channel_id) {
171 const int parent_fd = child_fds[content::ZygoteForkDelegate::kParentFDIndex];
172 const int dummy_fd = child_fds[content::ZygoteForkDelegate::kDummyFDIndex];
173
174 bool validack = false; 172 bool validack = false;
175 base::ProcessId real_pid; 173 base::ProcessId real_pid;
176 // Wait until the parent process has discovered our PID. We 174 // Wait until the parent process has discovered our PID. We
177 // should not fork any child processes (which the seccomp 175 // should not fork any child processes (which the seccomp
178 // sandbox does) until then, because that can interfere with the 176 // sandbox does) until then, because that can interfere with the
179 // parent's discovery of our PID. 177 // parent's discovery of our PID.
180 const ssize_t nread = 178 const ssize_t nread = HANDLE_EINTR(
181 HANDLE_EINTR(read(parent_fd, &real_pid, sizeof(real_pid))); 179 read(child_fds[content::ZygoteForkDelegate::kParentFDIndex]->get(),
180 &real_pid,
181 sizeof(real_pid)));
182 if (static_cast<size_t>(nread) == sizeof(real_pid)) { 182 if (static_cast<size_t>(nread) == sizeof(real_pid)) {
183 // Make sure the parent didn't accidentally send us our real PID. 183 // Make sure the parent didn't accidentally send us our real PID.
184 // We don't want it to be discoverable anywhere in our address space 184 // We don't want it to be discoverable anywhere in our address space
185 // when we start running untrusted code. 185 // when we start running untrusted code.
186 CHECK(real_pid == 0); 186 CHECK(real_pid == 0);
187 187
188 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 188 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
189 switches::kProcessChannelID, channel_id); 189 switches::kProcessChannelID, channel_id);
190 validack = true; 190 validack = true;
191 } else { 191 } else {
192 if (nread < 0) 192 if (nread < 0)
193 perror("read"); 193 perror("read");
194 LOG(ERROR) << "read returned " << nread; 194 LOG(ERROR) << "read returned " << nread;
195 } 195 }
196 196
jln (very slow on Chromium) 2014/04/28 23:48:55 We can't rely on the ScopedVector to release child
mdempsky 2014/04/29 00:10:33 As we discussed and you pointed out below, child_f
197 if (IGNORE_EINTR(close(dummy_fd)) != 0) 197 base::ScopedFD browser_fd(
198 LOG(ERROR) << "close(dummy_fd) failed"; 198 child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]->Pass());
199 if (IGNORE_EINTR(close(parent_fd)) != 0) 199 child_fds.clear();
jln (very slow on Chromium) 2014/04/28 23:57:19 I had missed this one, so there is no leak.
200 LOG(ERROR) << "close(parent_fd) failed"; 200
201 if (validack) { 201 if (validack) {
202 BecomeNaClLoader(child_fds, system_info, uses_nonsfi_mode); 202 BecomeNaClLoader(browser_fd.Pass(), system_info, uses_nonsfi_mode);
203 } else { 203 } else {
204 LOG(ERROR) << "Failed to synch with zygote"; 204 LOG(ERROR) << "Failed to synch with zygote";
205 } 205 }
206 _exit(1); 206 _exit(1);
207 } 207 }
208 208
209 // Handle a fork request from the Zygote. 209 // Handle a fork request from the Zygote.
210 // Some of this code was lifted from 210 // Some of this code was lifted from
211 // content/browser/zygote_main_linux.cc:ForkWithRealPid() 211 // content/browser/zygote_main_linux.cc:ForkWithRealPid()
212 bool HandleForkRequest(const std::vector<int>& child_fds, 212 bool HandleForkRequest(ScopedVector<base::ScopedFD> child_fds,
213 const NaClLoaderSystemInfo& system_info, 213 const NaClLoaderSystemInfo& system_info,
214 PickleIterator* input_iter, 214 PickleIterator* input_iter,
215 Pickle* output_pickle) { 215 Pickle* output_pickle) {
216 bool uses_nonsfi_mode; 216 bool uses_nonsfi_mode;
217 if (!input_iter->ReadBool(&uses_nonsfi_mode)) { 217 if (!input_iter->ReadBool(&uses_nonsfi_mode)) {
218 LOG(ERROR) << "Could not read uses_nonsfi_mode status"; 218 LOG(ERROR) << "Could not read uses_nonsfi_mode status";
219 return false; 219 return false;
220 } 220 }
221 221
222 std::string channel_id; 222 std::string channel_id;
223 if (!input_iter->ReadString(&channel_id)) { 223 if (!input_iter->ReadString(&channel_id)) {
224 LOG(ERROR) << "Could not read channel_id string"; 224 LOG(ERROR) << "Could not read channel_id string";
225 return false; 225 return false;
226 } 226 }
227 227
228 if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) { 228 if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) {
229 LOG(ERROR) << "nacl_helper: unexpected number of fds, got " 229 LOG(ERROR) << "nacl_helper: unexpected number of fds, got "
230 << child_fds.size(); 230 << child_fds.size();
231 return false; 231 return false;
232 } 232 }
233 233
234 VLOG(1) << "nacl_helper: forking"; 234 VLOG(1) << "nacl_helper: forking";
235 pid_t child_pid = fork(); 235 pid_t child_pid = fork();
236 if (child_pid < 0) { 236 if (child_pid < 0) {
237 PLOG(ERROR) << "*** fork() failed."; 237 PLOG(ERROR) << "*** fork() failed.";
238 } 238 }
239 239
240 if (child_pid == 0) { 240 if (child_pid == 0) {
241 ChildNaClLoaderInit(child_fds, system_info, uses_nonsfi_mode, channel_id); 241 ChildNaClLoaderInit(
242 child_fds.Pass(), system_info, uses_nonsfi_mode, channel_id);
242 NOTREACHED(); 243 NOTREACHED();
243 } 244 }
244 245
245 // I am the parent. 246 // I am the parent.
246 // First, close the dummy_fd so the sandbox won't find me when 247 // First, close the dummy_fd so the sandbox won't find me when
247 // looking for the child's pid in /proc. Also close other fds. 248 // looking for the child's pid in /proc. Also close other fds.
248 for (size_t i = 0; i < child_fds.size(); i++) { 249 child_fds.clear();
249 if (IGNORE_EINTR(close(child_fds[i])) != 0)
250 LOG(ERROR) << "close(child_fds[i]) failed";
251 }
252 VLOG(1) << "nacl_helper: child_pid is " << child_pid; 250 VLOG(1) << "nacl_helper: child_pid is " << child_pid;
253 251
254 // Now send child_pid (eventually -1 if fork failed) to the Chrome Zygote. 252 // Now send child_pid (eventually -1 if fork failed) to the Chrome Zygote.
255 output_pickle->WriteInt(child_pid); 253 output_pickle->WriteInt(child_pid);
256 return true; 254 return true;
257 } 255 }
258 256
259 bool HandleGetTerminationStatusRequest(PickleIterator* input_iter, 257 bool HandleGetTerminationStatusRequest(PickleIterator* input_iter,
260 Pickle* output_pickle) { 258 Pickle* output_pickle) {
261 pid_t child_to_wait; 259 pid_t child_to_wait;
(...skipping 22 matching lines...) Expand all
284 output_pickle->WriteInt(exit_code); 282 output_pickle->WriteInt(exit_code);
285 return true; 283 return true;
286 } 284 }
287 285
288 // Honor a command |command_type|. Eventual command parameters are 286 // Honor a command |command_type|. Eventual command parameters are
289 // available in |input_iter| and eventual file descriptors attached to 287 // available in |input_iter| and eventual file descriptors attached to
290 // the command are in |attached_fds|. 288 // the command are in |attached_fds|.
291 // Reply to the command on |reply_fds|. 289 // Reply to the command on |reply_fds|.
292 bool HonorRequestAndReply(int reply_fd, 290 bool HonorRequestAndReply(int reply_fd,
293 int command_type, 291 int command_type,
294 const std::vector<int>& attached_fds, 292 ScopedVector<base::ScopedFD> attached_fds,
295 const NaClLoaderSystemInfo& system_info, 293 const NaClLoaderSystemInfo& system_info,
296 PickleIterator* input_iter) { 294 PickleIterator* input_iter) {
297 Pickle write_pickle; 295 Pickle write_pickle;
298 bool have_to_reply = false; 296 bool have_to_reply = false;
299 // Commands must write anything to send back to |write_pickle|. 297 // Commands must write anything to send back to |write_pickle|.
300 switch (command_type) { 298 switch (command_type) {
301 case nacl::kNaClForkRequest: 299 case nacl::kNaClForkRequest:
302 have_to_reply = HandleForkRequest(attached_fds, system_info, 300 have_to_reply = HandleForkRequest(
303 input_iter, &write_pickle); 301 attached_fds.Pass(), system_info, input_iter, &write_pickle);
304 break; 302 break;
305 case nacl::kNaClGetTerminationStatusRequest: 303 case nacl::kNaClGetTerminationStatusRequest:
306 have_to_reply = 304 have_to_reply =
307 HandleGetTerminationStatusRequest(input_iter, &write_pickle); 305 HandleGetTerminationStatusRequest(input_iter, &write_pickle);
308 break; 306 break;
309 default: 307 default:
310 LOG(ERROR) << "Unsupported command from Zygote"; 308 LOG(ERROR) << "Unsupported command from Zygote";
311 return false; 309 return false;
312 } 310 }
313 if (!have_to_reply) 311 if (!have_to_reply)
314 return false; 312 return false;
315 const std::vector<int> empty; // We never send file descriptors back. 313 const std::vector<int> empty; // We never send file descriptors back.
316 if (!UnixDomainSocket::SendMsg(reply_fd, write_pickle.data(), 314 if (!UnixDomainSocket::SendMsg(reply_fd, write_pickle.data(),
317 write_pickle.size(), empty)) { 315 write_pickle.size(), empty)) {
318 LOG(ERROR) << "*** send() to zygote failed"; 316 LOG(ERROR) << "*** send() to zygote failed";
319 return false; 317 return false;
320 } 318 }
321 return true; 319 return true;
322 } 320 }
323 321
324 // Read a request from the Zygote from |zygote_ipc_fd| and handle it. 322 // Read a request from the Zygote from |zygote_ipc_fd| and handle it.
325 // Die on EOF from |zygote_ipc_fd|. 323 // Die on EOF from |zygote_ipc_fd|.
326 bool HandleZygoteRequest(int zygote_ipc_fd, 324 bool HandleZygoteRequest(int zygote_ipc_fd,
327 const NaClLoaderSystemInfo& system_info) { 325 const NaClLoaderSystemInfo& system_info) {
328 std::vector<int> fds; 326 ScopedVector<base::ScopedFD> fds;
329 char buf[kNaClMaxIPCMessageLength]; 327 char buf[kNaClMaxIPCMessageLength];
330 const ssize_t msglen = UnixDomainSocket::RecvMsg(zygote_ipc_fd, 328 const ssize_t msglen = UnixDomainSocket::RecvMsg(zygote_ipc_fd,
331 &buf, sizeof(buf), &fds); 329 &buf, sizeof(buf), &fds);
332 // If the Zygote has started handling requests, we should be sandboxed via 330 // If the Zygote has started handling requests, we should be sandboxed via
333 // the setuid sandbox. 331 // the setuid sandbox.
334 if (!IsSandboxed()) { 332 if (!IsSandboxed()) {
335 LOG(ERROR) << "NaCl helper process running without a sandbox!\n" 333 LOG(ERROR) << "NaCl helper process running without a sandbox!\n"
336 << "Most likely you need to configure your SUID sandbox " 334 << "Most likely you need to configure your SUID sandbox "
337 << "correctly"; 335 << "correctly";
338 } 336 }
339 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) { 337 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) {
340 // EOF from the browser. Goodbye! 338 // EOF from the browser. Goodbye!
341 _exit(0); 339 _exit(0);
342 } 340 }
343 if (msglen < 0) { 341 if (msglen < 0) {
344 PLOG(ERROR) << "nacl_helper: receive from zygote failed"; 342 PLOG(ERROR) << "nacl_helper: receive from zygote failed";
345 return false; 343 return false;
346 } 344 }
347 345
348 Pickle read_pickle(buf, msglen); 346 Pickle read_pickle(buf, msglen);
349 PickleIterator read_iter(read_pickle); 347 PickleIterator read_iter(read_pickle);
350 int command_type; 348 int command_type;
351 if (!read_iter.ReadInt(&command_type)) { 349 if (!read_iter.ReadInt(&command_type)) {
352 LOG(ERROR) << "Unable to read command from Zygote"; 350 LOG(ERROR) << "Unable to read command from Zygote";
353 return false; 351 return false;
354 } 352 }
355 return HonorRequestAndReply(zygote_ipc_fd, command_type, fds, system_info, 353 return HonorRequestAndReply(
356 &read_iter); 354 zygote_ipc_fd, command_type, fds.Pass(), system_info, &read_iter);
357 } 355 }
358 356
359 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero"; 357 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero";
360 static const char kNaClHelperRDebug[] = "r_debug"; 358 static const char kNaClHelperRDebug[] = "r_debug";
361 359
362 // Since we were started by nacl_helper_bootstrap rather than in the 360 // Since we were started by nacl_helper_bootstrap rather than in the
363 // usual way, the debugger cannot figure out where our executable 361 // usual way, the debugger cannot figure out where our executable
364 // or the dynamic linker or the shared libraries are in memory, 362 // or the dynamic linker or the shared libraries are in memory,
365 // so it won't find any symbols. But we can fake it out to find us. 363 // so it won't find any symbols. But we can fake it out to find us.
366 // 364 //
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 // Now handle requests from the Zygote. 487 // Now handle requests from the Zygote.
490 while (true) { 488 while (true) {
491 bool request_handled = HandleZygoteRequest(kNaClZygoteDescriptor, 489 bool request_handled = HandleZygoteRequest(kNaClZygoteDescriptor,
492 system_info); 490 system_info);
493 // Do not turn this into a CHECK() without thinking about robustness 491 // Do not turn this into a CHECK() without thinking about robustness
494 // against malicious IPC requests. 492 // against malicious IPC requests.
495 DCHECK(request_handled); 493 DCHECK(request_handled);
496 } 494 }
497 NOTREACHED(); 495 NOTREACHED();
498 } 496 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698