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