1 // Copyright (c) 2012 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 "content/ppapi_plugin/ppapi_thread.h" 6 7 #include <limits> 8 9 #include "base/command_line.h" 10 #include "base/cpu.h" 11 #include "base/debug/crash_logging.h" 12 #include "base/files/file_util.h" 13 #include "base/logging.h" 14 #include "base/metrics/histogram.h" 15 #include "base/metrics/sparse_histogram.h" 16 #include "base/rand_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/threading/platform_thread.h" 20 #include "base/time/time.h" 21 #include "content/child/browser_font_resource_trusted.h" 22 #include "content/child/child_process.h" 23 #include "content/common/child_process_messages.h" 24 #include "content/common/sandbox_util.h" 25 #include "content/ppapi_plugin/broker_process_dispatcher.h" 26 #include "content/ppapi_plugin/plugin_process_dispatcher.h" 27 #include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h" 28 #include "content/public/common/content_client.h" 29 #include "content/public/common/content_switches.h" 30 #include "content/public/common/pepper_plugin_info.h" 31 #include "content/public/common/sandbox_init.h" 32 #include "content/public/plugin/content_plugin_client.h" 33 #include "ipc/ipc_channel_handle.h" 34 #include "ipc/ipc_platform_file.h" 35 #include "ipc/ipc_sync_channel.h" 36 #include "ipc/ipc_sync_message_filter.h" 37 #include "ppapi/c/dev/ppp_network_state_dev.h" 38 #include "ppapi/c/pp_errors.h" 39 #include "ppapi/c/ppp.h" 40 #include "ppapi/proxy/interface_list.h" 41 #include "ppapi/proxy/plugin_globals.h" 42 #include "ppapi/proxy/plugin_message_filter.h" 43 #include "ppapi/proxy/ppapi_messages.h" 44 #include "ppapi/proxy/resource_reply_thread_registrar.h" 45 #include "third_party/WebKit/public/web/WebKit.h" 46 #include "ui/base/ui_base_switches.h" 47 48 #if defined(OS_WIN) 49 #include "base/win/win_util.h" 50 #include "base/win/windows_version.h" 51 #include "sandbox/win/src/sandbox.h" 52 #elif defined(OS_MACOSX) 53 #include "content/common/sandbox_init_mac.h" 54 #endif 55 56 #if defined(OS_WIN) 57 const char kWidevineCdmAdapterFileName[] = "widevinecdmadapter.dll"; 58 59 extern sandbox::TargetServices* g_target_services; 60 61 // Used by EnumSystemLocales for warming up. 62 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) { 63 return TRUE; 64 } 65 66 static BOOL CALLBACK EnumLocalesProcEx( 67 LPWSTR lpLocaleString, 68 DWORD dwFlags, 69 LPARAM lParam) { 70 return TRUE; 71 } 72 73 // Warm up language subsystems before the sandbox is turned on. 74 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) { 75 ::GetUserDefaultLangID(); 76 ::GetUserDefaultLCID(); 77 78 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) { 79 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 80 typedef BOOL (WINAPI *PfnEnumSystemLocalesEx) 81 (LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID); 82 83 HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll"); 84 PfnEnumSystemLocalesEx enum_sys_locales_ex = 85 reinterpret_cast<PfnEnumSystemLocalesEx> 86 (GetProcAddress(handle_kern32, "EnumSystemLocalesEx")); 87 88 enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0); 89 } else { 90 EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED); 91 } 92 } 93 } 94 #else 95 extern void* g_target_services; 96 #endif 97 98 namespace content { 99 100 typedef int32_t (*InitializeBrokerFunc) 101 (PP_ConnectInstance_Func* connect_instance_func); 102 103 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker) 104 : is_broker_(is_broker), 105 connect_instance_func_(NULL), 106 local_pp_module_( 107 base::RandInt(0, std::numeric_limits<PP_Module>::max())), 108 next_plugin_dispatcher_id_(1) { 109 ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get(); 110 globals->SetPluginProxyDelegate(this); 111 globals->set_command_line( 112 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs)); 113 114 webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl); 115 blink::initialize(webkit_platform_support_.get()); 116 117 if (!is_broker_) { 118 channel()->AddFilter( 119 new ppapi::proxy::PluginMessageFilter( 120 NULL, globals->resource_reply_thread_registrar())); 121 } 122 } 123 124 PpapiThread::~PpapiThread() { 125 } 126 127 void PpapiThread::Shutdown() { 128 ChildThread::Shutdown(); 129 130 ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate(); 131 if (plugin_entry_points_.shutdown_module) 132 plugin_entry_points_.shutdown_module(); 133 webkit_platform_support_->Shutdown(); 134 blink::shutdown(); 135 } 136 137 bool PpapiThread::Send(IPC::Message* msg) { 138 // Allow access from multiple threads. 139 if (base::MessageLoop::current() == message_loop()) 140 return ChildThread::Send(msg); 141 142 return sync_message_filter()->Send(msg); 143 } 144 145 // Note that this function is called only for messages from the channel to the 146 // browser process. Messages from the renderer process are sent via a different 147 // channel that ends up at Dispatcher::OnMessageReceived. 148 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) { 149 bool handled = true; 150 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg) 151 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin) 152 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel) 153 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState) 154 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash) 155 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang) 156 IPC_MESSAGE_UNHANDLED(handled = false) 157 IPC_END_MESSAGE_MAP() 158 return handled; 159 } 160 161 void PpapiThread::OnChannelConnected(int32 peer_pid) { 162 ChildThread::OnChannelConnected(peer_pid); 163 #if defined(OS_WIN) 164 if (is_broker_) 165 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid)); 166 #endif 167 } 168 169 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() { 170 return ChildProcess::current()->io_message_loop_proxy(); 171 } 172 173 base::WaitableEvent* PpapiThread::GetShutdownEvent() { 174 return ChildProcess::current()->GetShutDownEvent(); 175 } 176 177 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote( 178 base::PlatformFile handle, 179 base::ProcessId peer_pid, 180 bool should_close_source) { 181 #if defined(OS_WIN) 182 if (peer_handle_.IsValid()) { 183 DCHECK(is_broker_); 184 return IPC::GetFileHandleForProcess(handle, peer_handle_.Get(), 185 should_close_source); 186 } 187 #endif 188 189 DCHECK(peer_pid != base::kNullProcessId); 190 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source); 191 } 192 193 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() { 194 return &globally_seen_instance_ids_; 195 } 196 197 IPC::Sender* PpapiThread::GetBrowserSender() { 198 return this; 199 } 200 201 std::string PpapiThread::GetUILanguage() { 202 CommandLine* command_line = CommandLine::ForCurrentProcess(); 203 return command_line->GetSwitchValueASCII(switches::kLang); 204 } 205 206 void PpapiThread::PreCacheFont(const void* logfontw) { 207 #if defined(OS_WIN) 208 Send(new ChildProcessHostMsg_PreCacheFont( 209 *static_cast<const LOGFONTW*>(logfontw))); 210 #endif 211 } 212 213 void PpapiThread::SetActiveURL(const std::string& url) { 214 GetContentClient()->SetActiveURL(GURL(url)); 215 } 216 217 PP_Resource PpapiThread::CreateBrowserFont( 218 ppapi::proxy::Connection connection, 219 PP_Instance instance, 220 const PP_BrowserFont_Trusted_Description& desc, 221 const ppapi::Preferences& prefs) { 222 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc)) 223 return 0; 224 return (new BrowserFontResource_Trusted( 225 connection, instance, desc, prefs))->GetReference(); 226 } 227 228 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) { 229 if (!plugin_dispatcher || 230 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) { 231 return 0; 232 } 233 234 uint32 id = 0; 235 do { 236 // Although it is unlikely, make sure that we won't cause any trouble when 237 // the counter overflows. 238 id = next_plugin_dispatcher_id_++; 239 } while (id == 0 || 240 plugin_dispatchers_.find(id) != plugin_dispatchers_.end()); 241 plugin_dispatchers_[id] = plugin_dispatcher; 242 return id; 243 } 244 245 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) { 246 plugin_dispatchers_.erase(plugin_dispatcher_id); 247 } 248 249 void PpapiThread::OnLoadPlugin(const base::FilePath& path, 250 const ppapi::PpapiPermissions& permissions) { 251 // In case of crashes, the crash dump doesn't indicate which plugin 252 // it came from. 253 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII()); 254 255 SavePluginName(path); 256 257 // This must be set before calling into the plugin so it can get the 258 // interfaces it has permission for. 259 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions); 260 permissions_ = permissions; 261 262 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser 263 // binary. If we're being asked to load such a plugin (e.g. the Chromoting 264 // client) then fetch the entry points from the embedder, rather than a DLL. 265 std::vector<PepperPluginInfo> plugins; 266 GetContentClient()->AddPepperPlugins(&plugins); 267 for (size_t i = 0; i < plugins.size(); ++i) { 268 if (plugins[i].is_internal && plugins[i].path == path) { 269 // An internal plugin is being loaded, so fetch the entry points. 270 plugin_entry_points_ = plugins[i].internal_entry_points; 271 } 272 } 273 274 // If the plugin isn't internal then load it from |path|. 275 base::ScopedNativeLibrary library; 276 if (plugin_entry_points_.initialize_module == NULL) { 277 // Load the plugin from the specified library. 278 base::NativeLibraryLoadError error; 279 library.Reset(base::LoadNativeLibrary(path, &error)); 280 if (!library.is_valid()) { 281 LOG(ERROR) << "Failed to load Pepper module from " << path.value() 282 << " (error: " << error.ToString() << ")"; 283 if (!base::PathExists(path)) { 284 ReportLoadResult(path, FILE_MISSING); 285 return; 286 } 287 ReportLoadResult(path, LOAD_FAILED); 288 // Report detailed reason for load failure. 289 ReportLoadErrorCode(path, error); 290 return; 291 } 292 293 // Get the GetInterface function (required). 294 plugin_entry_points_.get_interface = 295 reinterpret_cast<PP_GetInterface_Func>( 296 library.GetFunctionPointer("PPP_GetInterface")); 297 if (!plugin_entry_points_.get_interface) { 298 LOG(WARNING) << "No PPP_GetInterface in plugin library"; 299 ReportLoadResult(path, ENTRY_POINT_MISSING); 300 return; 301 } 302 303 // The ShutdownModule/ShutdownBroker function is optional. 304 plugin_entry_points_.shutdown_module = 305 is_broker_ ? 306 reinterpret_cast<PP_ShutdownModule_Func>( 307 library.GetFunctionPointer("PPP_ShutdownBroker")) : 308 reinterpret_cast<PP_ShutdownModule_Func>( 309 library.GetFunctionPointer("PPP_ShutdownModule")); 310 311 if (!is_broker_) { 312 // Get the InitializeModule function (required for non-broker code). 313 plugin_entry_points_.initialize_module = 314 reinterpret_cast<PP_InitializeModule_Func>( 315 library.GetFunctionPointer("PPP_InitializeModule")); 316 if (!plugin_entry_points_.initialize_module) { 317 LOG(WARNING) << "No PPP_InitializeModule in plugin library"; 318 ReportLoadResult(path, ENTRY_POINT_MISSING); 319 return; 320 } 321 } 322 } 323 324 #if defined(OS_WIN) 325 // If code subsequently tries to exit using abort(), force a crash (since 326 // otherwise these would be silent terminations and fly under the radar). 327 base::win::SetAbortBehaviorForCrashReporting(); 328 329 // Once we lower the token the sandbox is locked down and no new modules 330 // can be loaded. TODO(cpu): consider changing to the loading style of 331 // regular plugins. 332 if (g_target_services) { 333 // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+. 334 if (permissions.HasPermission(ppapi::PERMISSION_FLASH) || 335 path.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName) { 336 if (base::win::OSInfo::GetInstance()->version() >= 337 base::win::VERSION_VISTA) { 338 LoadLibraryA("dxva2.dll"); 339 } 340 } 341 342 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) { 343 if (base::win::OSInfo::GetInstance()->version() >= 344 base::win::VERSION_WIN7) { 345 base::CPU cpu; 346 if (cpu.vendor_name() == "AuthenticAMD") { 347 // The AMD crypto acceleration is only AMD Bulldozer and above. 348 #if defined(_WIN64) 349 LoadLibraryA("amdhcp64.dll"); 350 #else 351 LoadLibraryA("amdhcp32.dll"); 352 #endif 353 } 354 } 355 } 356 357 // Cause advapi32 to load before the sandbox is turned on. 358 unsigned int dummy_rand; 359 rand_s(&dummy_rand); 360 361 WarmupWindowsLocales(permissions); 362 363 g_target_services->LowerToken(); 364 } 365 #endif 366 367 if (is_broker_) { 368 // Get the InitializeBroker function (required). 369 InitializeBrokerFunc init_broker = 370 reinterpret_cast<InitializeBrokerFunc>( 371 library.GetFunctionPointer("PPP_InitializeBroker")); 372 if (!init_broker) { 373 LOG(WARNING) << "No PPP_InitializeBroker in plugin library"; 374 ReportLoadResult(path, ENTRY_POINT_MISSING); 375 return; 376 } 377 378 int32_t init_error = init_broker(&connect_instance_func_); 379 if (init_error != PP_OK) { 380 LOG(WARNING) << "InitBroker failed with error " << init_error; 381 ReportLoadResult(path, INIT_FAILED); 382 return; 383 } 384 if (!connect_instance_func_) { 385 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func"; 386 ReportLoadResult(path, INIT_FAILED); 387 return; 388 } 389 } else { 390 #if defined(OS_MACOSX) 391 // We need to do this after getting |PPP_GetInterface()| (or presumably 392 // doing something nontrivial with the library), else the sandbox 393 // intercedes. 394 CHECK(InitializeSandbox()); 395 #endif 396 397 int32_t init_error = plugin_entry_points_.initialize_module( 398 local_pp_module_, 399 &ppapi::proxy::PluginDispatcher::GetBrowserInterface); 400 if (init_error != PP_OK) { 401 LOG(WARNING) << "InitModule failed with error " << init_error; 402 ReportLoadResult(path, INIT_FAILED); 403 return; 404 } 405 } 406 407 // Initialization succeeded, so keep the plugin DLL loaded. 408 library_.Reset(library.Release()); 409 410 ReportLoadResult(path, LOAD_SUCCESS); 411 } 412 413 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid, 414 int renderer_child_id, 415 bool incognito) { 416 IPC::ChannelHandle channel_handle; 417 418 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded. 419 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito, 420 &channel_handle)) { 421 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle())); 422 return; 423 } 424 425 Send(new PpapiHostMsg_ChannelCreated(channel_handle)); 426 } 427 428 void PpapiThread::OnSetNetworkState(bool online) { 429 // Note the browser-process side shouldn't send us these messages in the 430 // first unless the plugin has dev permissions, so we don't need to check 431 // again here. We don't want random plugins depending on this dev interface. 432 if (!plugin_entry_points_.get_interface) 433 return; 434 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>( 435 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE)); 436 if (ns) 437 ns->SetOnLine(PP_FromBool(online)); 438 } 439 440 void PpapiThread::OnCrash() { 441 // Intentionally crash upon the request of the browser. 442 volatile int* null_pointer = NULL; 443 *null_pointer = 0; 444 } 445 446 void PpapiThread::OnHang() { 447 // Intentionally hang upon the request of the browser. 448 for (;;) 449 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); 450 } 451 452 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid, 453 int renderer_child_id, 454 bool incognito, 455 IPC::ChannelHandle* handle) { 456 DCHECK(is_broker_ == (connect_instance_func_ != NULL)); 457 IPC::ChannelHandle plugin_handle; 458 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID( 459 base::StringPrintf( 460 "%d.r%d", base::GetCurrentProcId(), renderer_child_id)); 461 462 ppapi::proxy::ProxyChannel* dispatcher = NULL; 463 bool init_result = false; 464 if (is_broker_) { 465 BrokerProcessDispatcher* broker_dispatcher = 466 new BrokerProcessDispatcher(plugin_entry_points_.get_interface, 467 connect_instance_func_); 468 init_result = broker_dispatcher->InitBrokerWithChannel(this, 469 renderer_pid, 470 plugin_handle, 471 false); 472 dispatcher = broker_dispatcher; 473 } else { 474 PluginProcessDispatcher* plugin_dispatcher = 475 new PluginProcessDispatcher(plugin_entry_points_.get_interface, 476 permissions_, 477 incognito); 478 init_result = plugin_dispatcher->InitPluginWithChannel(this, 479 renderer_pid, 480 plugin_handle, 481 false); 482 dispatcher = plugin_dispatcher; 483 } 484 485 if (!init_result) { 486 delete dispatcher; 487 return false; 488 } 489 490 handle->name = plugin_handle.name; 491 #if defined(OS_POSIX) 492 // On POSIX, transfer ownership of the renderer-side (client) FD. 493 // This ensures this process will be notified when it is closed even if a 494 // connection is not established. 495 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true); 496 if (handle->socket.fd == -1) 497 return false; 498 #endif 499 500 // From here, the dispatcher will manage its own lifetime according to the 501 // lifetime of the attached channel. 502 return true; 503 } 504 505 void PpapiThread::SavePluginName(const base::FilePath& path) { 506 ppapi::proxy::PluginGlobals::Get()->set_plugin_name( 507 path.BaseName().AsUTF8Unsafe()); 508 509 // plugin() is NULL when in-process, which is fine, because this is 510 // just a hook for setting the process name. 511 if (GetContentClient()->plugin()) { 512 GetContentClient()->plugin()->PluginProcessStarted( 513 path.BaseName().RemoveExtension().LossyDisplayName()); 514 } 515 } 516 517 void PpapiThread::ReportLoadResult(const base::FilePath& path, 518 LoadResult result) { 519 DCHECK_LT(result, LOAD_RESULT_MAX); 520 std::string histogram_name = std::string("Plugin.Ppapi") + 521 (is_broker_ ? "Broker" : "Plugin") + 522 "LoadResult_" + path.BaseName().MaybeAsASCII(); 523 524 // Note: This leaks memory, which is expected behavior. 525 base::HistogramBase* histogram = 526 base::LinearHistogram::FactoryGet( 527 histogram_name, 528 1, 529 LOAD_RESULT_MAX, 530 LOAD_RESULT_MAX + 1, 531 base::HistogramBase::kUmaTargetedHistogramFlag); 532 533 histogram->Add(result); 534 } 535 536 void PpapiThread::ReportLoadErrorCode( 537 const base::FilePath& path, 538 const base::NativeLibraryLoadError& error) { 539 #if defined(OS_WIN) 540 // Only report load error code on Windows because that's the only platform 541 // that has a numerical error value. 542 std::string histogram_name = 543 std::string("Plugin.Ppapi") + (is_broker_ ? "Broker" : "Plugin") + 544 "LoadErrorCode_" + path.BaseName().MaybeAsASCII(); 545 546 // For sparse histograms, we can use the macro, as it does not incorporate a 547 // static. 548 UMA_HISTOGRAM_SPARSE_SLOWLY(histogram_name, error.code); 549 #endif 550 } 551 552 } // namespace content 553