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. 4 5 #include "build/build_config.h" 6 7 #include "chrome/browser/nacl_host/nacl_process_host.h" 8 9 #if defined(OS_POSIX) 10 #include <fcntl.h> 11 #endif 12 13 #include "base/command_line.h" 14 #include "base/metrics/nacl_histogram.h" 15 #include "base/utf_string_conversions.h" 16 #include "base/win/windows_version.h" 17 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/logging_chrome.h" 19 #include "chrome/common/nacl_cmd_line.h" 20 #include "chrome/common/nacl_messages.h" 21 #include "chrome/common/render_messages.h" 22 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" 23 #include "ipc/ipc_switches.h" 24 #include "native_client/src/shared/imc/nacl_imc.h" 25 26 #if defined(OS_POSIX) 27 #include "ipc/ipc_channel_posix.h" 28 #elif defined(OS_WIN) 29 #include "chrome/browser/nacl_host/nacl_broker_service_win.h" 30 #endif 31 32 namespace { 33 34 #if !defined(DISABLE_NACL) 35 void SetCloseOnExec(nacl::Handle fd) { 36 #if defined(OS_POSIX) 37 int flags = fcntl(fd, F_GETFD); 38 CHECK(flags != -1); 39 int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 40 CHECK(rc == 0); 41 #endif 42 } 43 #endif 44 45 } // namespace 46 47 struct NaClProcessHost::NaClInternal { 48 std::vector<nacl::Handle> sockets_for_renderer; 49 std::vector<nacl::Handle> sockets_for_sel_ldr; 50 }; 51 52 NaClProcessHost::NaClProcessHost(const std::wstring& url) 53 : BrowserChildProcessHost(NACL_LOADER_PROCESS), 54 reply_msg_(NULL), 55 internal_(new NaClInternal()), 56 running_on_wow64_(false) { 57 set_name(url); 58 #if defined(OS_WIN) 59 running_on_wow64_ = (base::win::OSInfo::GetInstance()->wow64_status() == 60 base::win::OSInfo::WOW64_ENABLED); 61 #endif 62 } 63 64 NaClProcessHost::~NaClProcessHost() { 65 if (!reply_msg_) 66 return; 67 68 // nacl::Close() is not available at link time if DISABLE_NACL is 69 // defined, but we still compile a bunch of other code from this 70 // file anyway. TODO(mseaborn): Make this less messy. 71 #ifndef DISABLE_NACL 72 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { 73 nacl::Close(internal_->sockets_for_renderer[i]); 74 } 75 for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { 76 nacl::Close(internal_->sockets_for_sel_ldr[i]); 77 } 78 #endif 79 80 // OnProcessLaunched didn't get called because the process couldn't launch. 81 // Don't keep the renderer hanging. 82 reply_msg_->set_reply_error(); 83 chrome_render_message_filter_->Send(reply_msg_); 84 } 85 86 bool NaClProcessHost::Launch( 87 ChromeRenderMessageFilter* chrome_render_message_filter, 88 int socket_count, 89 IPC::Message* reply_msg) { 90 #ifdef DISABLE_NACL 91 NOTIMPLEMENTED() << "Native Client disabled at build time"; 92 return false; 93 #else 94 // Place an arbitrary limit on the number of sockets to limit 95 // exposure in case the renderer is compromised. We can increase 96 // this if necessary. 97 if (socket_count > 8) { 98 return false; 99 } 100 101 // Rather than creating a socket pair in the renderer, and passing 102 // one side through the browser to sel_ldr, socket pairs are created 103 // in the browser and then passed to the renderer and sel_ldr. 104 // 105 // This is mainly for the benefit of Windows, where sockets cannot 106 // be passed in messages, but are copied via DuplicateHandle(). 107 // This means the sandboxed renderer cannot send handles to the 108 // browser process. 109 110 for (int i = 0; i < socket_count; i++) { 111 nacl::Handle pair[2]; 112 // Create a connected socket 113 if (nacl::SocketPair(pair) == -1) 114 return false; 115 internal_->sockets_for_renderer.push_back(pair[0]); 116 internal_->sockets_for_sel_ldr.push_back(pair[1]); 117 SetCloseOnExec(pair[0]); 118 SetCloseOnExec(pair[1]); 119 } 120 121 // Launch the process 122 if (!LaunchSelLdr()) { 123 return false; 124 } 125 UmaNaclHistogramEnumeration(NACL_STARTED); 126 chrome_render_message_filter_ = chrome_render_message_filter; 127 reply_msg_ = reply_msg; 128 129 return true; 130 #endif // DISABLE_NACL 131 } 132 133 bool NaClProcessHost::LaunchSelLdr() { 134 if (!CreateChannel()) 135 return false; 136 137 // Build command line for nacl. 138 FilePath exe_path = GetChildPath(true); 139 if (exe_path.empty()) 140 return false; 141 142 CommandLine* cmd_line = new CommandLine(exe_path); 143 nacl::CopyNaClCommandLineArguments(cmd_line); 144 145 cmd_line->AppendSwitchASCII(switches::kProcessType, 146 switches::kNaClLoaderProcess); 147 148 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); 149 150 SetCrashReporterCommandLine(cmd_line); 151 152 // On Windows we might need to start the broker process to launch a new loader 153 #if defined(OS_WIN) 154 if (running_on_wow64_) { 155 return NaClBrokerService::GetInstance()->LaunchLoader( 156 this, ASCIIToWide(channel_id())); 157 } else { 158 BrowserChildProcessHost::Launch(FilePath(), cmd_line); 159 } 160 #elif defined(OS_POSIX) 161 BrowserChildProcessHost::Launch(true, // use_zygote 162 base::environment_vector(), 163 cmd_line); 164 #endif 165 166 return true; 167 } 168 169 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) { 170 set_handle(handle); 171 OnProcessLaunched(); 172 } 173 174 base::TerminationStatus NaClProcessHost::GetChildTerminationStatus( 175 int* exit_code) { 176 if (running_on_wow64_) 177 return base::GetTerminationStatus(handle(), exit_code); 178 return BrowserChildProcessHost::GetChildTerminationStatus(exit_code); 179 } 180 181 void NaClProcessHost::OnChildDied() { 182 #if defined(OS_WIN) 183 NaClBrokerService::GetInstance()->OnLoaderDied(); 184 #endif 185 BrowserChildProcessHost::OnChildDied(); 186 } 187 188 void NaClProcessHost::OnProcessLaunched() { 189 std::vector<nacl::FileDescriptor> handles_for_renderer; 190 base::ProcessHandle nacl_process_handle; 191 192 for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { 193 #if defined(OS_WIN) 194 // Copy the handle into the renderer process. 195 HANDLE handle_in_renderer; 196 DuplicateHandle(base::GetCurrentProcessHandle(), 197 reinterpret_cast<HANDLE>( 198 internal_->sockets_for_renderer[i]), 199 chrome_render_message_filter_->peer_handle(), 200 &handle_in_renderer, 201 GENERIC_READ | GENERIC_WRITE, 202 FALSE, 203 DUPLICATE_CLOSE_SOURCE); 204 handles_for_renderer.push_back( 205 reinterpret_cast<nacl::FileDescriptor>(handle_in_renderer)); 206 #else 207 // No need to dup the imc_handle - we don't pass it anywhere else so 208 // it cannot be closed. 209 nacl::FileDescriptor imc_handle; 210 imc_handle.fd = internal_->sockets_for_renderer[i]; 211 imc_handle.auto_close = true; 212 handles_for_renderer.push_back(imc_handle); 213 #endif 214 } 215 216 #if defined(OS_WIN) 217 // Copy the process handle into the renderer process. 218 DuplicateHandle(base::GetCurrentProcessHandle(), 219 handle(), 220 chrome_render_message_filter_->peer_handle(), 221 &nacl_process_handle, 222 PROCESS_DUP_HANDLE, 223 FALSE, 224 0); 225 #else 226 // We use pid as process handle on Posix 227 nacl_process_handle = handle(); 228 #endif 229 230 // Get the pid of the NaCl process 231 base::ProcessId nacl_process_id = base::GetProcId(handle()); 232 233 ViewHostMsg_LaunchNaCl::WriteReplyParams( 234 reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id); 235 chrome_render_message_filter_->Send(reply_msg_); 236 chrome_render_message_filter_ = NULL; 237 reply_msg_ = NULL; 238 internal_->sockets_for_renderer.clear(); 239 240 SendStartMessage(); 241 } 242 243 void NaClProcessHost::SendStartMessage() { 244 std::vector<nacl::FileDescriptor> handles_for_sel_ldr; 245 for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { 246 #if defined(OS_WIN) 247 HANDLE channel; 248 if (!DuplicateHandle(GetCurrentProcess(), 249 reinterpret_cast<HANDLE>( 250 internal_->sockets_for_sel_ldr[i]), 251 handle(), 252 &channel, 253 GENERIC_READ | GENERIC_WRITE, 254 FALSE, DUPLICATE_CLOSE_SOURCE)) { 255 return; 256 } 257 handles_for_sel_ldr.push_back( 258 reinterpret_cast<nacl::FileDescriptor>(channel)); 259 #else 260 nacl::FileDescriptor channel; 261 channel.fd = dup(internal_->sockets_for_sel_ldr[i]); 262 if (channel.fd < 0) { 263 LOG(ERROR) << "Failed to dup() a file descriptor"; 264 return; 265 } 266 channel.auto_close = true; 267 handles_for_sel_ldr.push_back(channel); 268 #endif 269 } 270 271 #if defined(OS_MACOSX) 272 // For dynamic loading support, NaCl requires a file descriptor that 273 // was created in /tmp, since those created with shm_open() are not 274 // mappable with PROT_EXEC. Rather than requiring an extra IPC 275 // round trip out of the sandbox, we create an FD here. 276 base::SharedMemory memory_buffer; 277 if (!memory_buffer.CreateAnonymous(/* size= */ 1)) { 278 LOG(ERROR) << "Failed to allocate memory buffer"; 279 return; 280 } 281 nacl::FileDescriptor memory_fd; 282 memory_fd.fd = dup(memory_buffer.handle().fd); 283 if (memory_fd.fd < 0) { 284 LOG(ERROR) << "Failed to dup() a file descriptor"; 285 return; 286 } 287 memory_fd.auto_close = true; 288 handles_for_sel_ldr.push_back(memory_fd); 289 #endif 290 291 Send(new NaClProcessMsg_Start(handles_for_sel_ldr)); 292 internal_->sockets_for_sel_ldr.clear(); 293 } 294 295 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { 296 NOTREACHED() << "Invalid message with type = " << msg.type(); 297 return false; 298 } 299 300 bool NaClProcessHost::CanShutdown() { 301 return true; 302 } 303