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.
5 #include "content/ppapi_plugin/ppapi_thread.h"
9 #include "base/command_line.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/discardable_memory_allocator.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/rand_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/time/time.h"
22 #include "base/trace_event/trace_event.h"
23 #include "content/child/browser_font_resource_trusted.h"
24 #include "content/child/child_discardable_shared_memory_manager.h"
25 #include "content/child/child_process.h"
26 #include "content/common/child_process_messages.h"
27 #include "content/common/sandbox_util.h"
28 #include "content/ppapi_plugin/broker_process_dispatcher.h"
29 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
30 #include "content/ppapi_plugin/ppapi_blink_platform_impl.h"
31 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/pepper_plugin_info.h"
34 #include "content/public/common/sandbox_init.h"
35 #include "content/public/plugin/content_plugin_client.h"
36 #include "ipc/ipc_channel_handle.h"
37 #include "ipc/ipc_platform_file.h"
38 #include "ipc/ipc_sync_channel.h"
39 #include "ipc/ipc_sync_message_filter.h"
40 #include "ppapi/c/dev/ppp_network_state_dev.h"
41 #include "ppapi/c/pp_errors.h"
42 #include "ppapi/c/ppp.h"
43 #include "ppapi/proxy/interface_list.h"
44 #include "ppapi/proxy/plugin_globals.h"
45 #include "ppapi/proxy/plugin_message_filter.h"
46 #include "ppapi/proxy/ppapi_messages.h"
47 #include "ppapi/proxy/resource_reply_thread_registrar.h"
48 #include "third_party/WebKit/public/web/WebKit.h"
49 #include "ui/base/ui_base_switches.h"
52 #include "base/win/win_util.h"
53 #include "base/win/windows_version.h"
54 #include "sandbox/win/src/sandbox.h"
55 #elif defined(OS_MACOSX)
56 #include "content/common/sandbox_init_mac.h"
60 const char kWidevineCdmAdapterFileName
[] = "widevinecdmadapter.dll";
62 extern sandbox::TargetServices
* g_target_services
;
64 // Used by EnumSystemLocales for warming up.
65 static BOOL CALLBACK
EnumLocalesProc(LPTSTR lpLocaleString
) {
69 static BOOL CALLBACK
EnumLocalesProcEx(
70 LPWSTR lpLocaleString
,
76 // Warm up language subsystems before the sandbox is turned on.
77 static void WarmupWindowsLocales(const ppapi::PpapiPermissions
& permissions
) {
78 ::GetUserDefaultLangID();
79 ::GetUserDefaultLCID();
81 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
82 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
83 typedef BOOL (WINAPI
*PfnEnumSystemLocalesEx
)
84 (LOCALE_ENUMPROCEX
, DWORD
, LPARAM
, LPVOID
);
86 HMODULE handle_kern32
= GetModuleHandleW(L
"Kernel32.dll");
87 PfnEnumSystemLocalesEx enum_sys_locales_ex
=
88 reinterpret_cast<PfnEnumSystemLocalesEx
>
89 (GetProcAddress(handle_kern32
, "EnumSystemLocalesEx"));
91 enum_sys_locales_ex(EnumLocalesProcEx
, LOCALE_WINDOWS
, 0, 0);
93 EnumSystemLocalesW(EnumLocalesProc
, LCID_INSTALLED
);
102 typedef int32_t (*InitializeBrokerFunc
)
103 (PP_ConnectInstance_Func
* connect_instance_func
);
105 PpapiThread::PpapiThread(const base::CommandLine
& command_line
, bool is_broker
)
106 : is_broker_(is_broker
),
107 plugin_globals_(GetIOTaskRunner()),
108 connect_instance_func_(NULL
),
109 local_pp_module_(base::RandInt(0, std::numeric_limits
<PP_Module
>::max())),
110 next_plugin_dispatcher_id_(1) {
111 plugin_globals_
.SetPluginProxyDelegate(this);
112 plugin_globals_
.set_command_line(
113 command_line
.GetSwitchValueASCII(switches::kPpapiFlashArgs
));
115 blink_platform_impl_
.reset(new PpapiBlinkPlatformImpl
);
116 blink::initialize(blink_platform_impl_
.get());
119 scoped_refptr
<ppapi::proxy::PluginMessageFilter
> plugin_filter(
120 new ppapi::proxy::PluginMessageFilter(
121 NULL
, plugin_globals_
.resource_reply_thread_registrar()));
122 channel()->AddFilter(plugin_filter
.get());
123 plugin_globals_
.RegisterResourceMessageFilters(plugin_filter
.get());
126 // In single process, browser main loop set up the discardable memory
128 if (!command_line
.HasSwitch(switches::kSingleProcess
)) {
129 base::DiscardableMemoryAllocator::SetInstance(
130 ChildThreadImpl::discardable_shared_memory_manager());
134 PpapiThread::~PpapiThread() {
137 void PpapiThread::Shutdown() {
138 ChildThreadImpl::Shutdown();
140 ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate();
141 if (plugin_entry_points_
.shutdown_module
)
142 plugin_entry_points_
.shutdown_module();
143 blink_platform_impl_
->Shutdown();
147 bool PpapiThread::Send(IPC::Message
* msg
) {
148 // Allow access from multiple threads.
149 if (base::MessageLoop::current() == message_loop())
150 return ChildThreadImpl::Send(msg
);
152 return sync_message_filter()->Send(msg
);
155 // Note that this function is called only for messages from the channel to the
156 // browser process. Messages from the renderer process are sent via a different
157 // channel that ends up at Dispatcher::OnMessageReceived.
158 bool PpapiThread::OnControlMessageReceived(const IPC::Message
& msg
) {
160 IPC_BEGIN_MESSAGE_MAP(PpapiThread
, msg
)
161 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin
, OnLoadPlugin
)
162 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel
, OnCreateChannel
)
163 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState
, OnSetNetworkState
)
164 IPC_MESSAGE_HANDLER(PpapiMsg_Crash
, OnCrash
)
165 IPC_MESSAGE_HANDLER(PpapiMsg_Hang
, OnHang
)
166 IPC_MESSAGE_UNHANDLED(handled
= false)
167 IPC_END_MESSAGE_MAP()
171 void PpapiThread::OnChannelConnected(int32 peer_pid
) {
172 ChildThreadImpl::OnChannelConnected(peer_pid
);
175 peer_handle_
.Set(::OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, peer_pid
));
179 base::SingleThreadTaskRunner
* PpapiThread::GetIPCTaskRunner() {
180 return ChildProcess::current()->io_task_runner();
183 base::WaitableEvent
* PpapiThread::GetShutdownEvent() {
184 return ChildProcess::current()->GetShutDownEvent();
187 IPC::PlatformFileForTransit
PpapiThread::ShareHandleWithRemote(
188 base::PlatformFile handle
,
189 base::ProcessId peer_pid
,
190 bool should_close_source
) {
192 if (peer_handle_
.IsValid()) {
194 return IPC::GetFileHandleForProcess(handle
, peer_handle_
.Get(),
195 should_close_source
);
199 DCHECK(peer_pid
!= base::kNullProcessId
);
200 return BrokerGetFileHandleForProcess(handle
, peer_pid
, should_close_source
);
203 base::SharedMemoryHandle
PpapiThread::ShareSharedMemoryHandleWithRemote(
204 const base::SharedMemoryHandle
& handle
,
205 base::ProcessId remote_pid
) {
207 if (peer_handle_
.IsValid()) {
209 return IPC::GetFileHandleForProcess(handle
, peer_handle_
.Get(), false);
213 DCHECK(remote_pid
!= base::kNullProcessId
);
214 #if defined(OS_WIN) || defined(OS_MACOSX)
215 base::SharedMemoryHandle duped_handle
;
217 BrokerDuplicateSharedMemoryHandle(handle
, remote_pid
, &duped_handle
);
220 return base::SharedMemory::NULLHandle();
222 return base::SharedMemory::DuplicateHandle(handle
);
223 #endif // defined(OS_WIN) || defined(OS_MACOSX)
226 std::set
<PP_Instance
>* PpapiThread::GetGloballySeenInstanceIDSet() {
227 return &globally_seen_instance_ids_
;
230 IPC::Sender
* PpapiThread::GetBrowserSender() {
234 std::string
PpapiThread::GetUILanguage() {
235 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
236 return command_line
->GetSwitchValueASCII(switches::kLang
);
239 void PpapiThread::PreCacheFontForFlash(const void* logfontw
) {
241 ChildThreadImpl::PreCacheFont(*static_cast<const LOGFONTW
*>(logfontw
));
245 void PpapiThread::SetActiveURL(const std::string
& url
) {
246 GetContentClient()->SetActiveURL(GURL(url
));
249 PP_Resource
PpapiThread::CreateBrowserFont(
250 ppapi::proxy::Connection connection
,
251 PP_Instance instance
,
252 const PP_BrowserFont_Trusted_Description
& desc
,
253 const ppapi::Preferences
& prefs
) {
254 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc
))
256 return (new BrowserFontResource_Trusted(
257 connection
, instance
, desc
, prefs
))->GetReference();
260 uint32
PpapiThread::Register(ppapi::proxy::PluginDispatcher
* plugin_dispatcher
) {
261 if (!plugin_dispatcher
||
262 plugin_dispatchers_
.size() >= std::numeric_limits
<uint32
>::max()) {
268 // Although it is unlikely, make sure that we won't cause any trouble when
269 // the counter overflows.
270 id
= next_plugin_dispatcher_id_
++;
272 plugin_dispatchers_
.find(id
) != plugin_dispatchers_
.end());
273 plugin_dispatchers_
[id
] = plugin_dispatcher
;
277 void PpapiThread::Unregister(uint32 plugin_dispatcher_id
) {
278 plugin_dispatchers_
.erase(plugin_dispatcher_id
);
281 void PpapiThread::OnLoadPlugin(const base::FilePath
& path
,
282 const ppapi::PpapiPermissions
& permissions
) {
283 // In case of crashes, the crash dump doesn't indicate which plugin
285 base::debug::SetCrashKeyValue("ppapi_path", path
.MaybeAsASCII());
287 SavePluginName(path
);
289 // This must be set before calling into the plugin so it can get the
290 // interfaces it has permission for.
291 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions
);
292 permissions_
= permissions
;
294 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
295 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
296 // client) then fetch the entry points from the embedder, rather than a DLL.
297 std::vector
<PepperPluginInfo
> plugins
;
298 GetContentClient()->AddPepperPlugins(&plugins
);
299 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
300 if (plugins
[i
].is_internal
&& plugins
[i
].path
== path
) {
301 // An internal plugin is being loaded, so fetch the entry points.
302 plugin_entry_points_
= plugins
[i
].internal_entry_points
;
306 // If the plugin isn't internal then load it from |path|.
307 base::ScopedNativeLibrary library
;
308 if (plugin_entry_points_
.initialize_module
== NULL
) {
309 // Load the plugin from the specified library.
310 base::NativeLibraryLoadError error
;
311 base::TimeDelta load_time
;
313 TRACE_EVENT1("ppapi", "PpapiThread::LoadPlugin", "path",
314 path
.MaybeAsASCII());
316 base::TimeTicks start
= base::TimeTicks::Now();
317 library
.Reset(base::LoadNativeLibrary(path
, &error
));
318 load_time
= base::TimeTicks::Now() - start
;
321 if (!library
.is_valid()) {
322 LOG(ERROR
) << "Failed to load Pepper module from " << path
.value()
323 << " (error: " << error
.ToString() << ")";
324 if (!base::PathExists(path
)) {
325 ReportLoadResult(path
, FILE_MISSING
);
328 ReportLoadResult(path
, LOAD_FAILED
);
329 // Report detailed reason for load failure.
330 ReportLoadErrorCode(path
, error
);
334 // Only report load time for success loads.
335 ReportLoadTime(path
, load_time
);
337 // Get the GetInterface function (required).
338 plugin_entry_points_
.get_interface
=
339 reinterpret_cast<PP_GetInterface_Func
>(
340 library
.GetFunctionPointer("PPP_GetInterface"));
341 if (!plugin_entry_points_
.get_interface
) {
342 LOG(WARNING
) << "No PPP_GetInterface in plugin library";
343 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
347 // The ShutdownModule/ShutdownBroker function is optional.
348 plugin_entry_points_
.shutdown_module
=
350 reinterpret_cast<PP_ShutdownModule_Func
>(
351 library
.GetFunctionPointer("PPP_ShutdownBroker")) :
352 reinterpret_cast<PP_ShutdownModule_Func
>(
353 library
.GetFunctionPointer("PPP_ShutdownModule"));
356 // Get the InitializeModule function (required for non-broker code).
357 plugin_entry_points_
.initialize_module
=
358 reinterpret_cast<PP_InitializeModule_Func
>(
359 library
.GetFunctionPointer("PPP_InitializeModule"));
360 if (!plugin_entry_points_
.initialize_module
) {
361 LOG(WARNING
) << "No PPP_InitializeModule in plugin library";
362 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
369 // If code subsequently tries to exit using abort(), force a crash (since
370 // otherwise these would be silent terminations and fly under the radar).
371 base::win::SetAbortBehaviorForCrashReporting();
373 // Once we lower the token the sandbox is locked down and no new modules
374 // can be loaded. TODO(cpu): consider changing to the loading style of
376 if (g_target_services
) {
377 // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+.
378 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
) ||
379 path
.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName
) {
380 if (base::win::OSInfo::GetInstance()->version() >=
381 base::win::VERSION_VISTA
) {
382 LoadLibraryA("dxva2.dll");
386 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
387 if (base::win::OSInfo::GetInstance()->version() >=
388 base::win::VERSION_WIN7
) {
390 if (cpu
.vendor_name() == "AuthenticAMD") {
391 // The AMD crypto acceleration is only AMD Bulldozer and above.
393 LoadLibraryA("amdhcp64.dll");
395 LoadLibraryA("amdhcp32.dll");
401 // Cause advapi32 to load before the sandbox is turned on.
402 unsigned int dummy_rand
;
405 WarmupWindowsLocales(permissions
);
407 g_target_services
->LowerToken();
412 // Get the InitializeBroker function (required).
413 InitializeBrokerFunc init_broker
=
414 reinterpret_cast<InitializeBrokerFunc
>(
415 library
.GetFunctionPointer("PPP_InitializeBroker"));
417 LOG(WARNING
) << "No PPP_InitializeBroker in plugin library";
418 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
422 int32_t init_error
= init_broker(&connect_instance_func_
);
423 if (init_error
!= PP_OK
) {
424 LOG(WARNING
) << "InitBroker failed with error " << init_error
;
425 ReportLoadResult(path
, INIT_FAILED
);
428 if (!connect_instance_func_
) {
429 LOG(WARNING
) << "InitBroker did not provide PP_ConnectInstance_Func";
430 ReportLoadResult(path
, INIT_FAILED
);
434 #if defined(OS_MACOSX)
435 // We need to do this after getting |PPP_GetInterface()| (or presumably
436 // doing something nontrivial with the library), else the sandbox
438 CHECK(InitializeSandbox());
441 int32_t init_error
= plugin_entry_points_
.initialize_module(
443 &ppapi::proxy::PluginDispatcher::GetBrowserInterface
);
444 if (init_error
!= PP_OK
) {
445 LOG(WARNING
) << "InitModule failed with error " << init_error
;
446 ReportLoadResult(path
, INIT_FAILED
);
451 // Initialization succeeded, so keep the plugin DLL loaded.
452 library_
.Reset(library
.Release());
454 ReportLoadResult(path
, LOAD_SUCCESS
);
457 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid
,
458 int renderer_child_id
,
460 IPC::ChannelHandle channel_handle
;
462 if (!plugin_entry_points_
.get_interface
|| // Plugin couldn't be loaded.
463 !SetupRendererChannel(renderer_pid
, renderer_child_id
, incognito
,
465 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
469 Send(new PpapiHostMsg_ChannelCreated(channel_handle
));
472 void PpapiThread::OnSetNetworkState(bool online
) {
473 // Note the browser-process side shouldn't send us these messages in the
474 // first unless the plugin has dev permissions, so we don't need to check
475 // again here. We don't want random plugins depending on this dev interface.
476 if (!plugin_entry_points_
.get_interface
)
478 const PPP_NetworkState_Dev
* ns
= static_cast<const PPP_NetworkState_Dev
*>(
479 plugin_entry_points_
.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE
));
481 ns
->SetOnLine(PP_FromBool(online
));
484 void PpapiThread::OnCrash() {
485 // Intentionally crash upon the request of the browser.
486 volatile int* null_pointer
= NULL
;
490 void PpapiThread::OnHang() {
491 // Intentionally hang upon the request of the browser.
493 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
496 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid
,
497 int renderer_child_id
,
499 IPC::ChannelHandle
* handle
) {
500 DCHECK(is_broker_
== (connect_instance_func_
!= NULL
));
501 IPC::ChannelHandle plugin_handle
;
502 plugin_handle
.name
= IPC::Channel::GenerateVerifiedChannelID(
504 "%d.r%d", base::GetCurrentProcId(), renderer_child_id
));
506 ppapi::proxy::ProxyChannel
* dispatcher
= NULL
;
507 bool init_result
= false;
509 BrokerProcessDispatcher
* broker_dispatcher
=
510 new BrokerProcessDispatcher(plugin_entry_points_
.get_interface
,
511 connect_instance_func_
);
512 init_result
= broker_dispatcher
->InitBrokerWithChannel(this,
516 dispatcher
= broker_dispatcher
;
518 PluginProcessDispatcher
* plugin_dispatcher
=
519 new PluginProcessDispatcher(plugin_entry_points_
.get_interface
,
522 init_result
= plugin_dispatcher
->InitPluginWithChannel(this,
526 dispatcher
= plugin_dispatcher
;
534 handle
->name
= plugin_handle
.name
;
535 #if defined(OS_POSIX)
536 // On POSIX, transfer ownership of the renderer-side (client) FD.
537 // This ensures this process will be notified when it is closed even if a
538 // connection is not established.
539 handle
->socket
= base::FileDescriptor(dispatcher
->TakeRendererFD());
540 if (handle
->socket
.fd
== -1)
544 // From here, the dispatcher will manage its own lifetime according to the
545 // lifetime of the attached channel.
549 void PpapiThread::SavePluginName(const base::FilePath
& path
) {
550 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
551 path
.BaseName().AsUTF8Unsafe());
553 // plugin() is NULL when in-process, which is fine, because this is
554 // just a hook for setting the process name.
555 if (GetContentClient()->plugin()) {
556 GetContentClient()->plugin()->PluginProcessStarted(
557 path
.BaseName().RemoveExtension().LossyDisplayName());
561 static std::string
GetHistogramName(bool is_broker
,
562 const std::string
& metric_name
,
563 const base::FilePath
& path
) {
564 return std::string("Plugin.Ppapi") + (is_broker
? "Broker" : "Plugin") +
565 metric_name
+ "_" + path
.BaseName().MaybeAsASCII();
568 void PpapiThread::ReportLoadResult(const base::FilePath
& path
,
570 DCHECK_LT(result
, LOAD_RESULT_MAX
);
572 // Note: This leaks memory, which is expected behavior.
573 base::HistogramBase
* histogram
=
574 base::LinearHistogram::FactoryGet(
575 GetHistogramName(is_broker_
, "LoadResult", path
),
579 base::HistogramBase::kUmaTargetedHistogramFlag
);
581 histogram
->Add(result
);
584 void PpapiThread::ReportLoadErrorCode(
585 const base::FilePath
& path
,
586 const base::NativeLibraryLoadError
& error
) {
587 // Only report load error code on Windows because that's the only platform that
588 // has a numerical error value.
590 // For sparse histograms, we can use the macro, as it does not incorporate a
592 UMA_HISTOGRAM_SPARSE_SLOWLY(
593 GetHistogramName(is_broker_
, "LoadErrorCode", path
), error
.code
);
597 void PpapiThread::ReportLoadTime(const base::FilePath
& path
,
598 const base::TimeDelta load_time
) {
599 // Note: This leaks memory, which is expected behavior.
600 base::HistogramBase
* histogram
=
601 base::Histogram::FactoryTimeGet(
602 GetHistogramName(is_broker_
, "LoadTime", path
),
603 base::TimeDelta::FromMilliseconds(1),
604 base::TimeDelta::FromSeconds(10),
606 base::HistogramBase::kUmaTargetedHistogramFlag
);
608 histogram
->AddTime(load_time
);
611 } // namespace content