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