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"
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 "ppapi/shared_impl/api_id.h"
41 #include "third_party/WebKit/public/web/WebKit.h"
42 #include "ui/base/ui_base_switches.h"
45 #include "base/win/win_util.h"
46 #include "base/win/windows_version.h"
47 #include "sandbox/win/src/sandbox.h"
48 #elif defined(OS_MACOSX)
49 #include "content/common/sandbox_init_mac.h"
53 extern sandbox::TargetServices
* g_target_services
;
55 extern void* g_target_services
;
60 typedef int32_t (*InitializeBrokerFunc
)
61 (PP_ConnectInstance_Func
* connect_instance_func
);
63 PpapiThread::DispatcherMessageListener::DispatcherMessageListener(
64 PpapiThread
* owner
) : owner_(owner
) {
67 PpapiThread::DispatcherMessageListener::~DispatcherMessageListener() {
70 bool PpapiThread::DispatcherMessageListener::OnMessageReceived(
71 const IPC::Message
& msg
) {
72 // The first parameter should be a plugin dispatcher ID.
73 PickleIterator
iter(msg
);
75 if (!msg
.ReadUInt32(&iter
, &id
)) {
79 std::map
<uint32
, ppapi::proxy::PluginDispatcher
*>::iterator dispatcher
=
80 owner_
->plugin_dispatchers_
.find(id
);
81 if (dispatcher
!= owner_
->plugin_dispatchers_
.end())
82 return dispatcher
->second
->OnMessageReceived(msg
);
87 PpapiThread::PpapiThread(const CommandLine
& command_line
, bool is_broker
)
88 : is_broker_(is_broker
),
89 connect_instance_func_(NULL
),
91 base::RandInt(0, std::numeric_limits
<PP_Module
>::max())),
92 next_plugin_dispatcher_id_(1),
93 dispatcher_message_listener_(this) {
94 ppapi::proxy::PluginGlobals
* globals
= ppapi::proxy::PluginGlobals::Get();
95 globals
->set_plugin_proxy_delegate(this);
96 globals
->set_command_line(
97 command_line
.GetSwitchValueASCII(switches::kPpapiFlashArgs
));
99 webkit_platform_support_
.reset(new PpapiWebKitPlatformSupportImpl
);
100 WebKit::initialize(webkit_platform_support_
.get());
102 // Register interfaces that expect messages from the browser process. Please
103 // note that only those InterfaceProxy-based ones require registration.
104 AddRoute(ppapi::API_ID_PPB_HOSTRESOLVER_PRIVATE
,
105 &dispatcher_message_listener_
);
106 AddRoute(ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE
,
107 &dispatcher_message_listener_
);
110 PpapiThread::~PpapiThread() {
113 void PpapiThread::Shutdown() {
114 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL
);
115 if (plugin_entry_points_
.shutdown_module
)
116 plugin_entry_points_
.shutdown_module();
120 if (permissions_
.HasPermission(ppapi::PERMISSION_FLASH
))
121 base::win::SetShouldCrashOnProcessDetach(false);
125 bool PpapiThread::Send(IPC::Message
* msg
) {
126 // Allow access from multiple threads.
127 if (base::MessageLoop::current() == message_loop())
128 return ChildThread::Send(msg
);
130 return sync_message_filter()->Send(msg
);
133 // Note that this function is called only for messages from the channel to the
134 // browser process. Messages from the renderer process are sent via a different
135 // channel that ends up at Dispatcher::OnMessageReceived.
136 bool PpapiThread::OnControlMessageReceived(const IPC::Message
& msg
) {
138 IPC_BEGIN_MESSAGE_MAP(PpapiThread
, msg
)
139 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin
, OnLoadPlugin
)
140 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel
, OnCreateChannel
)
141 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState
, OnSetNetworkState
)
142 IPC_MESSAGE_HANDLER(PpapiMsg_Crash
, OnCrash
)
143 IPC_MESSAGE_HANDLER(PpapiMsg_Hang
, OnHang
)
144 IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply
, OnResourceReply
)
145 IPC_MESSAGE_UNHANDLED(handled
= false)
146 IPC_END_MESSAGE_MAP()
150 void PpapiThread::OnChannelConnected(int32 peer_pid
) {
151 ChildThread::OnChannelConnected(peer_pid
);
154 peer_handle_
.Set(::OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, peer_pid
));
158 base::MessageLoopProxy
* PpapiThread::GetIPCMessageLoop() {
159 return ChildProcess::current()->io_message_loop_proxy();
162 base::WaitableEvent
* PpapiThread::GetShutdownEvent() {
163 return ChildProcess::current()->GetShutDownEvent();
166 IPC::PlatformFileForTransit
PpapiThread::ShareHandleWithRemote(
167 base::PlatformFile handle
,
168 base::ProcessId peer_pid
,
169 bool should_close_source
) {
171 if (peer_handle_
.IsValid()) {
173 return IPC::GetFileHandleForProcess(handle
, peer_handle_
,
174 should_close_source
);
178 DCHECK(peer_pid
!= base::kNullProcessId
);
179 return BrokerGetFileHandleForProcess(handle
, peer_pid
, should_close_source
);
182 std::set
<PP_Instance
>* PpapiThread::GetGloballySeenInstanceIDSet() {
183 return &globally_seen_instance_ids_
;
186 IPC::Sender
* PpapiThread::GetBrowserSender() {
190 std::string
PpapiThread::GetUILanguage() {
191 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
192 return command_line
->GetSwitchValueASCII(switches::kLang
);
195 void PpapiThread::PreCacheFont(const void* logfontw
) {
197 Send(new ChildProcessHostMsg_PreCacheFont(
198 *static_cast<const LOGFONTW
*>(logfontw
)));
202 void PpapiThread::SetActiveURL(const std::string
& url
) {
203 GetContentClient()->SetActiveURL(GURL(url
));
206 PP_Resource
PpapiThread::CreateBrowserFont(
207 ppapi::proxy::Connection connection
,
208 PP_Instance instance
,
209 const PP_BrowserFont_Trusted_Description
& desc
,
210 const ppapi::Preferences
& prefs
) {
211 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc
))
213 return (new BrowserFontResource_Trusted(
214 connection
, instance
, desc
, prefs
))->GetReference();
217 uint32
PpapiThread::Register(ppapi::proxy::PluginDispatcher
* plugin_dispatcher
) {
218 if (!plugin_dispatcher
||
219 plugin_dispatchers_
.size() >= std::numeric_limits
<uint32
>::max()) {
225 // Although it is unlikely, make sure that we won't cause any trouble when
226 // the counter overflows.
227 id
= next_plugin_dispatcher_id_
++;
229 plugin_dispatchers_
.find(id
) != plugin_dispatchers_
.end());
230 plugin_dispatchers_
[id
] = plugin_dispatcher
;
234 void PpapiThread::Unregister(uint32 plugin_dispatcher_id
) {
235 plugin_dispatchers_
.erase(plugin_dispatcher_id
);
238 void PpapiThread::OnLoadPlugin(const base::FilePath
& path
,
239 const ppapi::PpapiPermissions
& permissions
) {
240 // In case of crashes, the crash dump doesn't indicate which plugin
242 base::debug::SetCrashKeyValue("ppapi_path", path
.MaybeAsASCII());
244 SavePluginName(path
);
246 // This must be set before calling into the plugin so it can get the
247 // interfaces it has permission for.
248 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions
);
249 permissions_
= permissions
;
251 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
252 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
253 // client) then fetch the entry points from the embedder, rather than a DLL.
254 std::vector
<PepperPluginInfo
> plugins
;
255 GetContentClient()->AddPepperPlugins(&plugins
);
256 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
257 if (plugins
[i
].is_internal
&& plugins
[i
].path
== path
) {
258 // An internal plugin is being loaded, so fetch the entry points.
259 plugin_entry_points_
= plugins
[i
].internal_entry_points
;
263 // If the plugin isn't internal then load it from |path|.
264 base::ScopedNativeLibrary library
;
265 if (plugin_entry_points_
.initialize_module
== NULL
) {
266 // Load the plugin from the specified library.
268 library
.Reset(base::LoadNativeLibrary(path
, &error
));
269 if (!library
.is_valid()) {
270 LOG(ERROR
) << "Failed to load Pepper module from "
271 << path
.value() << " (error: " << error
<< ")";
272 ReportLoadResult(path
, LOAD_FAILED
);
276 // Get the GetInterface function (required).
277 plugin_entry_points_
.get_interface
=
278 reinterpret_cast<PP_GetInterface_Func
>(
279 library
.GetFunctionPointer("PPP_GetInterface"));
280 if (!plugin_entry_points_
.get_interface
) {
281 LOG(WARNING
) << "No PPP_GetInterface in plugin library";
282 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
286 // The ShutdownModule/ShutdownBroker function is optional.
287 plugin_entry_points_
.shutdown_module
=
289 reinterpret_cast<PP_ShutdownModule_Func
>(
290 library
.GetFunctionPointer("PPP_ShutdownBroker")) :
291 reinterpret_cast<PP_ShutdownModule_Func
>(
292 library
.GetFunctionPointer("PPP_ShutdownModule"));
295 // Get the InitializeModule function (required for non-broker code).
296 plugin_entry_points_
.initialize_module
=
297 reinterpret_cast<PP_InitializeModule_Func
>(
298 library
.GetFunctionPointer("PPP_InitializeModule"));
299 if (!plugin_entry_points_
.initialize_module
) {
300 LOG(WARNING
) << "No PPP_InitializeModule in plugin library";
301 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
308 // If code subsequently tries to exit using abort(), force a crash (since
309 // otherwise these would be silent terminations and fly under the radar).
310 base::win::SetAbortBehaviorForCrashReporting();
311 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
312 // Force a crash for exit(), _exit(), or ExitProcess(), but only do that for
314 base::win::SetShouldCrashOnProcessDetach(true);
317 // Once we lower the token the sandbox is locked down and no new modules
318 // can be loaded. TODO(cpu): consider changing to the loading style of
320 if (g_target_services
) {
321 // Let Flash load DRM before lockdown on Vista+.
322 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
) &&
323 base::win::OSInfo::GetInstance()->version() >=
324 base::win::VERSION_VISTA
) {
325 LoadLibrary(L
"dxva2.dll");
328 // Cause advapi32 to load before the sandbox is turned on.
329 unsigned int dummy_rand
;
331 // Warm up language subsystems before the sandbox is turned on.
332 ::GetUserDefaultLangID();
333 ::GetUserDefaultLCID();
335 g_target_services
->LowerToken();
340 // Get the InitializeBroker function (required).
341 InitializeBrokerFunc init_broker
=
342 reinterpret_cast<InitializeBrokerFunc
>(
343 library
.GetFunctionPointer("PPP_InitializeBroker"));
345 LOG(WARNING
) << "No PPP_InitializeBroker in plugin library";
346 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
350 int32_t init_error
= init_broker(&connect_instance_func_
);
351 if (init_error
!= PP_OK
) {
352 LOG(WARNING
) << "InitBroker failed with error " << init_error
;
353 ReportLoadResult(path
, INIT_FAILED
);
356 if (!connect_instance_func_
) {
357 LOG(WARNING
) << "InitBroker did not provide PP_ConnectInstance_Func";
358 ReportLoadResult(path
, INIT_FAILED
);
362 #if defined(OS_MACOSX)
363 // We need to do this after getting |PPP_GetInterface()| (or presumably
364 // doing something nontrivial with the library), else the sandbox
366 CHECK(InitializeSandbox());
369 int32_t init_error
= plugin_entry_points_
.initialize_module(
371 &ppapi::proxy::PluginDispatcher::GetBrowserInterface
);
372 if (init_error
!= PP_OK
) {
373 LOG(WARNING
) << "InitModule failed with error " << init_error
;
374 ReportLoadResult(path
, INIT_FAILED
);
379 // Initialization succeeded, so keep the plugin DLL loaded.
380 library_
.Reset(library
.Release());
382 ReportLoadResult(path
, LOAD_SUCCESS
);
385 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid
,
386 int renderer_child_id
,
388 IPC::ChannelHandle channel_handle
;
390 if (!plugin_entry_points_
.get_interface
|| // Plugin couldn't be loaded.
391 !SetupRendererChannel(renderer_pid
, renderer_child_id
, incognito
,
393 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
397 Send(new PpapiHostMsg_ChannelCreated(channel_handle
));
400 void PpapiThread::OnResourceReply(
401 const ppapi::proxy::ResourceMessageReplyParams
& reply_params
,
402 const IPC::Message
& nested_msg
) {
403 ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params
,
407 void PpapiThread::OnSetNetworkState(bool online
) {
408 // Note the browser-process side shouldn't send us these messages in the
409 // first unless the plugin has dev permissions, so we don't need to check
410 // again here. We don't want random plugins depending on this dev interface.
411 if (!plugin_entry_points_
.get_interface
)
413 const PPP_NetworkState_Dev
* ns
= static_cast<const PPP_NetworkState_Dev
*>(
414 plugin_entry_points_
.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE
));
416 ns
->SetOnLine(PP_FromBool(online
));
419 void PpapiThread::OnCrash() {
420 // Intentionally crash upon the request of the browser.
421 volatile int* null_pointer
= NULL
;
425 void PpapiThread::OnHang() {
426 // Intentionally hang upon the request of the browser.
428 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
431 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid
,
432 int renderer_child_id
,
434 IPC::ChannelHandle
* handle
) {
435 DCHECK(is_broker_
== (connect_instance_func_
!= NULL
));
436 IPC::ChannelHandle plugin_handle
;
437 plugin_handle
.name
= IPC::Channel::GenerateVerifiedChannelID(
439 "%d.r%d", base::GetCurrentProcId(), renderer_child_id
));
441 ppapi::proxy::ProxyChannel
* dispatcher
= NULL
;
442 bool init_result
= false;
444 BrokerProcessDispatcher
* broker_dispatcher
=
445 new BrokerProcessDispatcher(plugin_entry_points_
.get_interface
,
446 connect_instance_func_
);
447 init_result
= broker_dispatcher
->InitBrokerWithChannel(this,
451 dispatcher
= broker_dispatcher
;
453 PluginProcessDispatcher
* plugin_dispatcher
=
454 new PluginProcessDispatcher(plugin_entry_points_
.get_interface
,
457 init_result
= plugin_dispatcher
->InitPluginWithChannel(this,
461 dispatcher
= plugin_dispatcher
;
469 handle
->name
= plugin_handle
.name
;
470 #if defined(OS_POSIX)
471 // On POSIX, transfer ownership of the renderer-side (client) FD.
472 // This ensures this process will be notified when it is closed even if a
473 // connection is not established.
474 handle
->socket
= base::FileDescriptor(dispatcher
->TakeRendererFD(), true);
475 if (handle
->socket
.fd
== -1)
479 // From here, the dispatcher will manage its own lifetime according to the
480 // lifetime of the attached channel.
484 void PpapiThread::SavePluginName(const base::FilePath
& path
) {
485 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
486 path
.BaseName().AsUTF8Unsafe());
488 // plugin() is NULL when in-process, which is fine, because this is
489 // just a hook for setting the process name.
490 if (GetContentClient()->plugin()) {
491 GetContentClient()->plugin()->PluginProcessStarted(
492 path
.BaseName().RemoveExtension().LossyDisplayName());
496 void PpapiThread::ReportLoadResult(const base::FilePath
& path
,
498 DCHECK_LT(result
, LOAD_RESULT_MAX
);
500 std::ostringstream histogram_name
;
501 histogram_name
<< "Plugin.Ppapi" << (is_broker_
? "Broker" : "Plugin")
502 << "LoadResult_" << path
.BaseName().MaybeAsASCII();
504 // Note: This leaks memory, which is expected behavior.
505 base::HistogramBase
* histogram
=
506 base::LinearHistogram::FactoryGet(
507 histogram_name
.str(),
511 base::HistogramBase::kUmaTargetedHistogramFlag
);
513 histogram
->Add(result
);
516 } // namespace content