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