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

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: Sync and resolve conflicts with r266735 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/sandbox_linux/nacl_sandbox_linux.h" 37 #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h"
(...skipping 18 matching lines...) Expand all
54 int pipefd[2]; 56 int pipefd[2];
55 PCHECK(0 == pipe(pipefd)); 57 PCHECK(0 == pipe(pipefd));
56 PCHECK(-1 != dup2(pipefd[0], file_descriptor)); 58 PCHECK(-1 != dup2(pipefd[0], file_descriptor));
57 PCHECK(0 == IGNORE_EINTR(close(pipefd[0]))); 59 PCHECK(0 == IGNORE_EINTR(close(pipefd[0])));
58 PCHECK(0 == IGNORE_EINTR(close(pipefd[1]))); 60 PCHECK(0 == IGNORE_EINTR(close(pipefd[1])));
59 } 61 }
60 62
61 // The child must mimic the behavior of zygote_main_linux.cc on the child 63 // The child must mimic the behavior of zygote_main_linux.cc on the child
62 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from 64 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from
63 // if (!child) { 65 // if (!child) {
64 void BecomeNaClLoader(const std::vector<int>& child_fds, 66 void BecomeNaClLoader(base::ScopedFD browser_fd,
65 const NaClLoaderSystemInfo& system_info, 67 const NaClLoaderSystemInfo& system_info,
66 bool uses_nonsfi_mode, 68 bool uses_nonsfi_mode,
67 nacl::NaClSandbox* nacl_sandbox) { 69 nacl::NaClSandbox* nacl_sandbox) {
68 DCHECK(nacl_sandbox); 70 DCHECK(nacl_sandbox);
69 VLOG(1) << "NaCl loader: setting up IPC descriptor"; 71 VLOG(1) << "NaCl loader: setting up IPC descriptor";
70 // Close or shutdown IPC channels that we don't need anymore. 72 // Close or shutdown IPC channels that we don't need anymore.
71 PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor))); 73 PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor)));
72 // In Non-SFI mode, it's important to close any non-expected IPC channels. 74 // In Non-SFI mode, it's important to close any non-expected IPC channels.
73 if (uses_nonsfi_mode) { 75 if (uses_nonsfi_mode) {
74 // The low-level kSandboxIPCChannel is used by renderers and NaCl for 76 // The low-level kSandboxIPCChannel is used by renderers and NaCl for
75 // various operations. See the LinuxSandbox::METHOD_* methods. NaCl uses 77 // various operations. See the LinuxSandbox::METHOD_* methods. NaCl uses
76 // LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT in SFI mode, so this 78 // LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT in SFI mode, so this
77 // should only be closed in Non-SFI mode. 79 // should only be closed in Non-SFI mode.
78 // This file descriptor is insidiously used by a number of APIs. Closing it 80 // This file descriptor is insidiously used by a number of APIs. Closing it
79 // could lead to difficult to debug issues. Instead of closing it, replace 81 // could lead to difficult to debug issues. Instead of closing it, replace
80 // it with a dummy. 82 // it with a dummy.
81 const int sandbox_ipc_channel = 83 const int sandbox_ipc_channel =
82 base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel; 84 base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel;
83 85
84 ReplaceFDWithDummy(sandbox_ipc_channel); 86 ReplaceFDWithDummy(sandbox_ipc_channel);
85 } 87 }
86 88
87 // Finish layer-1 sandbox initialization and initialize the layer-2 sandbox. 89 // Finish layer-1 sandbox initialization and initialize the layer-2 sandbox.
88 CHECK(!nacl_sandbox->HasOpenDirectory()); 90 CHECK(!nacl_sandbox->HasOpenDirectory());
89 nacl_sandbox->InitializeLayerTwoSandbox(uses_nonsfi_mode); 91 nacl_sandbox->InitializeLayerTwoSandbox(uses_nonsfi_mode);
90 nacl_sandbox->SealLayerOneSandbox(); 92 nacl_sandbox->SealLayerOneSandbox();
91 nacl_sandbox->CheckSandboxingStateWithPolicy(); 93 nacl_sandbox->CheckSandboxingStateWithPolicy();
92 94
93 base::GlobalDescriptors::GetInstance()->Set( 95 base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel,
94 kPrimaryIPCChannel, 96 browser_fd.release());
95 child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]);
96 97
97 base::MessageLoopForIO main_message_loop; 98 base::MessageLoopForIO main_message_loop;
98 NaClListener listener; 99 NaClListener listener;
99 listener.set_uses_nonsfi_mode(uses_nonsfi_mode); 100 listener.set_uses_nonsfi_mode(uses_nonsfi_mode);
100 listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size); 101 listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size);
101 listener.set_number_of_cores(system_info.number_of_cores); 102 listener.set_number_of_cores(system_info.number_of_cores);
102 listener.Listen(); 103 listener.Listen();
103 _exit(0); 104 _exit(0);
104 } 105 }
105 106
106 // Start the NaCl loader in a child created by the NaCl loader Zygote. 107 // Start the NaCl loader in a child created by the NaCl loader Zygote.
107 void ChildNaClLoaderInit(const std::vector<int>& child_fds, 108 void ChildNaClLoaderInit(ScopedVector<base::ScopedFD> child_fds,
108 const NaClLoaderSystemInfo& system_info, 109 const NaClLoaderSystemInfo& system_info,
109 bool uses_nonsfi_mode, 110 bool uses_nonsfi_mode,
110 nacl::NaClSandbox* nacl_sandbox, 111 nacl::NaClSandbox* nacl_sandbox,
111 const std::string& channel_id) { 112 const std::string& channel_id) {
112 const int parent_fd = child_fds[content::ZygoteForkDelegate::kParentFDIndex];
113 const int dummy_fd = child_fds[content::ZygoteForkDelegate::kDummyFDIndex];
114
115 bool validack = false; 113 bool validack = false;
116 base::ProcessId real_pid; 114 base::ProcessId real_pid;
117 // Wait until the parent process has discovered our PID. We 115 // Wait until the parent process has discovered our PID. We
118 // should not fork any child processes (which the seccomp 116 // should not fork any child processes (which the seccomp
119 // sandbox does) until then, because that can interfere with the 117 // sandbox does) until then, because that can interfere with the
120 // parent's discovery of our PID. 118 // parent's discovery of our PID.
121 const ssize_t nread = 119 const ssize_t nread = HANDLE_EINTR(
122 HANDLE_EINTR(read(parent_fd, &real_pid, sizeof(real_pid))); 120 read(child_fds[content::ZygoteForkDelegate::kParentFDIndex]->get(),
121 &real_pid,
122 sizeof(real_pid)));
123 if (static_cast<size_t>(nread) == sizeof(real_pid)) { 123 if (static_cast<size_t>(nread) == sizeof(real_pid)) {
124 // Make sure the parent didn't accidentally send us our real PID. 124 // Make sure the parent didn't accidentally send us our real PID.
125 // We don't want it to be discoverable anywhere in our address space 125 // We don't want it to be discoverable anywhere in our address space
126 // when we start running untrusted code. 126 // when we start running untrusted code.
127 CHECK(real_pid == 0); 127 CHECK(real_pid == 0);
128 128
129 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 129 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
130 switches::kProcessChannelID, channel_id); 130 switches::kProcessChannelID, channel_id);
131 validack = true; 131 validack = true;
132 } else { 132 } else {
133 if (nread < 0) 133 if (nread < 0)
134 perror("read"); 134 perror("read");
135 LOG(ERROR) << "read returned " << nread; 135 LOG(ERROR) << "read returned " << nread;
136 } 136 }
137 137
138 if (IGNORE_EINTR(close(dummy_fd)) != 0) 138 base::ScopedFD browser_fd(
139 LOG(ERROR) << "close(dummy_fd) failed"; 139 child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]->Pass());
140 if (IGNORE_EINTR(close(parent_fd)) != 0) 140 child_fds.clear();
141 LOG(ERROR) << "close(parent_fd) failed"; 141
142 if (validack) { 142 if (validack) {
143 BecomeNaClLoader(child_fds, system_info, uses_nonsfi_mode, nacl_sandbox); 143 BecomeNaClLoader(
144 browser_fd.Pass(), system_info, uses_nonsfi_mode, nacl_sandbox);
144 } else { 145 } else {
145 LOG(ERROR) << "Failed to synch with zygote"; 146 LOG(ERROR) << "Failed to synch with zygote";
146 } 147 }
147 _exit(1); 148 _exit(1);
148 } 149 }
149 150
150 // Handle a fork request from the Zygote. 151 // Handle a fork request from the Zygote.
151 // Some of this code was lifted from 152 // Some of this code was lifted from
152 // content/browser/zygote_main_linux.cc:ForkWithRealPid() 153 // content/browser/zygote_main_linux.cc:ForkWithRealPid()
153 bool HandleForkRequest(const std::vector<int>& child_fds, 154 bool HandleForkRequest(ScopedVector<base::ScopedFD> child_fds,
154 const NaClLoaderSystemInfo& system_info, 155 const NaClLoaderSystemInfo& system_info,
155 nacl::NaClSandbox* nacl_sandbox, 156 nacl::NaClSandbox* nacl_sandbox,
156 PickleIterator* input_iter, 157 PickleIterator* input_iter,
157 Pickle* output_pickle) { 158 Pickle* output_pickle) {
158 bool uses_nonsfi_mode; 159 bool uses_nonsfi_mode;
159 if (!input_iter->ReadBool(&uses_nonsfi_mode)) { 160 if (!input_iter->ReadBool(&uses_nonsfi_mode)) {
160 LOG(ERROR) << "Could not read uses_nonsfi_mode status"; 161 LOG(ERROR) << "Could not read uses_nonsfi_mode status";
161 return false; 162 return false;
162 } 163 }
163 164
164 std::string channel_id; 165 std::string channel_id;
165 if (!input_iter->ReadString(&channel_id)) { 166 if (!input_iter->ReadString(&channel_id)) {
166 LOG(ERROR) << "Could not read channel_id string"; 167 LOG(ERROR) << "Could not read channel_id string";
167 return false; 168 return false;
168 } 169 }
169 170
170 if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) { 171 if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) {
171 LOG(ERROR) << "nacl_helper: unexpected number of fds, got " 172 LOG(ERROR) << "nacl_helper: unexpected number of fds, got "
172 << child_fds.size(); 173 << child_fds.size();
173 return false; 174 return false;
174 } 175 }
175 176
176 VLOG(1) << "nacl_helper: forking"; 177 VLOG(1) << "nacl_helper: forking";
177 pid_t child_pid = fork(); 178 pid_t child_pid = fork();
178 if (child_pid < 0) { 179 if (child_pid < 0) {
179 PLOG(ERROR) << "*** fork() failed."; 180 PLOG(ERROR) << "*** fork() failed.";
180 } 181 }
181 182
182 if (child_pid == 0) { 183 if (child_pid == 0) {
183 ChildNaClLoaderInit( 184 ChildNaClLoaderInit(child_fds.Pass(),
184 child_fds, system_info, uses_nonsfi_mode, nacl_sandbox, channel_id); 185 system_info,
186 uses_nonsfi_mode,
187 nacl_sandbox,
188 channel_id);
185 NOTREACHED(); 189 NOTREACHED();
186 } 190 }
187 191
188 // I am the parent. 192 // I am the parent.
189 // First, close the dummy_fd so the sandbox won't find me when 193 // First, close the dummy_fd so the sandbox won't find me when
190 // looking for the child's pid in /proc. Also close other fds. 194 // looking for the child's pid in /proc. Also close other fds.
191 for (size_t i = 0; i < child_fds.size(); i++) { 195 child_fds.clear();
192 if (IGNORE_EINTR(close(child_fds[i])) != 0)
193 LOG(ERROR) << "close(child_fds[i]) failed";
194 }
195 VLOG(1) << "nacl_helper: child_pid is " << child_pid; 196 VLOG(1) << "nacl_helper: child_pid is " << child_pid;
196 197
197 // Now send child_pid (eventually -1 if fork failed) to the Chrome Zygote. 198 // Now send child_pid (eventually -1 if fork failed) to the Chrome Zygote.
198 output_pickle->WriteInt(child_pid); 199 output_pickle->WriteInt(child_pid);
199 return true; 200 return true;
200 } 201 }
201 202
202 bool HandleGetTerminationStatusRequest(PickleIterator* input_iter, 203 bool HandleGetTerminationStatusRequest(PickleIterator* input_iter,
203 Pickle* output_pickle) { 204 Pickle* output_pickle) {
204 pid_t child_to_wait; 205 pid_t child_to_wait;
(...skipping 22 matching lines...) Expand all
227 output_pickle->WriteInt(exit_code); 228 output_pickle->WriteInt(exit_code);
228 return true; 229 return true;
229 } 230 }
230 231
231 // Honor a command |command_type|. Eventual command parameters are 232 // Honor a command |command_type|. Eventual command parameters are
232 // available in |input_iter| and eventual file descriptors attached to 233 // available in |input_iter| and eventual file descriptors attached to
233 // the command are in |attached_fds|. 234 // the command are in |attached_fds|.
234 // Reply to the command on |reply_fds|. 235 // Reply to the command on |reply_fds|.
235 bool HonorRequestAndReply(int reply_fd, 236 bool HonorRequestAndReply(int reply_fd,
236 int command_type, 237 int command_type,
237 const std::vector<int>& attached_fds, 238 ScopedVector<base::ScopedFD> attached_fds,
238 const NaClLoaderSystemInfo& system_info, 239 const NaClLoaderSystemInfo& system_info,
239 nacl::NaClSandbox* nacl_sandbox, 240 nacl::NaClSandbox* nacl_sandbox,
240 PickleIterator* input_iter) { 241 PickleIterator* input_iter) {
241 Pickle write_pickle; 242 Pickle write_pickle;
242 bool have_to_reply = false; 243 bool have_to_reply = false;
243 // Commands must write anything to send back to |write_pickle|. 244 // Commands must write anything to send back to |write_pickle|.
244 switch (command_type) { 245 switch (command_type) {
245 case nacl::kNaClForkRequest: 246 case nacl::kNaClForkRequest:
246 have_to_reply = HandleForkRequest( 247 have_to_reply = HandleForkRequest(attached_fds.Pass(),
247 attached_fds, system_info, nacl_sandbox, input_iter, &write_pickle); 248 system_info,
249 nacl_sandbox,
250 input_iter,
251 &write_pickle);
248 break; 252 break;
249 case nacl::kNaClGetTerminationStatusRequest: 253 case nacl::kNaClGetTerminationStatusRequest:
250 have_to_reply = 254 have_to_reply =
251 HandleGetTerminationStatusRequest(input_iter, &write_pickle); 255 HandleGetTerminationStatusRequest(input_iter, &write_pickle);
252 break; 256 break;
253 default: 257 default:
254 LOG(ERROR) << "Unsupported command from Zygote"; 258 LOG(ERROR) << "Unsupported command from Zygote";
255 return false; 259 return false;
256 } 260 }
257 if (!have_to_reply) 261 if (!have_to_reply)
258 return false; 262 return false;
259 const std::vector<int> empty; // We never send file descriptors back. 263 const std::vector<int> empty; // We never send file descriptors back.
260 if (!UnixDomainSocket::SendMsg(reply_fd, write_pickle.data(), 264 if (!UnixDomainSocket::SendMsg(reply_fd, write_pickle.data(),
261 write_pickle.size(), empty)) { 265 write_pickle.size(), empty)) {
262 LOG(ERROR) << "*** send() to zygote failed"; 266 LOG(ERROR) << "*** send() to zygote failed";
263 return false; 267 return false;
264 } 268 }
265 return true; 269 return true;
266 } 270 }
267 271
268 // Read a request from the Zygote from |zygote_ipc_fd| and handle it. 272 // Read a request from the Zygote from |zygote_ipc_fd| and handle it.
269 // Die on EOF from |zygote_ipc_fd|. 273 // Die on EOF from |zygote_ipc_fd|.
270 bool HandleZygoteRequest(int zygote_ipc_fd, 274 bool HandleZygoteRequest(int zygote_ipc_fd,
271 const NaClLoaderSystemInfo& system_info, 275 const NaClLoaderSystemInfo& system_info,
272 nacl::NaClSandbox* nacl_sandbox) { 276 nacl::NaClSandbox* nacl_sandbox) {
273 std::vector<int> fds; 277 ScopedVector<base::ScopedFD> fds;
274 char buf[kNaClMaxIPCMessageLength]; 278 char buf[kNaClMaxIPCMessageLength];
275 const ssize_t msglen = UnixDomainSocket::RecvMsg(zygote_ipc_fd, 279 const ssize_t msglen = UnixDomainSocket::RecvMsg(zygote_ipc_fd,
276 &buf, sizeof(buf), &fds); 280 &buf, sizeof(buf), &fds);
277 // If the Zygote has started handling requests, we should be sandboxed via 281 // If the Zygote has started handling requests, we should be sandboxed via
278 // the setuid sandbox. 282 // the setuid sandbox.
279 if (!nacl_sandbox->layer_one_enabled()) { 283 if (!nacl_sandbox->layer_one_enabled()) {
280 LOG(ERROR) << "NaCl helper process running without a sandbox!\n" 284 LOG(ERROR) << "NaCl helper process running without a sandbox!\n"
281 << "Most likely you need to configure your SUID sandbox " 285 << "Most likely you need to configure your SUID sandbox "
282 << "correctly"; 286 << "correctly";
283 } 287 }
284 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) { 288 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) {
285 // EOF from the browser. Goodbye! 289 // EOF from the browser. Goodbye!
286 _exit(0); 290 _exit(0);
287 } 291 }
288 if (msglen < 0) { 292 if (msglen < 0) {
289 PLOG(ERROR) << "nacl_helper: receive from zygote failed"; 293 PLOG(ERROR) << "nacl_helper: receive from zygote failed";
290 return false; 294 return false;
291 } 295 }
292 296
293 Pickle read_pickle(buf, msglen); 297 Pickle read_pickle(buf, msglen);
294 PickleIterator read_iter(read_pickle); 298 PickleIterator read_iter(read_pickle);
295 int command_type; 299 int command_type;
296 if (!read_iter.ReadInt(&command_type)) { 300 if (!read_iter.ReadInt(&command_type)) {
297 LOG(ERROR) << "Unable to read command from Zygote"; 301 LOG(ERROR) << "Unable to read command from Zygote";
298 return false; 302 return false;
299 } 303 }
300 return HonorRequestAndReply( 304 return HonorRequestAndReply(zygote_ipc_fd,
301 zygote_ipc_fd, command_type, fds, system_info, nacl_sandbox, &read_iter); 305 command_type,
306 fds.Pass(),
307 system_info,
308 nacl_sandbox,
309 &read_iter);
302 } 310 }
303 311
304 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero"; 312 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero";
305 static const char kNaClHelperRDebug[] = "r_debug"; 313 static const char kNaClHelperRDebug[] = "r_debug";
306 314
307 // Since we were started by nacl_helper_bootstrap rather than in the 315 // Since we were started by nacl_helper_bootstrap rather than in the
308 // usual way, the debugger cannot figure out where our executable 316 // usual way, the debugger cannot figure out where our executable
309 // or the dynamic linker or the shared libraries are in memory, 317 // or the dynamic linker or the shared libraries are in memory,
310 // so it won't find any symbols. But we can fake it out to find us. 318 // so it won't find any symbols. But we can fake it out to find us.
311 // 319 //
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 // Now handle requests from the Zygote. 445 // Now handle requests from the Zygote.
438 while (true) { 446 while (true) {
439 bool request_handled = HandleZygoteRequest( 447 bool request_handled = HandleZygoteRequest(
440 kNaClZygoteDescriptor, system_info, nacl_sandbox.get()); 448 kNaClZygoteDescriptor, system_info, nacl_sandbox.get());
441 // Do not turn this into a CHECK() without thinking about robustness 449 // Do not turn this into a CHECK() without thinking about robustness
442 // against malicious IPC requests. 450 // against malicious IPC requests.
443 DCHECK(request_handled); 451 DCHECK(request_handled);
444 } 452 }
445 NOTREACHED(); 453 NOTREACHED();
446 } 454 }
OLDNEW
« no previous file with comments | « base/posix/unix_domain_socket_linux_unittest.cc ('k') | components/nacl/zygote/nacl_fork_delegate_linux.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698