1 /* 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" 8 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" 10 11 #include <string.h> 12 #include <string> 13 #include <utility> 14 15 #include "base/compiler_specific.h" 16 17 #include "native_client/src/include/portability_io.h" 18 #include "native_client/src/include/portability_string.h" 19 #include "native_client/src/include/nacl_macros.h" 20 #include "native_client/src/include/nacl_scoped_ptr.h" 21 #include "native_client/src/shared/platform/nacl_check.h" 22 #include "native_client/src/shared/platform/nacl_log.h" 23 #include "native_client/src/shared/platform/nacl_sync.h" 24 #include "native_client/src/shared/platform/nacl_sync_checked.h" 25 #include "native_client/src/shared/platform/nacl_sync_raii.h" 26 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" 27 28 #include "native_client/src/public/imc_types.h" 29 #include "native_client/src/public/nacl_file_info.h" 30 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" 31 32 #include "ppapi/c/pp_errors.h" 33 #include "ppapi/cpp/core.h" 34 #include "ppapi/cpp/completion_callback.h" 35 36 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 37 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 38 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" 39 #include "ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" 40 #include "ppapi/native_client/src/trusted/plugin/srpc_client.h" 41 #include "ppapi/native_client/src/trusted/plugin/utility.h" 42 #include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h" 43 44 namespace plugin { 45 46 OpenManifestEntryResource::~OpenManifestEntryResource() { 47 } 48 49 PluginReverseInterface::PluginReverseInterface( 50 nacl::WeakRefAnchor* anchor, 51 PP_Instance pp_instance, 52 ServiceRuntime* service_runtime, 53 pp::CompletionCallback init_done_cb) 54 : anchor_(anchor), 55 pp_instance_(pp_instance), 56 service_runtime_(service_runtime), 57 shutting_down_(false), 58 init_done_cb_(init_done_cb) { 59 NaClXMutexCtor(&mu_); 60 NaClXCondVarCtor(&cv_); 61 } 62 63 PluginReverseInterface::~PluginReverseInterface() { 64 NaClCondVarDtor(&cv_); 65 NaClMutexDtor(&mu_); 66 } 67 68 void PluginReverseInterface::ShutDown() { 69 NaClLog(4, "PluginReverseInterface::Shutdown: entered\n"); 70 nacl::MutexLocker take(&mu_); 71 shutting_down_ = true; 72 NaClXCondVarBroadcast(&cv_); 73 NaClLog(4, "PluginReverseInterface::Shutdown: broadcasted, exiting\n"); 74 } 75 76 void PluginReverseInterface::DoPostMessage(std::string message) { 77 // This feature is no longer used. 78 // TODO(teravest): Remove this once this is gone from nacl::ReverseInterface. 79 } 80 81 void PluginReverseInterface::StartupInitializationComplete() { 82 NaClLog(4, "PluginReverseInterface::StartupInitializationComplete\n"); 83 if (init_done_cb_.pp_completion_callback().func != NULL) { 84 NaClLog(4, 85 "PluginReverseInterface::StartupInitializationComplete:" 86 " invoking CB\n"); 87 pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_, PP_OK); 88 } else { 89 NaClLog(1, 90 "PluginReverseInterface::StartupInitializationComplete:" 91 " init_done_cb_ not valid, skipping.\n"); 92 } 93 } 94 95 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey 96 // and invoke StreamAsFile with a completion callback that invokes 97 // GetPOSIXFileDesc. 98 bool PluginReverseInterface::OpenManifestEntry(std::string url_key, 99 struct NaClFileInfo* info) { 100 // This method should only ever be called from the PNaCl translator, as the 101 // IRT is not available there. 102 // TODO(teravest): Remove support for OpenManifestEntry here once 103 // crbug.com/302078 is resolved. 104 if (service_runtime_->main_service_runtime()) { 105 NaClLog(LOG_ERROR, 106 "OpenManifestEntry should only be used by PNaCl translator.\n"); 107 return false; 108 } 109 110 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! 111 // The to_open object is owned by the weak ref callback. Because this function 112 // waits for the callback to finish, the to_open object will be deallocated on 113 // the main thread before this function can return. The pointers it contains 114 // to stack variables will not leak. 115 OpenManifestEntryResource* to_open = 116 new OpenManifestEntryResource(url_key, info, &op_complete); 117 CHECK(to_open != NULL); 118 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", 119 url_key.c_str()); 120 // This assumes we are not on the main thread. If false, we deadlock. 121 plugin::WeakRefCallOnMainThread( 122 anchor_, 123 0, 124 this, 125 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, 126 to_open); 127 NaClLog(4, 128 "PluginReverseInterface::OpenManifestEntry:" 129 " waiting on main thread\n"); 130 131 { 132 nacl::MutexLocker take(&mu_); 133 while (!shutting_down_ && !op_complete) 134 NaClXCondVarWait(&cv_, &mu_); 135 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: done!\n"); 136 if (shutting_down_) { 137 NaClLog(4, 138 "PluginReverseInterface::OpenManifestEntry:" 139 " plugin is shutting down\n"); 140 return false; 141 } 142 } 143 144 // info->desc has the returned descriptor if successful, else -1. 145 146 // The caller is responsible for not closing info->desc. If it is 147 // closed prematurely, then another open could re-use the OS 148 // descriptor, confusing the opened_ map. If the caller is going to 149 // want to make a NaClDesc object and transfer it etc., then the 150 // caller should DUP the descriptor (but remember the original 151 // value) for use by the NaClDesc object, which closes when the 152 // object is destroyed. 153 NaClLog(4, 154 "PluginReverseInterface::OpenManifestEntry: info->desc = %d\n", 155 info->desc); 156 if (info->desc == -1) { 157 // TODO(bsy,ncbray): what else should we do with the error? This 158 // is a runtime error that may simply be a programming error in 159 // the untrusted code, or it may be something else wrong w/ the 160 // manifest. 161 NaClLog(4, "OpenManifestEntry: failed for key %s", url_key.c_str()); 162 } 163 return true; 164 } 165 166 // Transfer point from OpenManifestEntry() which runs on the main thread 167 // (Some PPAPI actions -- like StreamAsFile -- can only run on the main thread). 168 // OpenManifestEntry() is waiting on a condvar for this continuation to 169 // complete. We Broadcast and awaken OpenManifestEntry() whenever we are done 170 // either here, or in a later MainThreadContinuation step, if there are 171 // multiple steps. 172 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation( 173 OpenManifestEntryResource* p, 174 int32_t err) { 175 UNREFERENCED_PARAMETER(err); 176 // CallOnMainThread continuations always called with err == PP_OK. 177 178 NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n"); 179 180 // Because p is owned by the callback of this invocation, so it is necessary 181 // to create another instance. 182 OpenManifestEntryResource* open_cont = new OpenManifestEntryResource(*p); 183 pp::CompletionCallback stream_cc = WeakRefNewCallback( 184 anchor_, 185 this, 186 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, 187 open_cont); 188 189 GetNaClInterface()->OpenManifestEntry( 190 pp_instance_, 191 PP_FromBool(!service_runtime_->main_service_runtime()), 192 p->url.c_str(), 193 &open_cont->pp_file_info, 194 stream_cc.pp_completion_callback()); 195 // p is deleted automatically. 196 } 197 198 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( 199 OpenManifestEntryResource* p, 200 int32_t result) { 201 NaClLog(4, "Entered StreamAsFile_MainThreadContinuation\n"); 202 { 203 nacl::MutexLocker take(&mu_); 204 if (result == PP_OK) { 205 // We downloaded this file to temporary storage for this plugin; it's 206 // reasonable to provide a file descriptor with write access. 207 p->file_info->desc = ConvertFileDescriptor(p->pp_file_info.handle, false); 208 p->file_info->file_token.lo = p->pp_file_info.token_lo; 209 p->file_info->file_token.hi = p->pp_file_info.token_hi; 210 NaClLog(4, 211 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", 212 p->file_info->desc); 213 } else { 214 NaClLog( 215 4, 216 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); 217 p->file_info->desc = -1; 218 } 219 *p->op_complete_ptr = true; 220 NaClXCondVarBroadcast(&cv_); 221 } 222 } 223 224 void PluginReverseInterface::ReportCrash() { 225 // This is now handled through Chromium IPC. 226 } 227 228 void PluginReverseInterface::ReportExitStatus(int exit_status) { 229 // We do nothing here; reporting exit status is handled through a separate 230 // embedder interface. 231 } 232 233 int64_t PluginReverseInterface::RequestQuotaForWrite( 234 std::string file_id, int64_t offset, int64_t bytes_to_write) { 235 return bytes_to_write; 236 } 237 238 ServiceRuntime::ServiceRuntime(Plugin* plugin, 239 PP_Instance pp_instance, 240 bool main_service_runtime, 241 bool uses_nonsfi_mode, 242 pp::CompletionCallback init_done_cb) 243 : plugin_(plugin), 244 pp_instance_(pp_instance), 245 main_service_runtime_(main_service_runtime), 246 uses_nonsfi_mode_(uses_nonsfi_mode), 247 reverse_service_(NULL), 248 anchor_(new nacl::WeakRefAnchor()), 249 rev_interface_(new PluginReverseInterface(anchor_, pp_instance, this, 250 init_done_cb)), 251 start_sel_ldr_done_(false), 252 start_nexe_done_(false), 253 nexe_started_ok_(false), 254 bootstrap_channel_(NACL_INVALID_HANDLE) { 255 NaClSrpcChannelInitialize(&command_channel_); 256 NaClXMutexCtor(&mu_); 257 NaClXCondVarCtor(&cond_); 258 } 259 260 bool ServiceRuntime::SetupCommandChannel() { 261 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", 262 static_cast<void*>(this), 263 static_cast<void*>(subprocess_.get())); 264 // Set up the bootstrap channel in our subprocess so that we can establish 265 // SRPC. 266 subprocess_->set_channel(bootstrap_channel_); 267 268 if (uses_nonsfi_mode_) { 269 // In non-SFI mode, no SRPC is used. Just skips and returns success. 270 return true; 271 } 272 273 if (!subprocess_->SetupCommand(&command_channel_)) { 274 ErrorInfo error_info; 275 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, 276 "ServiceRuntime: command channel creation failed"); 277 ReportLoadError(error_info); 278 return false; 279 } 280 return true; 281 } 282 283 bool ServiceRuntime::InitReverseService() { 284 if (uses_nonsfi_mode_) { 285 // In non-SFI mode, no reverse service is set up. Just returns success. 286 return true; 287 } 288 289 // Hook up the reverse service channel. We are the IMC client, but 290 // provide SRPC service. 291 NaClDesc* out_conn_cap; 292 NaClSrpcResultCodes rpc_result = 293 NaClSrpcInvokeBySignature(&command_channel_, 294 "reverse_setup::h", 295 &out_conn_cap); 296 297 if (NACL_SRPC_RESULT_OK != rpc_result) { 298 ErrorInfo error_info; 299 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_REV_SETUP, 300 "ServiceRuntime: reverse setup rpc failed"); 301 ReportLoadError(error_info); 302 return false; 303 } 304 // Get connection capability to service runtime where the IMC 305 // server/SRPC client is waiting for a rendezvous. 306 NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n", 307 (uintptr_t) out_conn_cap); 308 nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup( 309 out_conn_cap); 310 if (conn_cap == NULL) { 311 ErrorInfo error_info; 312 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_WRAPPER, 313 "ServiceRuntime: wrapper allocation failure"); 314 ReportLoadError(error_info); 315 return false; 316 } 317 out_conn_cap = NULL; // ownership passed 318 NaClLog(4, "ServiceRuntime::InitReverseService: starting reverse service\n"); 319 reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref()); 320 if (!reverse_service_->Start()) { 321 ErrorInfo error_info; 322 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE, 323 "ServiceRuntime: starting reverse services failed"); 324 ReportLoadError(error_info); 325 return false; 326 } 327 return true; 328 } 329 330 bool ServiceRuntime::StartModule() { 331 // start the module. otherwise we cannot connect for multimedia 332 // subsystem since that is handled by user-level code (not secure!) 333 // in libsrpc. 334 int load_status = -1; 335 if (uses_nonsfi_mode_) { 336 // In non-SFI mode, we don't need to call start_module SRPC to launch 337 // the plugin. 338 load_status = LOAD_OK; 339 } else { 340 NaClSrpcResultCodes rpc_result = 341 NaClSrpcInvokeBySignature(&command_channel_, 342 "start_module::i", 343 &load_status); 344 345 if (NACL_SRPC_RESULT_OK != rpc_result) { 346 ErrorInfo error_info; 347 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_START_MODULE, 348 "ServiceRuntime: could not start nacl module"); 349 ReportLoadError(error_info); 350 return false; 351 } 352 } 353 354 NaClLog(4, "ServiceRuntime::StartModule (load_status=%d)\n", load_status); 355 if (main_service_runtime_) { 356 if (load_status < 0 || load_status > NACL_ERROR_CODE_MAX) 357 load_status = LOAD_STATUS_UNKNOWN; 358 GetNaClInterface()->ReportSelLdrStatus(pp_instance_, 359 load_status, 360 NACL_ERROR_CODE_MAX); 361 } 362 363 if (LOAD_OK != load_status) { 364 ErrorInfo error_info; 365 error_info.SetReport( 366 PP_NACL_ERROR_SEL_LDR_START_STATUS, 367 NaClErrorString(static_cast<NaClErrorCode>(load_status))); 368 ReportLoadError(error_info); 369 return false; 370 } 371 return true; 372 } 373 374 void ServiceRuntime::StartSelLdr(const SelLdrStartParams& params, 375 pp::CompletionCallback callback) { 376 NaClLog(4, "ServiceRuntime::Start\n"); 377 378 nacl::scoped_ptr<SelLdrLauncherChrome> 379 tmp_subprocess(new SelLdrLauncherChrome()); 380 if (NULL == tmp_subprocess.get()) { 381 NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n"); 382 ErrorInfo error_info; 383 error_info.SetReport( 384 PP_NACL_ERROR_SEL_LDR_CREATE_LAUNCHER, 385 "ServiceRuntime: failed to create sel_ldr launcher"); 386 ReportLoadError(error_info); 387 pp::Module::Get()->core()->CallOnMainThread(0, callback, PP_ERROR_FAILED); 388 return; 389 } 390 391 bool enable_dev_interfaces = 392 GetNaClInterface()->DevInterfacesEnabled(pp_instance_); 393 394 GetNaClInterface()->LaunchSelLdr( 395 pp_instance_, 396 PP_FromBool(main_service_runtime_), 397 params.url.c_str(), 398 ¶ms.file_info, 399 PP_FromBool(params.uses_irt), 400 PP_FromBool(params.uses_ppapi), 401 PP_FromBool(uses_nonsfi_mode_), 402 PP_FromBool(enable_dev_interfaces), 403 PP_FromBool(params.enable_dyncode_syscalls), 404 PP_FromBool(params.enable_exception_handling), 405 PP_FromBool(params.enable_crash_throttling), 406 &bootstrap_channel_, 407 callback.pp_completion_callback()); 408 subprocess_.reset(tmp_subprocess.release()); 409 } 410 411 bool ServiceRuntime::WaitForSelLdrStart() { 412 // Time to wait on condvar (for browser to create a new sel_ldr process on 413 // our behalf). Use 6 seconds to be *fairly* conservative. 414 // 415 // On surfaway, the CallOnMainThread above may never get scheduled 416 // to unblock this condvar, or the IPC reply from the browser to renderer 417 // might get canceled/dropped. However, it is currently important to 418 // avoid waiting indefinitely because ~PnaclCoordinator will attempt to 419 // join() the PnaclTranslateThread, and the PnaclTranslateThread is waiting 420 // for the signal before exiting. 421 static int64_t const kWaitTimeMicrosecs = 6 * NACL_MICROS_PER_UNIT; 422 int64_t left_to_wait = kWaitTimeMicrosecs; 423 int64_t deadline = NaClGetTimeOfDayMicroseconds() + left_to_wait; 424 nacl::MutexLocker take(&mu_); 425 while(!start_sel_ldr_done_ && left_to_wait > 0) { 426 struct nacl_abi_timespec left_timespec; 427 left_timespec.tv_sec = left_to_wait / NACL_MICROS_PER_UNIT; 428 left_timespec.tv_nsec = 429 (left_to_wait % NACL_MICROS_PER_UNIT) * NACL_NANOS_PER_MICRO; 430 NaClXCondVarTimedWaitRelative(&cond_, &mu_, &left_timespec); 431 int64_t now = NaClGetTimeOfDayMicroseconds(); 432 left_to_wait = deadline - now; 433 } 434 return start_sel_ldr_done_; 435 } 436 437 void ServiceRuntime::SignalStartSelLdrDone() { 438 nacl::MutexLocker take(&mu_); 439 start_sel_ldr_done_ = true; 440 NaClXCondVarSignal(&cond_); 441 } 442 443 bool ServiceRuntime::WaitForNexeStart() { 444 nacl::MutexLocker take(&mu_); 445 while (!start_nexe_done_) 446 NaClXCondVarWait(&cond_, &mu_); 447 return nexe_started_ok_; 448 } 449 450 void ServiceRuntime::SignalNexeStarted(bool ok) { 451 nacl::MutexLocker take(&mu_); 452 start_nexe_done_ = true; 453 nexe_started_ok_ = ok; 454 NaClXCondVarSignal(&cond_); 455 } 456 457 void ServiceRuntime::StartNexe() { 458 bool ok = StartNexeInternal(); 459 if (ok) { 460 NaClLog(4, "ServiceRuntime::StartNexe (success)\n"); 461 } else { 462 ReapLogs(); 463 } 464 // This only matters if a background thread is waiting, but we signal in all 465 // cases to simplify the code. 466 SignalNexeStarted(ok); 467 } 468 469 bool ServiceRuntime::StartNexeInternal() { 470 if (!SetupCommandChannel()) 471 return false; 472 if (!InitReverseService()) 473 return false; 474 return StartModule(); 475 } 476 477 void ServiceRuntime::ReapLogs() { 478 // TODO(teravest): We should allow the NaCl process to crash itself when a 479 // module fails to start, and remove the call to RemoteLog() here. The 480 // reverse channel is no longer needed for crash reporting. 481 // 482 // The reasoning behind the current code behavior follows: 483 // On a load failure the NaCl process does not crash itself to 484 // avoid a race where the no-more-senders error on the reverse 485 // channel service thread might cause the crash-detection logic to 486 // kick in before the start_module RPC reply has been received. So 487 // we induce a NaCl process crash here. 488 RemoteLog(LOG_FATAL, "reap logs\n"); 489 490 // TODO(teravest): Release subprocess_ here since it's no longer needed. It 491 // was previously kept around to collect crash log output from the bootstrap 492 // channel. 493 } 494 495 void ServiceRuntime::ReportLoadError(const ErrorInfo& error_info) { 496 if (main_service_runtime_) { 497 plugin_->ReportLoadError(error_info); 498 } 499 } 500 501 SrpcClient* ServiceRuntime::SetupAppChannel() { 502 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", 503 reinterpret_cast<void*>(subprocess_.get())); 504 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); 505 if (NULL == connect_desc) { 506 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); 507 return NULL; 508 } else { 509 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", 510 static_cast<void*>(connect_desc)); 511 SrpcClient* srpc_client = SrpcClient::New(connect_desc); 512 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", 513 static_cast<void*>(srpc_client)); 514 delete connect_desc; 515 return srpc_client; 516 } 517 } 518 519 bool ServiceRuntime::RemoteLog(int severity, const std::string& msg) { 520 NaClSrpcResultCodes rpc_result = 521 NaClSrpcInvokeBySignature(&command_channel_, 522 "log:is:", 523 severity, 524 strdup(msg.c_str())); 525 return (NACL_SRPC_RESULT_OK == rpc_result); 526 } 527 528 void ServiceRuntime::Shutdown() { 529 rev_interface_->ShutDown(); 530 anchor_->Abandon(); 531 // Abandon callbacks, tell service threads to quit if they were 532 // blocked waiting for main thread operations to finish. Note that 533 // some callbacks must still await their completion event, e.g., 534 // CallOnMainThread must still wait for the time out, or I/O events 535 // must finish, so resources associated with pending events cannot 536 // be deallocated. 537 538 // Note that this does waitpid() to get rid of any zombie subprocess. 539 subprocess_.reset(NULL); 540 541 NaClSrpcDtor(&command_channel_); 542 543 // subprocess_ has been shut down, but threads waiting on messages 544 // from the service runtime may not have noticed yet. The low-level 545 // NaClSimpleRevService code takes care to refcount the data objects 546 // that it needs, and reverse_service_ is also refcounted. We wait 547 // for the service threads to get their EOF indications. 548 if (reverse_service_ != NULL) { 549 reverse_service_->WaitForServiceThreadsToExit(); 550 reverse_service_->Unref(); 551 reverse_service_ = NULL; 552 } 553 } 554 555 ServiceRuntime::~ServiceRuntime() { 556 NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n", 557 static_cast<void*>(this)); 558 // We do this just in case Shutdown() was not called. 559 subprocess_.reset(NULL); 560 if (reverse_service_ != NULL) 561 reverse_service_->Unref(); 562 563 rev_interface_->Unref(); 564 565 anchor_->Unref(); 566 NaClCondVarDtor(&cond_); 567 NaClMutexDtor(&mu_); 568 } 569 570 } // namespace plugin 571