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 3 // found in the LICENSE file. 4 5 #include "components/nacl/loader/nacl_listener.h" 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #if defined(OS_POSIX) 13 #include <unistd.h> 14 #endif 15 16 #include "base/command_line.h" 17 #include "base/logging.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "base/message_loop/message_loop.h" 20 #include "base/rand_util.h" 21 #include "components/nacl/common/nacl_messages.h" 22 #include "components/nacl/common/nacl_renderer_messages.h" 23 #include "components/nacl/loader/nacl_ipc_adapter.h" 24 #include "components/nacl/loader/nacl_validation_db.h" 25 #include "components/nacl/loader/nacl_validation_query.h" 26 #include "ipc/ipc_channel_handle.h" 27 #include "ipc/ipc_switches.h" 28 #include "ipc/ipc_sync_channel.h" 29 #include "ipc/ipc_sync_message_filter.h" 30 #include "native_client/src/public/chrome_main.h" 31 #include "native_client/src/public/nacl_app.h" 32 #include "native_client/src/public/nacl_file_info.h" 33 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h" 34 35 #if defined(OS_POSIX) 36 #include "base/file_descriptor_posix.h" 37 #endif 38 39 #if defined(OS_LINUX) 40 #include "content/public/common/child_process_sandbox_support_linux.h" 41 #endif 42 43 #if defined(OS_WIN) 44 #include <fcntl.h> 45 #include <io.h> 46 47 #include "content/public/common/sandbox_init.h" 48 #endif 49 50 namespace { 51 52 NaClListener* g_listener; 53 54 void FatalLogHandler(const char* data, size_t bytes) { 55 // We use uint32_t rather than size_t for the case when the browser and NaCl 56 // processes are a mix of 32-bit and 64-bit processes. 57 uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes), 58 nacl::kNaClCrashInfoMaxLogSize); 59 60 // We copy the length of the crash data to the start of the shared memory 61 // segment so we know how much to copy. 62 memcpy(g_listener->crash_info_shmem_memory(), ©_bytes, sizeof(uint32_t)); 63 64 memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t), 65 data, 66 copy_bytes); 67 } 68 69 #if defined(OS_MACOSX) 70 71 // On Mac OS X, shm_open() works in the sandbox but does not give us 72 // an FD that we can map as PROT_EXEC. Rather than doing an IPC to 73 // get an executable SHM region when CreateMemoryObject() is called, 74 // we preallocate one on startup, since NaCl's sel_ldr only needs one 75 // of them. This saves a round trip. 76 77 base::subtle::Atomic32 g_shm_fd = -1; 78 79 int CreateMemoryObject(size_t size, int executable) { 80 if (executable && size > 0) { 81 int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1); 82 if (result_fd != -1) { 83 // ftruncate() is disallowed by the Mac OS X sandbox and 84 // returns EPERM. Luckily, we can get the same effect with 85 // lseek() + write(). 86 if (lseek(result_fd, size - 1, SEEK_SET) == -1) { 87 LOG(ERROR) << "lseek() failed: " << errno; 88 return -1; 89 } 90 if (write(result_fd, "", 1) != 1) { 91 LOG(ERROR) << "write() failed: " << errno; 92 return -1; 93 } 94 return result_fd; 95 } 96 } 97 // Fall back to NaCl's default implementation. 98 return -1; 99 } 100 101 #elif defined(OS_LINUX) 102 103 int CreateMemoryObject(size_t size, int executable) { 104 return content::MakeSharedMemorySegmentViaIPC(size, executable); 105 } 106 107 #elif defined(OS_WIN) 108 // We wrap the function to convert the bool return value to an int. 109 int BrokerDuplicateHandle(NaClHandle source_handle, 110 uint32_t process_id, 111 NaClHandle* target_handle, 112 uint32_t desired_access, 113 uint32_t options) { 114 return content::BrokerDuplicateHandle(source_handle, process_id, 115 target_handle, desired_access, 116 options); 117 } 118 119 int AttachDebugExceptionHandler(const void* info, size_t info_size) { 120 std::string info_string(reinterpret_cast<const char*>(info), info_size); 121 bool result = false; 122 if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler( 123 info_string, &result))) 124 return false; 125 return result; 126 } 127 128 void DebugStubPortSelectedHandler(uint16_t port) { 129 g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port)); 130 } 131 132 #endif 133 134 // Creates the PPAPI IPC channel between the NaCl IRT and the host 135 // (browser/renderer) process, and starts to listen it on the thread where 136 // the given message_loop_proxy runs. 137 // Also, creates and sets the corresponding NaClDesc to the given nap with 138 // the FD #. 139 scoped_refptr<NaClIPCAdapter> SetUpIPCAdapter( 140 IPC::ChannelHandle* handle, 141 scoped_refptr<base::MessageLoopProxy> message_loop_proxy, 142 struct NaClApp* nap, 143 int nacl_fd) { 144 scoped_refptr<NaClIPCAdapter> ipc_adapter( 145 new NaClIPCAdapter(*handle, message_loop_proxy.get())); 146 ipc_adapter->ConnectChannel(); 147 #if defined(OS_POSIX) 148 handle->socket = 149 base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor(), true); 150 #endif 151 152 // Pass a NaClDesc to the untrusted side. This will hold a ref to the 153 // NaClIPCAdapter. 154 NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc()); 155 return ipc_adapter; 156 } 157 158 } // namespace 159 160 class BrowserValidationDBProxy : public NaClValidationDB { 161 public: 162 explicit BrowserValidationDBProxy(NaClListener* listener) 163 : listener_(listener) { 164 } 165 166 virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE { 167 // Initialize to false so that if the Send fails to write to the return 168 // value we're safe. For example if the message is (for some reason) 169 // dispatched as an async message the return parameter will not be written. 170 bool result = false; 171 if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature, 172 &result))) { 173 LOG(ERROR) << "Failed to query NaCl validation cache."; 174 result = false; 175 } 176 return result; 177 } 178 179 virtual void SetKnownToValidate(const std::string& signature) OVERRIDE { 180 // Caching is optional: NaCl will still work correctly if the IPC fails. 181 if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) { 182 LOG(ERROR) << "Failed to update NaCl validation cache."; 183 } 184 } 185 186 // This is the "old" code path for resolving file tokens. It's only 187 // used for resolving the main nexe. 188 // TODO(teravest): Remove this. 189 virtual bool ResolveFileToken(struct NaClFileToken* file_token, 190 int32* fd, std::string* path) OVERRIDE { 191 *fd = -1; 192 *path = ""; 193 if (!NaClFileTokenIsValid(file_token)) { 194 return false; 195 } 196 IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit(); 197 base::FilePath ipc_path; 198 if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo, 199 file_token->hi, 200 &ipc_fd, 201 &ipc_path))) { 202 return false; 203 } 204 if (ipc_fd == IPC::InvalidPlatformFileForTransit()) { 205 return false; 206 } 207 base::PlatformFile handle = 208 IPC::PlatformFileForTransitToPlatformFile(ipc_fd); 209 #if defined(OS_WIN) 210 // On Windows, valid handles are 32 bit unsigned integers so this is safe. 211 *fd = reinterpret_cast<uintptr_t>(handle); 212 #else 213 *fd = handle; 214 #endif 215 // It doesn't matter if the path is invalid UTF8 as long as it's consistent 216 // and unforgeable. 217 *path = ipc_path.AsUTF8Unsafe(); 218 return true; 219 } 220 221 private: 222 // The listener never dies, otherwise this might be a dangling reference. 223 NaClListener* listener_; 224 }; 225 226 227 NaClListener::NaClListener() : shutdown_event_(true, false), 228 io_thread_("NaCl_IOThread"), 229 #if defined(OS_LINUX) 230 prereserved_sandbox_size_(0), 231 #endif 232 #if defined(OS_POSIX) 233 number_of_cores_(-1), // unknown/error 234 #endif 235 main_loop_(NULL) { 236 io_thread_.StartWithOptions( 237 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 238 DCHECK(g_listener == NULL); 239 g_listener = this; 240 } 241 242 NaClListener::~NaClListener() { 243 NOTREACHED(); 244 shutdown_event_.Signal(); 245 g_listener = NULL; 246 } 247 248 bool NaClListener::Send(IPC::Message* msg) { 249 DCHECK(main_loop_ != NULL); 250 if (base::MessageLoop::current() == main_loop_) { 251 // This thread owns the channel. 252 return channel_->Send(msg); 253 } else { 254 // This thread does not own the channel. 255 return filter_->Send(msg); 256 } 257 } 258 259 // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be 260 // processed in a MessageFilter so it can be handled on the IO thread. 261 // The main thread used by NaClListener is busy in 262 // NaClChromeMainAppStart(), so it can't be used for servicing messages. 263 class FileTokenMessageFilter : public IPC::MessageFilter { 264 public: 265 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { 266 bool handled = true; 267 IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg) 268 IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenAsyncReply, 269 OnResolveFileTokenAsyncReply) 270 IPC_MESSAGE_UNHANDLED(handled = false) 271 IPC_END_MESSAGE_MAP() 272 return handled; 273 } 274 275 void OnResolveFileTokenAsyncReply( 276 uint64_t token_lo, 277 uint64_t token_hi, 278 IPC::PlatformFileForTransit ipc_fd, 279 base::FilePath file_path) { 280 CHECK(g_listener); 281 g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path); 282 } 283 private: 284 virtual ~FileTokenMessageFilter() { } 285 }; 286 287 void NaClListener::Listen() { 288 std::string channel_name = 289 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 290 switches::kProcessChannelID); 291 channel_ = IPC::SyncChannel::Create( 292 this, io_thread_.message_loop_proxy().get(), &shutdown_event_); 293 filter_ = new IPC::SyncMessageFilter(&shutdown_event_); 294 channel_->AddFilter(filter_.get()); 295 channel_->AddFilter(new FileTokenMessageFilter()); 296 channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true); 297 main_loop_ = base::MessageLoop::current(); 298 main_loop_->Run(); 299 } 300 301 bool NaClListener::OnMessageReceived(const IPC::Message& msg) { 302 bool handled = true; 303 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg) 304 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart) 305 IPC_MESSAGE_UNHANDLED(handled = false) 306 IPC_END_MESSAGE_MAP() 307 return handled; 308 } 309 310 void NaClListener::OnStart(const nacl::NaClStartParams& params) { 311 #if defined(OS_LINUX) || defined(OS_MACOSX) 312 int urandom_fd = dup(base::GetUrandomFD()); 313 if (urandom_fd < 0) { 314 LOG(ERROR) << "Failed to dup() the urandom FD"; 315 return; 316 } 317 NaClChromeMainSetUrandomFd(urandom_fd); 318 #endif 319 struct NaClApp* nap = NULL; 320 NaClChromeMainInit(); 321 322 crash_info_shmem_.reset(new base::SharedMemory(params.crash_info_shmem_handle, 323 false)); 324 CHECK(crash_info_shmem_->Map(nacl::kNaClCrashInfoShmemSize)); 325 NaClSetFatalErrorCallback(&FatalLogHandler); 326 327 nap = NaClAppCreate(); 328 if (nap == NULL) { 329 LOG(ERROR) << "NaClAppCreate() failed"; 330 return; 331 } 332 333 IPC::ChannelHandle browser_handle; 334 IPC::ChannelHandle ppapi_renderer_handle; 335 IPC::ChannelHandle manifest_service_handle; 336 337 if (params.enable_ipc_proxy) { 338 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); 339 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); 340 manifest_service_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); 341 342 // Create the PPAPI IPC channels between the NaCl IRT and the host 343 // (browser/renderer) processes. The IRT uses these channels to 344 // communicate with the host and to initialize the IPC dispatchers. 345 SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(), 346 nap, NACL_CHROME_DESC_BASE); 347 SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(), 348 nap, NACL_CHROME_DESC_BASE + 1); 349 350 scoped_refptr<NaClIPCAdapter> manifest_ipc_adapter = 351 SetUpIPCAdapter(&manifest_service_handle, 352 io_thread_.message_loop_proxy(), 353 nap, 354 NACL_CHROME_DESC_BASE + 2); 355 manifest_ipc_adapter->set_resolve_file_token_callback( 356 base::Bind(&NaClListener::ResolveFileToken, base::Unretained(this))); 357 } 358 359 trusted_listener_ = new NaClTrustedListener( 360 IPC::Channel::GenerateVerifiedChannelID("nacl"), 361 io_thread_.message_loop_proxy().get(), 362 &shutdown_event_); 363 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated( 364 browser_handle, 365 ppapi_renderer_handle, 366 trusted_listener_->TakeClientChannelHandle(), 367 manifest_service_handle))) 368 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost."; 369 370 std::vector<nacl::FileDescriptor> handles = params.handles; 371 struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate(); 372 if (args == NULL) { 373 LOG(ERROR) << "NaClChromeMainArgsCreate() failed"; 374 return; 375 } 376 377 #if defined(OS_LINUX) || defined(OS_MACOSX) 378 args->number_of_cores = number_of_cores_; 379 args->create_memory_object_func = CreateMemoryObject; 380 # if defined(OS_MACOSX) 381 CHECK(handles.size() >= 1); 382 g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]); 383 handles.pop_back(); 384 # endif 385 #endif 386 387 if (params.uses_irt) { 388 CHECK(handles.size() >= 1); 389 NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]); 390 handles.pop_back(); 391 392 #if defined(OS_WIN) 393 args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle), 394 _O_RDONLY | _O_BINARY); 395 if (args->irt_fd < 0) { 396 LOG(ERROR) << "_open_osfhandle() failed"; 397 return; 398 } 399 #else 400 args->irt_fd = irt_handle; 401 #endif 402 } else { 403 // Otherwise, the IRT handle is not even sent. 404 args->irt_fd = -1; 405 } 406 407 if (params.validation_cache_enabled) { 408 // SHA256 block size. 409 CHECK_EQ(params.validation_cache_key.length(), (size_t) 64); 410 // The cache structure is not freed and exists until the NaCl process exits. 411 args->validation_cache = CreateValidationCache( 412 new BrowserValidationDBProxy(this), params.validation_cache_key, 413 params.version); 414 } 415 416 CHECK(handles.size() == 1); 417 args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]); 418 args->enable_exception_handling = params.enable_exception_handling; 419 args->enable_debug_stub = params.enable_debug_stub; 420 args->enable_dyncode_syscalls = params.enable_dyncode_syscalls; 421 if (!params.enable_dyncode_syscalls) { 422 // Bound the initial nexe's code segment size under PNaCl to 423 // reduce the chance of a code spraying attack succeeding (see 424 // https://code.google.com/p/nativeclient/issues/detail?id=3572). 425 // We assume that !params.enable_dyncode_syscalls is synonymous 426 // with PNaCl. We can't apply this arbitrary limit outside of 427 // PNaCl because it might break existing NaCl apps, and this limit 428 // is only useful if the dyncode syscalls are disabled. 429 args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB 430 431 // Indicate that this is a PNaCl module. 432 // TODO(jvoung): Plumb through something indicating that this is PNaCl 433 // instead of relying on enable_dyncode_syscalls. 434 args->pnacl_mode = 1; 435 } 436 #if defined(OS_LINUX) || defined(OS_MACOSX) 437 args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle( 438 params.debug_stub_server_bound_socket); 439 #endif 440 #if defined(OS_WIN) 441 args->broker_duplicate_handle_func = BrokerDuplicateHandle; 442 args->attach_debug_exception_handler_func = AttachDebugExceptionHandler; 443 args->debug_stub_server_port_selected_handler_func = 444 DebugStubPortSelectedHandler; 445 #endif 446 #if defined(OS_LINUX) 447 args->prereserved_sandbox_size = prereserved_sandbox_size_; 448 #endif 449 450 NaClFileInfo nexe_file_info; 451 base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile( 452 params.nexe_file); 453 #if defined(OS_WIN) 454 nexe_file_info.desc = 455 _open_osfhandle(reinterpret_cast<intptr_t>(nexe_file), 456 _O_RDONLY | _O_BINARY); 457 #elif defined(OS_POSIX) 458 nexe_file_info.desc = nexe_file; 459 #else 460 #error Unsupported target platform. 461 #endif 462 nexe_file_info.file_token.lo = params.nexe_token_lo; 463 nexe_file_info.file_token.hi = params.nexe_token_hi; 464 args->nexe_desc = NaClDescIoFromFileInfo(nexe_file_info, NACL_ABI_O_RDONLY); 465 466 int exit_status; 467 if (!NaClChromeMainStart(nap, args, &exit_status)) 468 NaClExit(1); 469 470 // Report the plugin's exit status if the application started successfully. 471 trusted_listener_->Send(new NaClRendererMsg_ReportExitStatus(exit_status)); 472 NaClExit(exit_status); 473 } 474 475 void NaClListener::ResolveFileToken( 476 uint64_t token_lo, 477 uint64_t token_hi, 478 base::Callback<void(IPC::PlatformFileForTransit, base::FilePath)> cb) { 479 if (!Send(new NaClProcessMsg_ResolveFileTokenAsync(token_lo, token_hi))) { 480 cb.Run(IPC::PlatformFileForTransit(), base::FilePath()); 481 return; 482 } 483 resolved_cb_ = cb; 484 } 485 486 void NaClListener::OnFileTokenResolved( 487 uint64_t token_lo, 488 uint64_t token_hi, 489 IPC::PlatformFileForTransit ipc_fd, 490 base::FilePath file_path) { 491 resolved_cb_.Run(ipc_fd, file_path); 492 resolved_cb_.Reset(); 493 } 494