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