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 <set> 13 #include <string> 14 #include <utility> 15 16 #include "base/compiler_specific.h" 17 18 #include "native_client/src/include/checked_cast.h" 19 #include "native_client/src/include/portability_io.h" 20 #include "native_client/src/include/portability_string.h" 21 #include "native_client/src/include/nacl_macros.h" 22 #include "native_client/src/include/nacl_scoped_ptr.h" 23 #include "native_client/src/include/nacl_string.h" 24 #include "native_client/src/shared/platform/nacl_check.h" 25 #include "native_client/src/shared/platform/nacl_log.h" 26 #include "native_client/src/shared/platform/nacl_sync.h" 27 #include "native_client/src/shared/platform/nacl_sync_checked.h" 28 #include "native_client/src/shared/platform/nacl_sync_raii.h" 29 #include "native_client/src/shared/platform/scoped_ptr_refcount.h" 30 #include "native_client/src/trusted/desc/nacl_desc_imc.h" 31 // remove when we no longer need to cast the DescWrapper below. 32 #include "native_client/src/trusted/desc/nacl_desc_io.h" 33 #include "native_client/src/trusted/desc/nrd_xfer.h" 34 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" 35 36 // This is here due to a Windows API collision; plugin.h through 37 // file_downloader.h transitively includes Instance.h which defines a 38 // PostMessage method, so this undef must appear before any of those. 39 #ifdef PostMessage 40 #undef PostMessage 41 #endif 42 #include "native_client/src/public/imc_types.h" 43 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" 44 #include "native_client/src/trusted/validator/nacl_file_info.h" 45 46 #include "ppapi/c/pp_errors.h" 47 #include "ppapi/c/trusted/ppb_file_io_trusted.h" 48 #include "ppapi/cpp/core.h" 49 #include "ppapi/cpp/completion_callback.h" 50 #include "ppapi/cpp/file_io.h" 51 52 #include "ppapi/native_client/src/trusted/plugin/manifest.h" 53 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 54 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 55 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h" 56 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" 57 #include "ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" 58 #include "ppapi/native_client/src/trusted/plugin/srpc_client.h" 59 #include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h" 60 61 namespace { 62 63 // For doing crude quota enforcement on writes to temp files. 64 // We do not allow a temp file bigger than 512 MB for now. 65 const uint64_t kMaxTempQuota = 0x20000000; 66 67 } // namespace 68 69 namespace plugin { 70 71 PluginReverseInterface::PluginReverseInterface( 72 nacl::WeakRefAnchor* anchor, 73 Plugin* plugin, 74 const Manifest* manifest, 75 ServiceRuntime* service_runtime, 76 pp::CompletionCallback init_done_cb, 77 pp::CompletionCallback crash_cb) 78 : anchor_(anchor), 79 plugin_(plugin), 80 manifest_(manifest), 81 service_runtime_(service_runtime), 82 shutting_down_(false), 83 init_done_cb_(init_done_cb), 84 crash_cb_(crash_cb) { 85 NaClXMutexCtor(&mu_); 86 NaClXCondVarCtor(&cv_); 87 } 88 89 PluginReverseInterface::~PluginReverseInterface() { 90 NaClCondVarDtor(&cv_); 91 NaClMutexDtor(&mu_); 92 } 93 94 void PluginReverseInterface::ShutDown() { 95 NaClLog(4, "PluginReverseInterface::Shutdown: entered\n"); 96 nacl::MutexLocker take(&mu_); 97 shutting_down_ = true; 98 NaClXCondVarBroadcast(&cv_); 99 NaClLog(4, "PluginReverseInterface::Shutdown: broadcasted, exiting\n"); 100 } 101 102 void PluginReverseInterface::Log(nacl::string message) { 103 LogToJavaScriptConsoleResource* continuation = 104 new LogToJavaScriptConsoleResource(message); 105 CHECK(continuation != NULL); 106 NaClLog(4, "PluginReverseInterface::Log(%s)\n", message.c_str()); 107 plugin::WeakRefCallOnMainThread( 108 anchor_, 109 0, /* delay in ms */ 110 this, 111 &plugin::PluginReverseInterface::Log_MainThreadContinuation, 112 continuation); 113 } 114 115 void PluginReverseInterface::DoPostMessage(nacl::string message) { 116 PostMessageResource* continuation = new PostMessageResource(message); 117 CHECK(continuation != NULL); 118 NaClLog(4, "PluginReverseInterface::DoPostMessage(%s)\n", message.c_str()); 119 plugin::WeakRefCallOnMainThread( 120 anchor_, 121 0, /* delay in ms */ 122 this, 123 &plugin::PluginReverseInterface::PostMessage_MainThreadContinuation, 124 continuation); 125 } 126 127 void PluginReverseInterface::StartupInitializationComplete() { 128 NaClLog(4, "PluginReverseInterface::StartupInitializationComplete\n"); 129 if (init_done_cb_.pp_completion_callback().func != NULL) { 130 NaClLog(4, 131 "PluginReverseInterface::StartupInitializationComplete:" 132 " invoking CB\n"); 133 pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_, PP_OK); 134 } else { 135 NaClLog(1, 136 "PluginReverseInterface::StartupInitializationComplete:" 137 " init_done_cb_ not valid, skipping.\n"); 138 } 139 } 140 141 void PluginReverseInterface::Log_MainThreadContinuation( 142 LogToJavaScriptConsoleResource* p, 143 int32_t err) { 144 UNREFERENCED_PARAMETER(err); 145 NaClLog(4, 146 "PluginReverseInterface::Log_MainThreadContinuation(%s)\n", 147 p->message.c_str()); 148 plugin_->AddToConsole(p->message); 149 } 150 void PluginReverseInterface::PostMessage_MainThreadContinuation( 151 PostMessageResource* p, 152 int32_t err) { 153 UNREFERENCED_PARAMETER(err); 154 NaClLog(4, 155 "PluginReverseInterface::PostMessage_MainThreadContinuation(%s)\n", 156 p->message.c_str()); 157 plugin_->PostMessage(std::string("DEBUG_POSTMESSAGE:") + p->message); 158 } 159 160 bool PluginReverseInterface::EnumerateManifestKeys( 161 std::set<nacl::string>* out_keys) { 162 Manifest const* mp = manifest_; 163 164 if (!mp->GetFileKeys(out_keys)) { 165 return false; 166 } 167 168 return true; 169 } 170 171 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey 172 // and invoke StreamAsFile with a completion callback that invokes 173 // GetPOSIXFileDesc. 174 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, 175 struct NaClFileInfo* info) { 176 ErrorInfo error_info; 177 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! 178 // The to_open object is owned by the weak ref callback. Because this function 179 // waits for the callback to finish, the to_open object will be deallocated on 180 // the main thread before this function can return. The pointers it contains 181 // to stack variables will not leak. 182 OpenManifestEntryResource* to_open = 183 new OpenManifestEntryResource(url_key, info, 184 &error_info, &op_complete); 185 CHECK(to_open != NULL); 186 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", 187 url_key.c_str()); 188 // This assumes we are not on the main thread. If false, we deadlock. 189 plugin::WeakRefCallOnMainThread( 190 anchor_, 191 0, 192 this, 193 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, 194 to_open); 195 NaClLog(4, 196 "PluginReverseInterface::OpenManifestEntry:" 197 " waiting on main thread\n"); 198 bool shutting_down; 199 do { 200 nacl::MutexLocker take(&mu_); 201 for (;;) { 202 NaClLog(4, 203 "PluginReverseInterface::OpenManifestEntry:" 204 " got lock, checking shutdown and completion: (%s, %s)\n", 205 shutting_down_ ? "yes" : "no", 206 op_complete ? "yes" : "no"); 207 shutting_down = shutting_down_; 208 if (op_complete || shutting_down) { 209 NaClLog(4, 210 "PluginReverseInterface::OpenManifestEntry:" 211 " done!\n"); 212 break; 213 } 214 NaClXCondVarWait(&cv_, &mu_); 215 } 216 } while (0); 217 if (shutting_down) { 218 NaClLog(4, 219 "PluginReverseInterface::OpenManifestEntry:" 220 " plugin is shutting down\n"); 221 return false; 222 } 223 // out_desc has the returned descriptor if successful, else -1. 224 225 // The caller is responsible for not closing *out_desc. If it is 226 // closed prematurely, then another open could re-use the OS 227 // descriptor, confusing the opened_ map. If the caller is going to 228 // want to make a NaClDesc object and transfer it etc., then the 229 // caller should DUP the descriptor (but remember the original 230 // value) for use by the NaClDesc object, which closes when the 231 // object is destroyed. 232 NaClLog(4, 233 "PluginReverseInterface::OpenManifestEntry:" 234 " *out_desc = %d\n", 235 info->desc); 236 if (info->desc == -1) { 237 // TODO(bsy,ncbray): what else should we do with the error? This 238 // is a runtime error that may simply be a programming error in 239 // the untrusted code, or it may be something else wrong w/ the 240 // manifest. 241 NaClLog(4, 242 "OpenManifestEntry: failed for key %s, code %d (%s)\n", 243 url_key.c_str(), 244 error_info.error_code(), 245 error_info.message().c_str()); 246 } 247 return true; 248 } 249 250 // Transfer point from OpenManifestEntry() which runs on the main thread 251 // (Some PPAPI actions -- like StreamAsFile -- can only run on the main thread). 252 // OpenManifestEntry() is waiting on a condvar for this continuation to 253 // complete. We Broadcast and awaken OpenManifestEntry() whenever we are done 254 // either here, or in a later MainThreadContinuation step, if there are 255 // multiple steps. 256 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation( 257 OpenManifestEntryResource* p, 258 int32_t err) { 259 OpenManifestEntryResource *open_cont; 260 UNREFERENCED_PARAMETER(err); 261 // CallOnMainThread continuations always called with err == PP_OK. 262 263 NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n"); 264 265 std::string mapped_url; 266 PnaclOptions pnacl_options; 267 if (!manifest_->ResolveKey(p->url, &mapped_url, 268 &pnacl_options, p->error_info)) { 269 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); 270 // Failed, and error_info has the details on what happened. Wake 271 // up requesting thread -- we are done. 272 nacl::MutexLocker take(&mu_); 273 *p->op_complete_ptr = true; // done... 274 p->file_info->desc = -1; // but failed. 275 NaClXCondVarBroadcast(&cv_); 276 return; 277 } 278 NaClLog(4, 279 "OpenManifestEntry_MainThreadContinuation: " 280 "ResolveKey: %s -> %s (pnacl_translate(%d))\n", 281 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate()); 282 283 open_cont = new OpenManifestEntryResource(*p); // copy ctor! 284 CHECK(open_cont != NULL); 285 open_cont->url = mapped_url; 286 if (!pnacl_options.translate()) { 287 pp::CompletionCallback stream_cc = WeakRefNewCallback( 288 anchor_, 289 this, 290 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, 291 open_cont); 292 // Normal files. 293 if (!PnaclUrls::IsPnaclComponent(mapped_url)) { 294 if (!plugin_->StreamAsFile(mapped_url, 295 stream_cc.pp_completion_callback())) { 296 NaClLog(4, 297 "OpenManifestEntry_MainThreadContinuation: " 298 "StreamAsFile failed\n"); 299 nacl::MutexLocker take(&mu_); 300 *p->op_complete_ptr = true; // done... 301 p->file_info->desc = -1; // but failed. 302 p->error_info->SetReport(ERROR_MANIFEST_OPEN, 303 "ServiceRuntime: StreamAsFile failed"); 304 NaClXCondVarBroadcast(&cv_); 305 return; 306 } 307 NaClLog(4, 308 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); 309 } else { 310 // Special PNaCl support files, that are installed on the 311 // user machine. 312 int32_t fd = PnaclResources::GetPnaclFD( 313 plugin_, 314 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str()); 315 if (fd < 0) { 316 // We should check earlier if the pnacl component wasn't installed 317 // yet. At this point, we can't do much anymore, so just continue 318 // with an invalid fd. 319 NaClLog(4, 320 "OpenManifestEntry_MainThreadContinuation: " 321 "GetReadonlyPnaclFd failed\n"); 322 // TODO(jvoung): Separate the error codes? 323 p->error_info->SetReport(ERROR_MANIFEST_OPEN, 324 "ServiceRuntime: GetPnaclFd failed"); 325 } 326 nacl::MutexLocker take(&mu_); 327 *p->op_complete_ptr = true; // done! 328 // TODO(ncbray): enable the fast loading and validation paths for this 329 // type of file. 330 p->file_info->desc = fd; 331 NaClXCondVarBroadcast(&cv_); 332 NaClLog(4, 333 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n"); 334 } 335 } else { 336 // Requires PNaCl translation. 337 NaClLog(4, 338 "OpenManifestEntry_MainThreadContinuation: " 339 "pulling down and translating.\n"); 340 if (plugin_->nacl_interface()->IsPnaclEnabled()) { 341 pp::CompletionCallback translate_callback = 342 WeakRefNewCallback( 343 anchor_, 344 this, 345 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation, 346 open_cont); 347 // Will always call the callback on success or failure. 348 pnacl_coordinator_.reset( 349 PnaclCoordinator::BitcodeToNative(plugin_, 350 mapped_url, 351 pnacl_options, 352 translate_callback)); 353 } else { 354 nacl::MutexLocker take(&mu_); 355 *p->op_complete_ptr = true; // done... 356 p->file_info->desc = -1; // but failed. 357 p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED, 358 "ServiceRuntime: GetPnaclFd failed -- pnacl not " 359 "enabled with --enable-pnacl."); 360 NaClXCondVarBroadcast(&cv_); 361 return; 362 } 363 } 364 // p is deleted automatically 365 } 366 367 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( 368 OpenManifestEntryResource* p, 369 int32_t result) { 370 NaClLog(4, 371 "Entered StreamAsFile_MainThreadContinuation\n"); 372 373 nacl::MutexLocker take(&mu_); 374 if (result == PP_OK) { 375 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n", 376 p->url.c_str()); 377 *p->file_info = plugin_->GetFileInfo(p->url); 378 379 NaClLog(4, 380 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", 381 p->file_info->desc); 382 } else { 383 NaClLog(4, 384 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); 385 p->file_info->desc = -1; 386 p->error_info->SetReport(ERROR_MANIFEST_OPEN, 387 "Plugin StreamAsFile failed at callback"); 388 } 389 *p->op_complete_ptr = true; 390 NaClXCondVarBroadcast(&cv_); 391 } 392 393 394 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation( 395 OpenManifestEntryResource* p, 396 int32_t result) { 397 NaClLog(4, 398 "Entered BitcodeTranslate_MainThreadContinuation\n"); 399 400 nacl::MutexLocker take(&mu_); 401 if (result == PP_OK) { 402 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is 403 // a host IO desc and doing a downcast. Once the ReverseInterface 404 // accepts NaClDescs we can avoid this downcast. 405 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc(); 406 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc; 407 p->file_info->desc = ndiodp->hd->d; 408 pnacl_coordinator_.reset(NULL); 409 NaClLog(4, 410 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n", 411 p->file_info->desc); 412 } else { 413 NaClLog(4, 414 "BitcodeTranslate_MainThreadContinuation: !PP_OK, " 415 "setting desc -1\n"); 416 p->file_info->desc = -1; 417 // Error should have been reported by pnacl coordinator. 418 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n"); 419 } 420 *p->op_complete_ptr = true; 421 NaClXCondVarBroadcast(&cv_); 422 } 423 424 425 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { 426 bool op_complete = false; 427 bool op_result; 428 CloseManifestEntryResource* to_close = 429 new CloseManifestEntryResource(desc, &op_complete, &op_result); 430 431 bool shutting_down; 432 plugin::WeakRefCallOnMainThread( 433 anchor_, 434 0, 435 this, 436 &plugin::PluginReverseInterface:: 437 CloseManifestEntry_MainThreadContinuation, 438 to_close); 439 // wait for completion or surf-away. 440 do { 441 nacl::MutexLocker take(&mu_); 442 for (;;) { 443 shutting_down = shutting_down_; 444 if (op_complete || shutting_down) { 445 break; 446 } 447 NaClXCondVarWait(&cv_, &mu_); 448 } 449 } while (0); 450 451 if (shutting_down) return false; 452 // op_result true if close was successful; false otherwise (e.g., bad desc). 453 return op_result; 454 } 455 456 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation( 457 CloseManifestEntryResource* cls, 458 int32_t err) { 459 UNREFERENCED_PARAMETER(err); 460 461 nacl::MutexLocker take(&mu_); 462 // TODO(bsy): once the plugin has a reliable way to report that the 463 // file usage is done -- and sel_ldr uses this RPC call -- we should 464 // tell the plugin that the associated resources can be freed. 465 *cls->op_result_ptr = true; 466 *cls->op_complete_ptr = true; 467 NaClXCondVarBroadcast(&cv_); 468 // cls automatically deleted 469 } 470 471 void PluginReverseInterface::ReportCrash() { 472 NaClLog(4, "PluginReverseInterface::ReportCrash\n"); 473 474 if (crash_cb_.pp_completion_callback().func != NULL) { 475 NaClLog(4, "PluginReverseInterface::ReportCrash: invoking CB\n"); 476 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb_, PP_OK); 477 } else { 478 NaClLog(1, 479 "PluginReverseInterface::ReportCrash:" 480 " crash_cb_ not valid, skipping\n"); 481 } 482 } 483 484 void PluginReverseInterface::ReportExitStatus(int exit_status) { 485 service_runtime_->set_exit_status(exit_status); 486 } 487 488 void PluginReverseInterface::QuotaRequest_MainThreadContinuation( 489 QuotaRequest* request, 490 int32_t err) { 491 if (err != PP_OK) { 492 return; 493 } 494 495 switch (request->data.type) { 496 case plugin::PepperQuotaType: { 497 const PPB_FileIOTrusted* file_io_trusted = 498 static_cast<const PPB_FileIOTrusted*>( 499 pp::Module::Get()->GetBrowserInterface( 500 PPB_FILEIOTRUSTED_INTERFACE)); 501 // Copy the request object because this one will be deleted on return. 502 // copy ctor! 503 QuotaRequest* cont_for_response = new QuotaRequest(*request); 504 pp::CompletionCallback quota_cc = WeakRefNewCallback( 505 anchor_, 506 this, 507 &PluginReverseInterface::QuotaRequest_MainThreadResponse, 508 cont_for_response); 509 file_io_trusted->WillWrite(request->data.resource, 510 request->offset, 511 // TODO(sehr): remove need for cast. 512 // Unify WillWrite interface vs Quota request. 513 nacl::assert_cast<int32_t>( 514 request->bytes_requested), 515 quota_cc.pp_completion_callback()); 516 break; 517 } 518 case plugin::TempQuotaType: { 519 uint64_t len = request->offset + request->bytes_requested; 520 nacl::MutexLocker take(&mu_); 521 // Do some crude quota enforcement. 522 if (len > kMaxTempQuota) { 523 *request->bytes_granted = 0; 524 } else { 525 *request->bytes_granted = request->bytes_requested; 526 } 527 *request->op_complete_ptr = true; 528 NaClXCondVarBroadcast(&cv_); 529 break; 530 } 531 } 532 // request automatically deleted 533 } 534 535 void PluginReverseInterface::QuotaRequest_MainThreadResponse( 536 QuotaRequest* request, 537 int32_t err) { 538 NaClLog(4, 539 "PluginReverseInterface::QuotaRequest_MainThreadResponse:" 540 " (resource=%" NACL_PRIx32 ", offset=%" NACL_PRId64 ", requested=%" 541 NACL_PRId64 ", err=%" NACL_PRId32 ")\n", 542 request->data.resource, 543 request->offset, request->bytes_requested, err); 544 nacl::MutexLocker take(&mu_); 545 if (err >= PP_OK) { 546 *request->bytes_granted = err; 547 } else { 548 *request->bytes_granted = 0; 549 } 550 *request->op_complete_ptr = true; 551 NaClXCondVarBroadcast(&cv_); 552 // request automatically deleted 553 } 554 555 int64_t PluginReverseInterface::RequestQuotaForWrite( 556 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { 557 NaClLog(4, 558 "PluginReverseInterface::RequestQuotaForWrite:" 559 " (file_id='%s', offset=%" NACL_PRId64 ", bytes_to_write=%" 560 NACL_PRId64 ")\n", file_id.c_str(), offset, bytes_to_write); 561 QuotaData quota_data; 562 { 563 nacl::MutexLocker take(&mu_); 564 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10); 565 if (quota_map_.find(file_key) == quota_map_.end()) { 566 // Look up failed to find the requested quota managed resource. 567 NaClLog(4, "PluginReverseInterface::RequestQuotaForWrite: failed...\n"); 568 return 0; 569 } 570 quota_data = quota_map_[file_key]; 571 } 572 // Variables set by requesting quota. 573 int64_t quota_granted = 0; 574 bool op_complete = false; 575 QuotaRequest* continuation = 576 new QuotaRequest(quota_data, offset, bytes_to_write, "a_granted, 577 &op_complete); 578 // The reverse service is running on a background thread and the PPAPI quota 579 // methods must be invoked only from the main thread. 580 plugin::WeakRefCallOnMainThread( 581 anchor_, 582 0, /* delay in ms */ 583 this, 584 &plugin::PluginReverseInterface::QuotaRequest_MainThreadContinuation, 585 continuation); 586 // Wait for the main thread to request quota and signal completion. 587 // It is also possible that the main thread will signal shut down. 588 bool shutting_down; 589 do { 590 nacl::MutexLocker take(&mu_); 591 for (;;) { 592 shutting_down = shutting_down_; 593 if (op_complete || shutting_down) { 594 break; 595 } 596 NaClXCondVarWait(&cv_, &mu_); 597 } 598 } while (0); 599 if (shutting_down) return 0; 600 return quota_granted; 601 } 602 603 void PluginReverseInterface::AddQuotaManagedFile(const nacl::string& file_id, 604 const pp::FileIO& file_io) { 605 PP_Resource resource = file_io.pp_resource(); 606 NaClLog(4, 607 "PluginReverseInterface::AddQuotaManagedFile: " 608 "(file_id='%s', file_io_ref=%" NACL_PRIx32 ")\n", 609 file_id.c_str(), resource); 610 nacl::MutexLocker take(&mu_); 611 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10); 612 QuotaData data(plugin::PepperQuotaType, resource); 613 quota_map_[file_key] = data; 614 } 615 616 void PluginReverseInterface::AddTempQuotaManagedFile( 617 const nacl::string& file_id) { 618 NaClLog(4, "PluginReverseInterface::AddTempQuotaManagedFile: " 619 "(file_id='%s')\n", file_id.c_str()); 620 nacl::MutexLocker take(&mu_); 621 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10); 622 QuotaData data(plugin::TempQuotaType, 0); 623 quota_map_[file_key] = data; 624 } 625 626 ServiceRuntime::ServiceRuntime(Plugin* plugin, 627 const Manifest* manifest, 628 bool should_report_uma, 629 pp::CompletionCallback init_done_cb, 630 pp::CompletionCallback crash_cb) 631 : plugin_(plugin), 632 should_report_uma_(should_report_uma), 633 reverse_service_(NULL), 634 anchor_(new nacl::WeakRefAnchor()), 635 rev_interface_(new PluginReverseInterface(anchor_, plugin, 636 manifest, 637 this, 638 init_done_cb, crash_cb)), 639 exit_status_(-1), 640 start_sel_ldr_done_(false) { 641 NaClSrpcChannelInitialize(&command_channel_); 642 NaClXMutexCtor(&mu_); 643 NaClXCondVarCtor(&cond_); 644 } 645 646 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* nacl_desc, 647 ErrorInfo* error_info) { 648 NaClLog(4, "ServiceRuntime::InitCommunication" 649 " (this=%p, subprocess=%p)\n", 650 static_cast<void*>(this), 651 static_cast<void*>(subprocess_.get())); 652 // Create the command channel to the sel_ldr and load the nexe from nacl_desc. 653 if (!subprocess_->SetupCommandAndLoad(&command_channel_, nacl_desc)) { 654 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, 655 "ServiceRuntime: command channel creation failed"); 656 return false; 657 } 658 // Hook up the reverse service channel. We are the IMC client, but 659 // provide SRPC service. 660 NaClDesc* out_conn_cap; 661 NaClSrpcResultCodes rpc_result = 662 NaClSrpcInvokeBySignature(&command_channel_, 663 "reverse_setup::h", 664 &out_conn_cap); 665 666 if (NACL_SRPC_RESULT_OK != rpc_result) { 667 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP, 668 "ServiceRuntime: reverse setup rpc failed"); 669 return false; 670 } 671 // Get connection capability to service runtime where the IMC 672 // server/SRPC client is waiting for a rendezvous. 673 NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n", 674 (uintptr_t) out_conn_cap); 675 nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup( 676 out_conn_cap); 677 if (conn_cap == NULL) { 678 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER, 679 "ServiceRuntime: wrapper allocation failure"); 680 return false; 681 } 682 out_conn_cap = NULL; // ownership passed 683 NaClLog(4, "ServiceRuntime::InitCommunication: starting reverse service\n"); 684 reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref()); 685 if (!reverse_service_->Start()) { 686 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE, 687 "ServiceRuntime: starting reverse services failed"); 688 return false; 689 } 690 691 // start the module. otherwise we cannot connect for multimedia 692 // subsystem since that is handled by user-level code (not secure!) 693 // in libsrpc. 694 int load_status = -1; 695 rpc_result = 696 NaClSrpcInvokeBySignature(&command_channel_, 697 "start_module::i", 698 &load_status); 699 700 if (NACL_SRPC_RESULT_OK != rpc_result) { 701 error_info->SetReport(ERROR_SEL_LDR_START_MODULE, 702 "ServiceRuntime: could not start nacl module"); 703 return false; 704 } 705 NaClLog(4, "ServiceRuntime::InitCommunication (load_status=%d)\n", 706 load_status); 707 if (should_report_uma_) { 708 plugin_->ReportSelLdrLoadStatus(load_status); 709 } 710 if (LOAD_OK != load_status) { 711 error_info->SetReport( 712 ERROR_SEL_LDR_START_STATUS, 713 NaClErrorString(static_cast<NaClErrorCode>(load_status))); 714 return false; 715 } 716 return true; 717 } 718 719 bool ServiceRuntime::StartSelLdr(const SelLdrStartParams& params) { 720 NaClLog(4, "ServiceRuntime::Start\n"); 721 722 nacl::scoped_ptr<SelLdrLauncherChrome> 723 tmp_subprocess(new SelLdrLauncherChrome()); 724 if (NULL == tmp_subprocess.get()) { 725 NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n"); 726 params.error_info->SetReport( 727 ERROR_SEL_LDR_CREATE_LAUNCHER, 728 "ServiceRuntime: failed to create sel_ldr launcher"); 729 return false; 730 } 731 nacl::string error_message; 732 bool started = tmp_subprocess->Start(plugin_->pp_instance(), 733 params.url.c_str(), 734 params.uses_irt, 735 params.uses_ppapi, 736 params.enable_dev_interfaces, 737 params.enable_dyncode_syscalls, 738 params.enable_exception_handling, 739 &error_message); 740 if (!started) { 741 NaClLog(LOG_ERROR, "ServiceRuntime::Start (start failed)\n"); 742 params.error_info->SetReportWithConsoleOnlyError( 743 ERROR_SEL_LDR_LAUNCH, 744 "ServiceRuntime: failed to start", 745 error_message); 746 return false; 747 } 748 749 subprocess_.reset(tmp_subprocess.release()); 750 NaClLog(4, "ServiceRuntime::StartSelLdr (return 1)\n"); 751 return true; 752 } 753 754 void ServiceRuntime::WaitForSelLdrStart() { 755 nacl::MutexLocker take(&mu_); 756 while(!start_sel_ldr_done_) { 757 NaClXCondVarWait(&cond_, &mu_); 758 } 759 } 760 761 void ServiceRuntime::SignalStartSelLdrDone() { 762 nacl::MutexLocker take(&mu_); 763 start_sel_ldr_done_ = true; 764 NaClXCondVarSignal(&cond_); 765 } 766 767 bool ServiceRuntime::LoadNexeAndStart(nacl::DescWrapper* nacl_desc, 768 ErrorInfo* error_info, 769 const pp::CompletionCallback& crash_cb) { 770 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (nacl_desc=%p)\n", 771 reinterpret_cast<void*>(nacl_desc)); 772 if (!InitCommunication(nacl_desc, error_info)) { 773 // On a load failure the service runtime does not crash itself to 774 // avoid a race where the no-more-senders error on the reverse 775 // channel esrvice thread might cause the crash-detection logic to 776 // kick in before the start_module RPC reply has been received. So 777 // we induce a service runtime crash here. We do not release 778 // subprocess_ since it's needed to collect crash log output after 779 // the error is reported. 780 Log(LOG_FATAL, "reap logs"); 781 if (NULL == reverse_service_) { 782 // No crash detector thread. 783 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); 784 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb, PP_OK); 785 NaClLog(LOG_ERROR, "should fire soon\n"); 786 } else { 787 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); 788 } 789 return false; 790 } 791 792 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n"); 793 return true; 794 } 795 796 SrpcClient* ServiceRuntime::SetupAppChannel() { 797 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", 798 reinterpret_cast<void*>(subprocess_.get())); 799 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); 800 if (NULL == connect_desc) { 801 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); 802 return NULL; 803 } else { 804 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", 805 static_cast<void*>(connect_desc)); 806 SrpcClient* srpc_client = SrpcClient::New(connect_desc); 807 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", 808 static_cast<void*>(srpc_client)); 809 delete connect_desc; 810 return srpc_client; 811 } 812 } 813 814 bool ServiceRuntime::Log(int severity, const nacl::string& msg) { 815 NaClSrpcResultCodes rpc_result = 816 NaClSrpcInvokeBySignature(&command_channel_, 817 "log:is:", 818 severity, 819 strdup(msg.c_str())); 820 return (NACL_SRPC_RESULT_OK == rpc_result); 821 } 822 823 void ServiceRuntime::Shutdown() { 824 rev_interface_->ShutDown(); 825 anchor_->Abandon(); 826 // Abandon callbacks, tell service threads to quit if they were 827 // blocked waiting for main thread operations to finish. Note that 828 // some callbacks must still await their completion event, e.g., 829 // CallOnMainThread must still wait for the time out, or I/O events 830 // must finish, so resources associated with pending events cannot 831 // be deallocated. 832 833 // Note that this does waitpid() to get rid of any zombie subprocess. 834 subprocess_.reset(NULL); 835 836 NaClSrpcDtor(&command_channel_); 837 838 // subprocess_ has been shut down, but threads waiting on messages 839 // from the service runtime may not have noticed yet. The low-level 840 // NaClSimpleRevService code takes care to refcount the data objects 841 // that it needs, and reverse_service_ is also refcounted. We wait 842 // for the service threads to get their EOF indications. 843 if (reverse_service_ != NULL) { 844 reverse_service_->WaitForServiceThreadsToExit(); 845 reverse_service_->Unref(); 846 reverse_service_ = NULL; 847 } 848 } 849 850 ServiceRuntime::~ServiceRuntime() { 851 NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n", 852 static_cast<void*>(this)); 853 // We do this just in case Shutdown() was not called. 854 subprocess_.reset(NULL); 855 if (reverse_service_ != NULL) { 856 reverse_service_->Unref(); 857 } 858 859 rev_interface_->Unref(); 860 861 anchor_->Unref(); 862 NaClCondVarDtor(&cond_); 863 NaClMutexDtor(&mu_); 864 } 865 866 int ServiceRuntime::exit_status() { 867 nacl::MutexLocker take(&mu_); 868 return exit_status_; 869 } 870 871 void ServiceRuntime::set_exit_status(int exit_status) { 872 nacl::MutexLocker take(&mu_); 873 exit_status_ = exit_status & 0xff; 874 } 875 876 nacl::string ServiceRuntime::GetCrashLogOutput() { 877 if (NULL != subprocess_.get()) { 878 return subprocess_->GetCrashLogOutput(); 879 } else { 880 return std::string(); 881 } 882 } 883 884 } // namespace plugin 885