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/alias.h"
12 #include "base/debug/crash_logging.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/discardable_memory_allocator.h"
16 #include "base/metrics/histogram.h"
17 #include "base/metrics/sparse_histogram.h"
18 #include "base/rand_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "content/child/browser_font_resource_trusted.h"
25 #include "content/child/child_discardable_shared_memory_manager.h"
26 #include "content/child/child_process.h"
27 #include "content/common/child_process_messages.h"
28 #include "content/common/sandbox_util.h"
29 #include "content/ppapi_plugin/broker_process_dispatcher.h"
30 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
31 #include "content/ppapi_plugin/ppapi_blink_platform_impl.h"
32 #include "content/public/common/content_client.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/pepper_plugin_info.h"
35 #include "content/public/common/sandbox_init.h"
36 #include "content/public/plugin/content_plugin_client.h"
37 #include "ipc/ipc_channel_handle.h"
38 #include "ipc/ipc_platform_file.h"
39 #include "ipc/ipc_sync_channel.h"
40 #include "ipc/ipc_sync_message_filter.h"
41 #include "ppapi/c/dev/ppp_network_state_dev.h"
42 #include "ppapi/c/pp_errors.h"
43 #include "ppapi/c/ppp.h"
44 #include "ppapi/proxy/interface_list.h"
45 #include "ppapi/proxy/plugin_globals.h"
46 #include "ppapi/proxy/plugin_message_filter.h"
47 #include "ppapi/proxy/ppapi_messages.h"
48 #include "ppapi/proxy/resource_reply_thread_registrar.h"
49 #include "third_party/WebKit/public/web/WebKit.h"
50 #include "ui/base/ui_base_switches.h"
53 #include "base/win/win_util.h"
54 #include "base/win/windows_version.h"
55 #include "sandbox/win/src/sandbox.h"
56 #elif defined(OS_MACOSX)
57 #include "content/common/sandbox_init_mac.h"
61 const char kWidevineCdmAdapterFileName
[] = "widevinecdmadapter.dll";
63 extern sandbox::TargetServices
* g_target_services
;
65 // Used by EnumSystemLocales for warming up.
66 static BOOL CALLBACK
EnumLocalesProc(LPTSTR lpLocaleString
) {
70 static BOOL CALLBACK
EnumLocalesProcEx(
71 LPWSTR lpLocaleString
,
77 // Warm up language subsystems before the sandbox is turned on.
78 static void WarmupWindowsLocales(const ppapi::PpapiPermissions
& permissions
) {
79 ::GetUserDefaultLangID();
80 ::GetUserDefaultLCID();
82 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
83 if (base::win::GetVersion() >= base::win::VERSION_VISTA
) {
84 typedef BOOL (WINAPI
*PfnEnumSystemLocalesEx
)
85 (LOCALE_ENUMPROCEX
, DWORD
, LPARAM
, LPVOID
);
87 HMODULE handle_kern32
= GetModuleHandleW(L
"Kernel32.dll");
88 PfnEnumSystemLocalesEx enum_sys_locales_ex
=
89 reinterpret_cast<PfnEnumSystemLocalesEx
>
90 (GetProcAddress(handle_kern32
, "EnumSystemLocalesEx"));
92 enum_sys_locales_ex(EnumLocalesProcEx
, LOCALE_WINDOWS
, 0, 0);
94 EnumSystemLocalesW(EnumLocalesProc
, LCID_INSTALLED
);
103 typedef int32_t (*InitializeBrokerFunc
)
104 (PP_ConnectInstance_Func
* connect_instance_func
);
106 PpapiThread::PpapiThread(const base::CommandLine
& command_line
, bool is_broker
)
107 : is_broker_(is_broker
),
108 plugin_globals_(GetIOTaskRunner()),
109 connect_instance_func_(NULL
),
110 local_pp_module_(base::RandInt(0, std::numeric_limits
<PP_Module
>::max())),
111 next_plugin_dispatcher_id_(1) {
112 plugin_globals_
.SetPluginProxyDelegate(this);
113 plugin_globals_
.set_command_line(
114 command_line
.GetSwitchValueASCII(switches::kPpapiFlashArgs
));
116 blink_platform_impl_
.reset(new PpapiBlinkPlatformImpl
);
117 blink::initializeWithoutV8(blink_platform_impl_
.get());
120 scoped_refptr
<ppapi::proxy::PluginMessageFilter
> plugin_filter(
121 new ppapi::proxy::PluginMessageFilter(
122 NULL
, plugin_globals_
.resource_reply_thread_registrar()));
123 channel()->AddFilter(plugin_filter
.get());
124 plugin_globals_
.RegisterResourceMessageFilters(plugin_filter
.get());
127 // In single process, browser main loop set up the discardable memory
129 if (!command_line
.HasSwitch(switches::kSingleProcess
)) {
130 base::DiscardableMemoryAllocator::SetInstance(
131 ChildThreadImpl::discardable_shared_memory_manager());
135 PpapiThread::~PpapiThread() {
138 void PpapiThread::Shutdown() {
139 ChildThreadImpl::Shutdown();
141 ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate();
142 if (plugin_entry_points_
.shutdown_module
)
143 plugin_entry_points_
.shutdown_module();
144 blink_platform_impl_
->Shutdown();
145 blink::shutdownWithoutV8();
148 bool PpapiThread::Send(IPC::Message
* msg
) {
149 // Allow access from multiple threads.
150 if (base::MessageLoop::current() == message_loop())
151 return ChildThreadImpl::Send(msg
);
153 return sync_message_filter()->Send(msg
);
156 // Note that this function is called only for messages from the channel to the
157 // browser process. Messages from the renderer process are sent via a different
158 // channel that ends up at Dispatcher::OnMessageReceived.
159 bool PpapiThread::OnControlMessageReceived(const IPC::Message
& msg
) {
161 IPC_BEGIN_MESSAGE_MAP(PpapiThread
, msg
)
162 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin
, OnLoadPlugin
)
163 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel
, OnCreateChannel
)
164 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState
, OnSetNetworkState
)
165 IPC_MESSAGE_HANDLER(PpapiMsg_Crash
, OnCrash
)
166 IPC_MESSAGE_HANDLER(PpapiMsg_Hang
, OnHang
)
167 IPC_MESSAGE_UNHANDLED(handled
= false)
168 IPC_END_MESSAGE_MAP()
172 void PpapiThread::OnChannelConnected(int32 peer_pid
) {
173 ChildThreadImpl::OnChannelConnected(peer_pid
);
176 peer_handle_
.Set(::OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, peer_pid
));
180 base::SingleThreadTaskRunner
* PpapiThread::GetIPCTaskRunner() {
181 return ChildProcess::current()->io_task_runner();
184 base::WaitableEvent
* PpapiThread::GetShutdownEvent() {
185 return ChildProcess::current()->GetShutDownEvent();
188 IPC::PlatformFileForTransit
PpapiThread::ShareHandleWithRemote(
189 base::PlatformFile handle
,
190 base::ProcessId peer_pid
,
191 bool should_close_source
) {
193 if (peer_handle_
.IsValid()) {
195 return IPC::GetFileHandleForProcess(handle
, peer_handle_
.Get(),
196 should_close_source
);
200 DCHECK(peer_pid
!= base::kNullProcessId
);
201 return BrokerGetFileHandleForProcess(handle
, peer_pid
, should_close_source
);
204 base::SharedMemoryHandle
PpapiThread::ShareSharedMemoryHandleWithRemote(
205 const base::SharedMemoryHandle
& handle
,
206 base::ProcessId remote_pid
) {
208 if (peer_handle_
.IsValid()) {
210 return IPC::GetFileHandleForProcess(handle
, peer_handle_
.Get(), false);
214 DCHECK(remote_pid
!= base::kNullProcessId
);
215 #if defined(OS_WIN) || defined(OS_MACOSX)
216 base::SharedMemoryHandle duped_handle
;
218 BrokerDuplicateSharedMemoryHandle(handle
, remote_pid
, &duped_handle
);
221 return base::SharedMemory::NULLHandle();
223 return base::SharedMemory::DuplicateHandle(handle
);
224 #endif // defined(OS_WIN) || defined(OS_MACOSX)
227 std::set
<PP_Instance
>* PpapiThread::GetGloballySeenInstanceIDSet() {
228 return &globally_seen_instance_ids_
;
231 IPC::Sender
* PpapiThread::GetBrowserSender() {
235 std::string
PpapiThread::GetUILanguage() {
236 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
237 return command_line
->GetSwitchValueASCII(switches::kLang
);
240 void PpapiThread::PreCacheFontForFlash(const void* logfontw
) {
242 ChildThreadImpl::PreCacheFont(*static_cast<const LOGFONTW
*>(logfontw
));
246 void PpapiThread::SetActiveURL(const std::string
& url
) {
247 GetContentClient()->SetActiveURL(GURL(url
));
250 PP_Resource
PpapiThread::CreateBrowserFont(
251 ppapi::proxy::Connection connection
,
252 PP_Instance instance
,
253 const PP_BrowserFont_Trusted_Description
& desc
,
254 const ppapi::Preferences
& prefs
) {
255 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc
))
257 return (new BrowserFontResource_Trusted(
258 connection
, instance
, desc
, prefs
))->GetReference();
261 uint32
PpapiThread::Register(
262 ppapi::proxy::PluginDispatcher
* plugin_dispatcher
) {
263 if (!plugin_dispatcher
||
264 plugin_dispatchers_
.size() >= std::numeric_limits
<uint32
>::max()) {
270 // Although it is unlikely, make sure that we won't cause any trouble when
271 // the counter overflows.
272 id
= next_plugin_dispatcher_id_
++;
274 plugin_dispatchers_
.find(id
) != plugin_dispatchers_
.end());
275 plugin_dispatchers_
[id
] = plugin_dispatcher
;
279 void PpapiThread::Unregister(uint32 plugin_dispatcher_id
) {
280 plugin_dispatchers_
.erase(plugin_dispatcher_id
);
283 void PpapiThread::OnLoadPlugin(const base::FilePath
& path
,
284 const ppapi::PpapiPermissions
& permissions
) {
285 // In case of crashes, the crash dump doesn't indicate which plugin
287 base::debug::SetCrashKeyValue("ppapi_path", path
.MaybeAsASCII());
289 SavePluginName(path
);
291 // This must be set before calling into the plugin so it can get the
292 // interfaces it has permission for.
293 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions
);
294 permissions_
= permissions
;
296 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
297 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
298 // client) then fetch the entry points from the embedder, rather than a DLL.
299 std::vector
<PepperPluginInfo
> plugins
;
300 GetContentClient()->AddPepperPlugins(&plugins
);
301 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
302 if (plugins
[i
].is_internal
&& plugins
[i
].path
== path
) {
303 // An internal plugin is being loaded, so fetch the entry points.
304 plugin_entry_points_
= plugins
[i
].internal_entry_points
;
308 // If the plugin isn't internal then load it from |path|.
309 base::ScopedNativeLibrary library
;
310 if (plugin_entry_points_
.initialize_module
== NULL
) {
311 // Load the plugin from the specified library.
312 base::NativeLibraryLoadError error
;
313 base::TimeDelta load_time
;
315 TRACE_EVENT1("ppapi", "PpapiThread::LoadPlugin", "path",
316 path
.MaybeAsASCII());
318 base::TimeTicks start
= base::TimeTicks::Now();
319 library
.Reset(base::LoadNativeLibrary(path
, &error
));
320 load_time
= base::TimeTicks::Now() - start
;
323 if (!library
.is_valid()) {
324 LOG(ERROR
) << "Failed to load Pepper module from " << path
.value()
325 << " (error: " << error
.ToString() << ")";
326 if (!base::PathExists(path
)) {
327 ReportLoadResult(path
, FILE_MISSING
);
330 ReportLoadResult(path
, LOAD_FAILED
);
331 // Report detailed reason for load failure.
332 ReportLoadErrorCode(path
, error
);
336 // Only report load time for success loads.
337 ReportLoadTime(path
, load_time
);
339 // Get the GetInterface function (required).
340 plugin_entry_points_
.get_interface
=
341 reinterpret_cast<PP_GetInterface_Func
>(
342 library
.GetFunctionPointer("PPP_GetInterface"));
343 if (!plugin_entry_points_
.get_interface
) {
344 LOG(WARNING
) << "No PPP_GetInterface in plugin library";
345 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
349 // The ShutdownModule/ShutdownBroker function is optional.
350 plugin_entry_points_
.shutdown_module
=
352 reinterpret_cast<PP_ShutdownModule_Func
>(
353 library
.GetFunctionPointer("PPP_ShutdownBroker")) :
354 reinterpret_cast<PP_ShutdownModule_Func
>(
355 library
.GetFunctionPointer("PPP_ShutdownModule"));
358 // Get the InitializeModule function (required for non-broker code).
359 plugin_entry_points_
.initialize_module
=
360 reinterpret_cast<PP_InitializeModule_Func
>(
361 library
.GetFunctionPointer("PPP_InitializeModule"));
362 if (!plugin_entry_points_
.initialize_module
) {
363 LOG(WARNING
) << "No PPP_InitializeModule in plugin library";
364 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
371 // If code subsequently tries to exit using abort(), force a crash (since
372 // otherwise these would be silent terminations and fly under the radar).
373 base::win::SetAbortBehaviorForCrashReporting();
375 // Once we lower the token the sandbox is locked down and no new modules
376 // can be loaded. TODO(cpu): consider changing to the loading style of
378 if (g_target_services
) {
379 // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+.
380 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
) ||
381 path
.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName
) {
382 if (base::win::OSInfo::GetInstance()->version() >=
383 base::win::VERSION_VISTA
) {
384 LoadLibraryA("dxva2.dll");
388 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
389 if (base::win::OSInfo::GetInstance()->version() >=
390 base::win::VERSION_WIN7
) {
392 if (cpu
.vendor_name() == "AuthenticAMD") {
393 // The AMD crypto acceleration is only AMD Bulldozer and above.
395 LoadLibraryA("amdhcp64.dll");
397 LoadLibraryA("amdhcp32.dll");
403 // Cause advapi32 to load before the sandbox is turned on.
404 unsigned int dummy_rand
;
407 WarmupWindowsLocales(permissions
);
409 g_target_services
->LowerToken();
414 // Get the InitializeBroker function (required).
415 InitializeBrokerFunc init_broker
=
416 reinterpret_cast<InitializeBrokerFunc
>(
417 library
.GetFunctionPointer("PPP_InitializeBroker"));
419 LOG(WARNING
) << "No PPP_InitializeBroker in plugin library";
420 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
424 int32_t init_error
= init_broker(&connect_instance_func_
);
425 if (init_error
!= PP_OK
) {
426 LOG(WARNING
) << "InitBroker failed with error " << init_error
;
427 ReportLoadResult(path
, INIT_FAILED
);
430 if (!connect_instance_func_
) {
431 LOG(WARNING
) << "InitBroker did not provide PP_ConnectInstance_Func";
432 ReportLoadResult(path
, INIT_FAILED
);
436 #if defined(OS_MACOSX)
437 // We need to do this after getting |PPP_GetInterface()| (or presumably
438 // doing something nontrivial with the library), else the sandbox
440 CHECK(InitializeSandbox());
443 int32_t init_error
= plugin_entry_points_
.initialize_module(
445 &ppapi::proxy::PluginDispatcher::GetBrowserInterface
);
446 if (init_error
!= PP_OK
) {
447 LOG(WARNING
) << "InitModule failed with error " << init_error
;
448 ReportLoadResult(path
, INIT_FAILED
);
453 // Initialization succeeded, so keep the plugin DLL loaded.
454 library_
.Reset(library
.Release());
456 ReportLoadResult(path
, LOAD_SUCCESS
);
459 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid
,
460 int renderer_child_id
,
462 IPC::ChannelHandle channel_handle
;
464 if (!plugin_entry_points_
.get_interface
|| // Plugin couldn't be loaded.
465 !SetupRendererChannel(renderer_pid
, renderer_child_id
, incognito
,
467 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
471 Send(new PpapiHostMsg_ChannelCreated(channel_handle
));
474 void PpapiThread::OnSetNetworkState(bool online
) {
475 // Note the browser-process side shouldn't send us these messages in the
476 // first unless the plugin has dev permissions, so we don't need to check
477 // again here. We don't want random plugins depending on this dev interface.
478 if (!plugin_entry_points_
.get_interface
)
480 const PPP_NetworkState_Dev
* ns
= static_cast<const PPP_NetworkState_Dev
*>(
481 plugin_entry_points_
.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE
));
483 ns
->SetOnLine(PP_FromBool(online
));
486 void PpapiThread::OnCrash() {
487 // Intentionally crash upon the request of the browser.
489 // Linker's ICF feature may merge this function with other functions with the
490 // same definition and it may confuse the crash report processing system.
491 static int static_variable_to_make_this_function_unique
= 0;
492 base::debug::Alias(&static_variable_to_make_this_function_unique
);
494 volatile int* null_pointer
= nullptr;
498 void PpapiThread::OnHang() {
499 // Intentionally hang upon the request of the browser.
501 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
504 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid
,
505 int renderer_child_id
,
507 IPC::ChannelHandle
* handle
) {
508 DCHECK(is_broker_
== (connect_instance_func_
!= NULL
));
509 IPC::ChannelHandle plugin_handle
;
510 plugin_handle
.name
= IPC::Channel::GenerateVerifiedChannelID(
512 "%d.r%d", base::GetCurrentProcId(), renderer_child_id
));
514 ppapi::proxy::ProxyChannel
* dispatcher
= NULL
;
515 bool init_result
= false;
517 BrokerProcessDispatcher
* broker_dispatcher
=
518 new BrokerProcessDispatcher(plugin_entry_points_
.get_interface
,
519 connect_instance_func_
);
520 init_result
= broker_dispatcher
->InitBrokerWithChannel(this,
524 dispatcher
= broker_dispatcher
;
526 PluginProcessDispatcher
* plugin_dispatcher
=
527 new PluginProcessDispatcher(plugin_entry_points_
.get_interface
,
530 init_result
= plugin_dispatcher
->InitPluginWithChannel(this,
534 dispatcher
= plugin_dispatcher
;
542 handle
->name
= plugin_handle
.name
;
543 #if defined(OS_POSIX)
544 // On POSIX, transfer ownership of the renderer-side (client) FD.
545 // This ensures this process will be notified when it is closed even if a
546 // connection is not established.
547 handle
->socket
= base::FileDescriptor(dispatcher
->TakeRendererFD());
548 if (handle
->socket
.fd
== -1)
552 // From here, the dispatcher will manage its own lifetime according to the
553 // lifetime of the attached channel.
557 void PpapiThread::SavePluginName(const base::FilePath
& path
) {
558 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
559 path
.BaseName().AsUTF8Unsafe());
561 // plugin() is NULL when in-process, which is fine, because this is
562 // just a hook for setting the process name.
563 if (GetContentClient()->plugin()) {
564 GetContentClient()->plugin()->PluginProcessStarted(
565 path
.BaseName().RemoveExtension().LossyDisplayName());
569 static std::string
GetHistogramName(bool is_broker
,
570 const std::string
& metric_name
,
571 const base::FilePath
& path
) {
572 return std::string("Plugin.Ppapi") + (is_broker
? "Broker" : "Plugin") +
573 metric_name
+ "_" + path
.BaseName().MaybeAsASCII();
576 void PpapiThread::ReportLoadResult(const base::FilePath
& path
,
578 DCHECK_LT(result
, LOAD_RESULT_MAX
);
580 // Note: This leaks memory, which is expected behavior.
581 base::HistogramBase
* histogram
=
582 base::LinearHistogram::FactoryGet(
583 GetHistogramName(is_broker_
, "LoadResult", path
),
587 base::HistogramBase::kUmaTargetedHistogramFlag
);
589 histogram
->Add(result
);
592 void PpapiThread::ReportLoadErrorCode(
593 const base::FilePath
& path
,
594 const base::NativeLibraryLoadError
& error
) {
595 // Only report load error code on Windows because that's the only platform that
596 // has a numerical error value.
598 // For sparse histograms, we can use the macro, as it does not incorporate a
600 UMA_HISTOGRAM_SPARSE_SLOWLY(
601 GetHistogramName(is_broker_
, "LoadErrorCode", path
), error
.code
);
605 void PpapiThread::ReportLoadTime(const base::FilePath
& path
,
606 const base::TimeDelta load_time
) {
607 // Note: This leaks memory, which is expected behavior.
608 base::HistogramBase
* histogram
=
609 base::Histogram::FactoryTimeGet(
610 GetHistogramName(is_broker_
, "LoadTime", path
),
611 base::TimeDelta::FromMilliseconds(1),
612 base::TimeDelta::FromSeconds(10),
614 base::HistogramBase::kUmaTargetedHistogramFlag
);
616 histogram
->AddTime(load_time
);
619 } // namespace content