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