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/process_util.h"
14 #include "base/rand_util.h"
15 #include "base/stringprintf.h"
16 #include "base/threading/platform_thread.h"
17 #include "base/time.h"
18 #include "base/utf_string_conversions.h"
19 #include "content/common/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/pepper_plugin_info.h"
27 #include "content/public/common/sandbox_init.h"
28 #include "content/public/plugin/content_plugin_client.h"
29 #include "ipc/ipc_channel_handle.h"
30 #include "ipc/ipc_platform_file.h"
31 #include "ipc/ipc_sync_channel.h"
32 #include "ipc/ipc_sync_message_filter.h"
33 #include "ppapi/c/dev/ppp_network_state_dev.h"
34 #include "ppapi/c/pp_errors.h"
35 #include "ppapi/c/ppp.h"
36 #include "ppapi/proxy/plugin_globals.h"
37 #include "ppapi/proxy/ppapi_messages.h"
38 #include "ppapi/proxy/interface_list.h"
39 #include "ppapi/shared_impl/api_id.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
41 #include "ui/base/ui_base_switches.h"
42 #include "webkit/plugins/plugin_switches.h"
45 #include "base/win/win_util.h"
46 #include "sandbox/win/src/sandbox.h"
47 #elif defined(OS_MACOSX)
48 #include "content/common/sandbox_init_mac.h"
52 extern sandbox::TargetServices
* g_target_services
;
54 extern void* g_target_services
;
59 typedef int32_t (*InitializeBrokerFunc
)
60 (PP_ConnectInstance_Func
* connect_instance_func
);
62 PpapiThread::DispatcherMessageListener::DispatcherMessageListener(
63 PpapiThread
* owner
) : owner_(owner
) {
66 PpapiThread::DispatcherMessageListener::~DispatcherMessageListener() {
69 bool PpapiThread::DispatcherMessageListener::OnMessageReceived(
70 const IPC::Message
& msg
) {
71 // The first parameter should be a plugin dispatcher ID.
72 PickleIterator
iter(msg
);
74 if (!msg
.ReadUInt32(&iter
, &id
)) {
78 std::map
<uint32
, ppapi::proxy::PluginDispatcher
*>::iterator dispatcher
=
79 owner_
->plugin_dispatchers_
.find(id
);
80 if (dispatcher
!= owner_
->plugin_dispatchers_
.end())
81 return dispatcher
->second
->OnMessageReceived(msg
);
86 PpapiThread::PpapiThread(const CommandLine
& command_line
, bool is_broker
)
87 : is_broker_(is_broker
),
88 connect_instance_func_(NULL
),
90 base::RandInt(0, std::numeric_limits
<PP_Module
>::max())),
91 next_plugin_dispatcher_id_(1),
92 dispatcher_message_listener_(this) {
93 ppapi::proxy::PluginGlobals
* globals
= ppapi::proxy::PluginGlobals::Get();
94 globals
->set_plugin_proxy_delegate(this);
95 globals
->set_command_line(
96 command_line
.GetSwitchValueASCII(switches::kPpapiFlashArgs
));
98 webkit_platform_support_
.reset(new PpapiWebKitPlatformSupportImpl
);
99 WebKit::initialize(webkit_platform_support_
.get());
101 // Register interfaces that expect messages from the browser process. Please
102 // note that only those InterfaceProxy-based ones require registration.
103 AddRoute(ppapi::API_ID_PPB_TCPSERVERSOCKET_PRIVATE
,
104 &dispatcher_message_listener_
);
105 AddRoute(ppapi::API_ID_PPB_TCPSOCKET_PRIVATE
,
106 &dispatcher_message_listener_
);
107 AddRoute(ppapi::API_ID_PPB_UDPSOCKET_PRIVATE
,
108 &dispatcher_message_listener_
);
109 AddRoute(ppapi::API_ID_PPB_HOSTRESOLVER_PRIVATE
,
110 &dispatcher_message_listener_
);
111 AddRoute(ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE
,
112 &dispatcher_message_listener_
);
115 PpapiThread::~PpapiThread() {
118 void PpapiThread::Shutdown() {
119 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL
);
120 if (plugin_entry_points_
.shutdown_module
)
121 plugin_entry_points_
.shutdown_module();
125 if (permissions_
.HasPermission(ppapi::PERMISSION_FLASH
))
126 base::win::SetShouldCrashOnProcessDetach(false);
130 bool PpapiThread::Send(IPC::Message
* msg
) {
131 // Allow access from multiple threads.
132 if (base::MessageLoop::current() == message_loop())
133 return ChildThread::Send(msg
);
135 return sync_message_filter()->Send(msg
);
138 // Note that this function is called only for messages from the channel to the
139 // browser process. Messages from the renderer process are sent via a different
140 // channel that ends up at Dispatcher::OnMessageReceived.
141 bool PpapiThread::OnControlMessageReceived(const IPC::Message
& msg
) {
143 IPC_BEGIN_MESSAGE_MAP(PpapiThread
, msg
)
144 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin
, OnLoadPlugin
)
145 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel
, OnCreateChannel
)
146 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState
, OnSetNetworkState
)
147 IPC_MESSAGE_HANDLER(PpapiMsg_Crash
, OnCrash
)
148 IPC_MESSAGE_HANDLER(PpapiMsg_Hang
, OnHang
)
149 IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply
, OnResourceReply
)
150 IPC_MESSAGE_UNHANDLED(handled
= false)
151 IPC_END_MESSAGE_MAP()
155 void PpapiThread::OnChannelConnected(int32 peer_pid
) {
156 ChildThread::OnChannelConnected(peer_pid
);
159 peer_handle_
.Set(::OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, peer_pid
));
163 base::MessageLoopProxy
* PpapiThread::GetIPCMessageLoop() {
164 return ChildProcess::current()->io_message_loop_proxy();
167 base::WaitableEvent
* PpapiThread::GetShutdownEvent() {
168 return ChildProcess::current()->GetShutDownEvent();
171 IPC::PlatformFileForTransit
PpapiThread::ShareHandleWithRemote(
172 base::PlatformFile handle
,
173 base::ProcessId peer_pid
,
174 bool should_close_source
) {
176 if (peer_handle_
.IsValid()) {
178 return IPC::GetFileHandleForProcess(handle
, peer_handle_
,
179 should_close_source
);
183 DCHECK(peer_pid
!= base::kNullProcessId
);
184 return BrokerGetFileHandleForProcess(handle
, peer_pid
, should_close_source
);
187 std::set
<PP_Instance
>* PpapiThread::GetGloballySeenInstanceIDSet() {
188 return &globally_seen_instance_ids_
;
191 IPC::Sender
* PpapiThread::GetBrowserSender() {
195 std::string
PpapiThread::GetUILanguage() {
196 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
197 return command_line
->GetSwitchValueASCII(switches::kLang
);
200 void PpapiThread::PreCacheFont(const void* logfontw
) {
202 Send(new ChildProcessHostMsg_PreCacheFont(
203 *static_cast<const LOGFONTW
*>(logfontw
)));
207 void PpapiThread::SetActiveURL(const std::string
& url
) {
208 GetContentClient()->SetActiveURL(GURL(url
));
211 uint32
PpapiThread::Register(ppapi::proxy::PluginDispatcher
* plugin_dispatcher
) {
212 if (!plugin_dispatcher
||
213 plugin_dispatchers_
.size() >= std::numeric_limits
<uint32
>::max()) {
219 // Although it is unlikely, make sure that we won't cause any trouble when
220 // the counter overflows.
221 id
= next_plugin_dispatcher_id_
++;
223 plugin_dispatchers_
.find(id
) != plugin_dispatchers_
.end());
224 plugin_dispatchers_
[id
] = plugin_dispatcher
;
228 void PpapiThread::Unregister(uint32 plugin_dispatcher_id
) {
229 plugin_dispatchers_
.erase(plugin_dispatcher_id
);
232 void PpapiThread::OnLoadPlugin(const base::FilePath
& path
,
233 const ppapi::PpapiPermissions
& permissions
) {
234 // In case of crashes, the crash dump doesn't indicate which plugin
236 base::debug::SetCrashKeyValue("ppapi_path", path
.MaybeAsASCII());
238 SavePluginName(path
);
240 // This must be set before calling into the plugin so it can get the
241 // interfaces it has permission for.
242 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions
);
243 permissions_
= permissions
;
245 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
246 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
247 // client) then fetch the entry points from the embedder, rather than a DLL.
248 std::vector
<PepperPluginInfo
> plugins
;
249 GetContentClient()->AddPepperPlugins(&plugins
);
250 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
251 if (plugins
[i
].is_internal
&& plugins
[i
].path
== path
) {
252 // An internal plugin is being loaded, so fetch the entry points.
253 plugin_entry_points_
= plugins
[i
].internal_entry_points
;
257 // If the plugin isn't internal then load it from |path|.
258 base::ScopedNativeLibrary library
;
259 if (plugin_entry_points_
.initialize_module
== NULL
) {
260 // Load the plugin from the specified library.
262 library
.Reset(base::LoadNativeLibrary(path
, &error
));
263 if (!library
.is_valid()) {
264 LOG(ERROR
) << "Failed to load Pepper module from "
265 << path
.value() << " (error: " << error
<< ")";
266 ReportLoadResult(path
, LOAD_FAILED
);
270 // Get the GetInterface function (required).
271 plugin_entry_points_
.get_interface
=
272 reinterpret_cast<PP_GetInterface_Func
>(
273 library
.GetFunctionPointer("PPP_GetInterface"));
274 if (!plugin_entry_points_
.get_interface
) {
275 LOG(WARNING
) << "No PPP_GetInterface in plugin library";
276 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
280 // The ShutdownModule/ShutdownBroker function is optional.
281 plugin_entry_points_
.shutdown_module
=
283 reinterpret_cast<PP_ShutdownModule_Func
>(
284 library
.GetFunctionPointer("PPP_ShutdownBroker")) :
285 reinterpret_cast<PP_ShutdownModule_Func
>(
286 library
.GetFunctionPointer("PPP_ShutdownModule"));
289 // Get the InitializeModule function (required for non-broker code).
290 plugin_entry_points_
.initialize_module
=
291 reinterpret_cast<PP_InitializeModule_Func
>(
292 library
.GetFunctionPointer("PPP_InitializeModule"));
293 if (!plugin_entry_points_
.initialize_module
) {
294 LOG(WARNING
) << "No PPP_InitializeModule in plugin library";
295 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
302 // If code subsequently tries to exit using abort(), force a crash (since
303 // otherwise these would be silent terminations and fly under the radar).
304 base::win::SetAbortBehaviorForCrashReporting();
305 if (permissions
.HasPermission(ppapi::PERMISSION_FLASH
)) {
306 // Force a crash for exit(), _exit(), or ExitProcess(), but only do that for
308 base::win::SetShouldCrashOnProcessDetach(true);
311 // Once we lower the token the sandbox is locked down and no new modules
312 // can be loaded. TODO(cpu): consider changing to the loading style of
314 if (g_target_services
) {
315 // Cause advapi32 to load before the sandbox is turned on.
316 unsigned int dummy_rand
;
318 // Warm up language subsystems before the sandbox is turned on.
319 ::GetUserDefaultLangID();
320 ::GetUserDefaultLCID();
322 g_target_services
->LowerToken();
327 // Get the InitializeBroker function (required).
328 InitializeBrokerFunc init_broker
=
329 reinterpret_cast<InitializeBrokerFunc
>(
330 library
.GetFunctionPointer("PPP_InitializeBroker"));
332 LOG(WARNING
) << "No PPP_InitializeBroker in plugin library";
333 ReportLoadResult(path
, ENTRY_POINT_MISSING
);
337 int32_t init_error
= init_broker(&connect_instance_func_
);
338 if (init_error
!= PP_OK
) {
339 LOG(WARNING
) << "InitBroker failed with error " << init_error
;
340 ReportLoadResult(path
, INIT_FAILED
);
343 if (!connect_instance_func_
) {
344 LOG(WARNING
) << "InitBroker did not provide PP_ConnectInstance_Func";
345 ReportLoadResult(path
, INIT_FAILED
);
349 #if defined(OS_MACOSX)
350 // We need to do this after getting |PPP_GetInterface()| (or presumably
351 // doing something nontrivial with the library), else the sandbox
353 CHECK(InitializeSandbox());
356 int32_t init_error
= plugin_entry_points_
.initialize_module(
358 &ppapi::proxy::PluginDispatcher::GetBrowserInterface
);
359 if (init_error
!= PP_OK
) {
360 LOG(WARNING
) << "InitModule failed with error " << init_error
;
361 ReportLoadResult(path
, INIT_FAILED
);
366 // Initialization succeeded, so keep the plugin DLL loaded.
367 library_
.Reset(library
.Release());
369 ReportLoadResult(path
, LOAD_SUCCESS
);
372 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid
,
373 int renderer_child_id
,
375 IPC::ChannelHandle channel_handle
;
377 if (!plugin_entry_points_
.get_interface
|| // Plugin couldn't be loaded.
378 !SetupRendererChannel(renderer_pid
, renderer_child_id
, incognito
,
380 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
384 Send(new PpapiHostMsg_ChannelCreated(channel_handle
));
387 void PpapiThread::OnResourceReply(
388 const ppapi::proxy::ResourceMessageReplyParams
& reply_params
,
389 const IPC::Message
& nested_msg
) {
390 ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params
,
394 void PpapiThread::OnSetNetworkState(bool online
) {
395 // Note the browser-process side shouldn't send us these messages in the
396 // first unless the plugin has dev permissions, so we don't need to check
397 // again here. We don't want random plugins depending on this dev interface.
398 if (!plugin_entry_points_
.get_interface
)
400 const PPP_NetworkState_Dev
* ns
= static_cast<const PPP_NetworkState_Dev
*>(
401 plugin_entry_points_
.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE
));
403 ns
->SetOnLine(PP_FromBool(online
));
406 void PpapiThread::OnCrash() {
407 // Intentionally crash upon the request of the browser.
408 volatile int* null_pointer
= NULL
;
412 void PpapiThread::OnHang() {
413 // Intentionally hang upon the request of the browser.
415 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
418 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid
,
419 int renderer_child_id
,
421 IPC::ChannelHandle
* handle
) {
422 DCHECK(is_broker_
== (connect_instance_func_
!= NULL
));
423 IPC::ChannelHandle plugin_handle
;
424 plugin_handle
.name
= IPC::Channel::GenerateVerifiedChannelID(
426 "%d.r%d", base::GetCurrentProcId(), renderer_child_id
));
428 ppapi::proxy::ProxyChannel
* dispatcher
= NULL
;
429 bool init_result
= false;
431 BrokerProcessDispatcher
* broker_dispatcher
=
432 new BrokerProcessDispatcher(plugin_entry_points_
.get_interface
,
433 connect_instance_func_
);
434 init_result
= broker_dispatcher
->InitBrokerWithChannel(this,
438 dispatcher
= broker_dispatcher
;
440 PluginProcessDispatcher
* plugin_dispatcher
=
441 new PluginProcessDispatcher(plugin_entry_points_
.get_interface
,
444 init_result
= plugin_dispatcher
->InitPluginWithChannel(this,
448 dispatcher
= plugin_dispatcher
;
456 handle
->name
= plugin_handle
.name
;
457 #if defined(OS_POSIX)
458 // On POSIX, transfer ownership of the renderer-side (client) FD.
459 // This ensures this process will be notified when it is closed even if a
460 // connection is not established.
461 handle
->socket
= base::FileDescriptor(dispatcher
->TakeRendererFD(), true);
462 if (handle
->socket
.fd
== -1)
466 // From here, the dispatcher will manage its own lifetime according to the
467 // lifetime of the attached channel.
471 void PpapiThread::SavePluginName(const base::FilePath
& path
) {
472 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
473 path
.BaseName().AsUTF8Unsafe());
475 // plugin() is NULL when in-process, which is fine, because this is
476 // just a hook for setting the process name.
477 if (GetContentClient()->plugin()) {
478 GetContentClient()->plugin()->PluginProcessStarted(
479 path
.BaseName().RemoveExtension().LossyDisplayName());
483 void PpapiThread::ReportLoadResult(const base::FilePath
& path
,
485 DCHECK_LT(result
, LOAD_RESULT_MAX
);
487 std::ostringstream histogram_name
;
488 histogram_name
<< "Plugin.Ppapi" << (is_broker_
? "Broker" : "Plugin")
489 << "LoadResult_" << path
.BaseName().MaybeAsASCII();
491 // Note: This leaks memory, which is expected behavior.
492 base::HistogramBase
* histogram
=
493 base::LinearHistogram::FactoryGet(
494 histogram_name
.str(),
498 base::HistogramBase::kUmaTargetedHistogramFlag
);
500 histogram
->Add(result
);
503 } // namespace content