ppapi_thread.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/cpu.h"
11#include "base/debug/crash_logging.h"
12#include "base/logging.h"
13#include "base/metrics/histogram.h"
14#include "base/rand_util.h"
15#include "base/strings/stringprintf.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/threading/platform_thread.h"
18#include "base/time/time.h"
19#include "content/child/browser_font_resource_trusted.h"
20#include "content/child/child_process.h"
21#include "content/common/child_process_messages.h"
22#include "content/common/sandbox_util.h"
23#include "content/ppapi_plugin/broker_process_dispatcher.h"
24#include "content/ppapi_plugin/plugin_process_dispatcher.h"
25#include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"
26#include "content/public/common/content_client.h"
27#include "content/public/common/content_switches.h"
28#include "content/public/common/pepper_plugin_info.h"
29#include "content/public/common/sandbox_init.h"
30#include "content/public/plugin/content_plugin_client.h"
31#include "ipc/ipc_channel_handle.h"
32#include "ipc/ipc_platform_file.h"
33#include "ipc/ipc_sync_channel.h"
34#include "ipc/ipc_sync_message_filter.h"
35#include "ppapi/c/dev/ppp_network_state_dev.h"
36#include "ppapi/c/pp_errors.h"
37#include "ppapi/c/ppp.h"
38#include "ppapi/proxy/interface_list.h"
39#include "ppapi/proxy/plugin_globals.h"
40#include "ppapi/proxy/plugin_message_filter.h"
41#include "ppapi/proxy/ppapi_messages.h"
42#include "ppapi/proxy/resource_reply_thread_registrar.h"
43#include "third_party/WebKit/public/web/WebKit.h"
44#include "ui/base/ui_base_switches.h"
45
46#if defined(OS_WIN)
47#include "base/win/win_util.h"
48#include "base/win/windows_version.h"
49#include "sandbox/win/src/sandbox.h"
50#elif defined(OS_MACOSX)
51#include "content/common/sandbox_init_mac.h"
52#endif
53
54#if defined(OS_WIN)
55extern sandbox::TargetServices* g_target_services;
56
57// Used by EnumSystemLocales for warming up.
58static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
59  return TRUE;
60}
61#else
62extern void* g_target_services;
63#endif
64
65namespace content {
66
67typedef int32_t (*InitializeBrokerFunc)
68    (PP_ConnectInstance_Func* connect_instance_func);
69
70PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
71    : is_broker_(is_broker),
72      connect_instance_func_(NULL),
73      local_pp_module_(
74          base::RandInt(0, std::numeric_limits<PP_Module>::max())),
75      next_plugin_dispatcher_id_(1) {
76  ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
77  globals->set_plugin_proxy_delegate(this);
78  globals->set_command_line(
79      command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
80
81  webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
82  blink::initialize(webkit_platform_support_.get());
83
84  if (!is_broker_) {
85    channel()->AddFilter(
86        new ppapi::proxy::PluginMessageFilter(
87            NULL, globals->resource_reply_thread_registrar()));
88  }
89}
90
91PpapiThread::~PpapiThread() {
92}
93
94void PpapiThread::Shutdown() {
95  ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
96  if (plugin_entry_points_.shutdown_module)
97    plugin_entry_points_.shutdown_module();
98  webkit_platform_support_->Shutdown();
99  blink::shutdown();
100}
101
102bool PpapiThread::Send(IPC::Message* msg) {
103  // Allow access from multiple threads.
104  if (base::MessageLoop::current() == message_loop())
105    return ChildThread::Send(msg);
106
107  return sync_message_filter()->Send(msg);
108}
109
110// Note that this function is called only for messages from the channel to the
111// browser process. Messages from the renderer process are sent via a different
112// channel that ends up at Dispatcher::OnMessageReceived.
113bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
114  bool handled = true;
115  IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
116    IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
117    IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
118    IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
119    IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
120    IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
121    IPC_MESSAGE_UNHANDLED(handled = false)
122  IPC_END_MESSAGE_MAP()
123  return handled;
124}
125
126void PpapiThread::OnChannelConnected(int32 peer_pid) {
127  ChildThread::OnChannelConnected(peer_pid);
128#if defined(OS_WIN)
129  if (is_broker_)
130    peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
131#endif
132}
133
134base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
135  return ChildProcess::current()->io_message_loop_proxy();
136}
137
138base::WaitableEvent* PpapiThread::GetShutdownEvent() {
139  return ChildProcess::current()->GetShutDownEvent();
140}
141
142IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
143    base::PlatformFile handle,
144    base::ProcessId peer_pid,
145    bool should_close_source) {
146#if defined(OS_WIN)
147  if (peer_handle_.IsValid()) {
148    DCHECK(is_broker_);
149    return IPC::GetFileHandleForProcess(handle, peer_handle_,
150                                        should_close_source);
151  }
152#endif
153
154  DCHECK(peer_pid != base::kNullProcessId);
155  return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
156}
157
158std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
159  return &globally_seen_instance_ids_;
160}
161
162IPC::Sender* PpapiThread::GetBrowserSender() {
163  return this;
164}
165
166std::string PpapiThread::GetUILanguage() {
167  CommandLine* command_line = CommandLine::ForCurrentProcess();
168  return command_line->GetSwitchValueASCII(switches::kLang);
169}
170
171void PpapiThread::PreCacheFont(const void* logfontw) {
172#if defined(OS_WIN)
173  Send(new ChildProcessHostMsg_PreCacheFont(
174      *static_cast<const LOGFONTW*>(logfontw)));
175#endif
176}
177
178void PpapiThread::SetActiveURL(const std::string& url) {
179  GetContentClient()->SetActiveURL(GURL(url));
180}
181
182PP_Resource PpapiThread::CreateBrowserFont(
183    ppapi::proxy::Connection connection,
184    PP_Instance instance,
185    const PP_BrowserFont_Trusted_Description& desc,
186    const ppapi::Preferences& prefs) {
187  if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
188    return 0;
189  return (new BrowserFontResource_Trusted(
190        connection, instance, desc, prefs))->GetReference();
191}
192
193uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
194  if (!plugin_dispatcher ||
195      plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
196    return 0;
197  }
198
199  uint32 id = 0;
200  do {
201    // Although it is unlikely, make sure that we won't cause any trouble when
202    // the counter overflows.
203    id = next_plugin_dispatcher_id_++;
204  } while (id == 0 ||
205           plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
206  plugin_dispatchers_[id] = plugin_dispatcher;
207  return id;
208}
209
210void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
211  plugin_dispatchers_.erase(plugin_dispatcher_id);
212}
213
214void PpapiThread::OnLoadPlugin(const base::FilePath& path,
215                               const ppapi::PpapiPermissions& permissions) {
216  // In case of crashes, the crash dump doesn't indicate which plugin
217  // it came from.
218  base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
219
220  SavePluginName(path);
221
222  // This must be set before calling into the plugin so it can get the
223  // interfaces it has permission for.
224  ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
225  permissions_ = permissions;
226
227  // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
228  // binary.  If we're being asked to load such a plugin (e.g. the Chromoting
229  // client) then fetch the entry points from the embedder, rather than a DLL.
230  std::vector<PepperPluginInfo> plugins;
231  GetContentClient()->AddPepperPlugins(&plugins);
232  for (size_t i = 0; i < plugins.size(); ++i) {
233    if (plugins[i].is_internal && plugins[i].path == path) {
234      // An internal plugin is being loaded, so fetch the entry points.
235      plugin_entry_points_ = plugins[i].internal_entry_points;
236    }
237  }
238
239  // If the plugin isn't internal then load it from |path|.
240  base::ScopedNativeLibrary library;
241  if (plugin_entry_points_.initialize_module == NULL) {
242    // Load the plugin from the specified library.
243    std::string error;
244    library.Reset(base::LoadNativeLibrary(path, &error));
245    if (!library.is_valid()) {
246      LOG(ERROR) << "Failed to load Pepper module from "
247        << path.value() << " (error: " << error << ")";
248      ReportLoadResult(path, LOAD_FAILED);
249      return;
250    }
251
252    // Get the GetInterface function (required).
253    plugin_entry_points_.get_interface =
254        reinterpret_cast<PP_GetInterface_Func>(
255            library.GetFunctionPointer("PPP_GetInterface"));
256    if (!plugin_entry_points_.get_interface) {
257      LOG(WARNING) << "No PPP_GetInterface in plugin library";
258      ReportLoadResult(path, ENTRY_POINT_MISSING);
259      return;
260    }
261
262    // The ShutdownModule/ShutdownBroker function is optional.
263    plugin_entry_points_.shutdown_module =
264        is_broker_ ?
265        reinterpret_cast<PP_ShutdownModule_Func>(
266            library.GetFunctionPointer("PPP_ShutdownBroker")) :
267        reinterpret_cast<PP_ShutdownModule_Func>(
268            library.GetFunctionPointer("PPP_ShutdownModule"));
269
270    if (!is_broker_) {
271      // Get the InitializeModule function (required for non-broker code).
272      plugin_entry_points_.initialize_module =
273          reinterpret_cast<PP_InitializeModule_Func>(
274              library.GetFunctionPointer("PPP_InitializeModule"));
275      if (!plugin_entry_points_.initialize_module) {
276        LOG(WARNING) << "No PPP_InitializeModule in plugin library";
277        ReportLoadResult(path, ENTRY_POINT_MISSING);
278        return;
279      }
280    }
281  }
282
283#if defined(OS_WIN)
284  // If code subsequently tries to exit using abort(), force a crash (since
285  // otherwise these would be silent terminations and fly under the radar).
286  base::win::SetAbortBehaviorForCrashReporting();
287
288  // Once we lower the token the sandbox is locked down and no new modules
289  // can be loaded. TODO(cpu): consider changing to the loading style of
290  // regular plugins.
291  if (g_target_services) {
292    // Let Flash load DXVA before lockdown on Vista+.
293    if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
294      if (base::win::OSInfo::GetInstance()->version() >=
295          base::win::VERSION_VISTA) {
296        LoadLibraryA("dxva2.dll");
297      }
298
299      if (base::win::OSInfo::GetInstance()->version() >=
300          base::win::VERSION_WIN7) {
301        base::CPU cpu;
302        if ((cpu.vendor_name() == "AuthenticAMD") && (cpu.family() > 0x14)) {
303          // The AMD crypto acceleration is only AMD Bulldozer and above.
304#if defined(_WIN64)
305          LoadLibraryA("amdhcp64.dll");
306#else
307          LoadLibraryA("amdhcp32.dll");
308#endif
309        }
310      }
311    }
312
313    // Cause advapi32 to load before the sandbox is turned on.
314    unsigned int dummy_rand;
315    rand_s(&dummy_rand);
316    // Warm up language subsystems before the sandbox is turned on.
317    ::GetUserDefaultLangID();
318    ::GetUserDefaultLCID();
319
320    if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
321      // Warm up system locales.
322      EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
323    }
324    // Engage the sandbox.
325    g_target_services->LowerToken();
326  }
327#endif
328
329  if (is_broker_) {
330    // Get the InitializeBroker function (required).
331    InitializeBrokerFunc init_broker =
332        reinterpret_cast<InitializeBrokerFunc>(
333            library.GetFunctionPointer("PPP_InitializeBroker"));
334    if (!init_broker) {
335      LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
336      ReportLoadResult(path, ENTRY_POINT_MISSING);
337      return;
338    }
339
340    int32_t init_error = init_broker(&connect_instance_func_);
341    if (init_error != PP_OK) {
342      LOG(WARNING) << "InitBroker failed with error " << init_error;
343      ReportLoadResult(path, INIT_FAILED);
344      return;
345    }
346    if (!connect_instance_func_) {
347      LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
348      ReportLoadResult(path, INIT_FAILED);
349      return;
350    }
351  } else {
352#if defined(OS_MACOSX)
353    // We need to do this after getting |PPP_GetInterface()| (or presumably
354    // doing something nontrivial with the library), else the sandbox
355    // intercedes.
356    CHECK(InitializeSandbox());
357#endif
358
359    int32_t init_error = plugin_entry_points_.initialize_module(
360        local_pp_module_,
361        &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
362    if (init_error != PP_OK) {
363      LOG(WARNING) << "InitModule failed with error " << init_error;
364      ReportLoadResult(path, INIT_FAILED);
365      return;
366    }
367  }
368
369  // Initialization succeeded, so keep the plugin DLL loaded.
370  library_.Reset(library.Release());
371
372  ReportLoadResult(path, LOAD_SUCCESS);
373}
374
375void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
376                                  int renderer_child_id,
377                                  bool incognito) {
378  IPC::ChannelHandle channel_handle;
379
380  if (!plugin_entry_points_.get_interface ||  // Plugin couldn't be loaded.
381      !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
382                            &channel_handle)) {
383    Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
384    return;
385  }
386
387  Send(new PpapiHostMsg_ChannelCreated(channel_handle));
388}
389
390void PpapiThread::OnSetNetworkState(bool online) {
391  // Note the browser-process side shouldn't send us these messages in the
392  // first unless the plugin has dev permissions, so we don't need to check
393  // again here. We don't want random plugins depending on this dev interface.
394  if (!plugin_entry_points_.get_interface)
395    return;
396  const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
397      plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
398  if (ns)
399    ns->SetOnLine(PP_FromBool(online));
400}
401
402void PpapiThread::OnCrash() {
403  // Intentionally crash upon the request of the browser.
404  volatile int* null_pointer = NULL;
405  *null_pointer = 0;
406}
407
408void PpapiThread::OnHang() {
409  // Intentionally hang upon the request of the browser.
410  for (;;)
411    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
412}
413
414bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
415                                       int renderer_child_id,
416                                       bool incognito,
417                                       IPC::ChannelHandle* handle) {
418  DCHECK(is_broker_ == (connect_instance_func_ != NULL));
419  IPC::ChannelHandle plugin_handle;
420  plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
421      base::StringPrintf(
422          "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
423
424  ppapi::proxy::ProxyChannel* dispatcher = NULL;
425  bool init_result = false;
426  if (is_broker_) {
427    BrokerProcessDispatcher* broker_dispatcher =
428        new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
429                                    connect_instance_func_);
430    init_result = broker_dispatcher->InitBrokerWithChannel(this,
431                                                           renderer_pid,
432                                                           plugin_handle,
433                                                           false);
434    dispatcher = broker_dispatcher;
435  } else {
436    PluginProcessDispatcher* plugin_dispatcher =
437        new PluginProcessDispatcher(plugin_entry_points_.get_interface,
438                                    permissions_,
439                                    incognito);
440    init_result = plugin_dispatcher->InitPluginWithChannel(this,
441                                                           renderer_pid,
442                                                           plugin_handle,
443                                                           false);
444    dispatcher = plugin_dispatcher;
445  }
446
447  if (!init_result) {
448    delete dispatcher;
449    return false;
450  }
451
452  handle->name = plugin_handle.name;
453#if defined(OS_POSIX)
454  // On POSIX, transfer ownership of the renderer-side (client) FD.
455  // This ensures this process will be notified when it is closed even if a
456  // connection is not established.
457  handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
458  if (handle->socket.fd == -1)
459    return false;
460#endif
461
462  // From here, the dispatcher will manage its own lifetime according to the
463  // lifetime of the attached channel.
464  return true;
465}
466
467void PpapiThread::SavePluginName(const base::FilePath& path) {
468  ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
469      path.BaseName().AsUTF8Unsafe());
470
471  // plugin() is NULL when in-process, which is fine, because this is
472  // just a hook for setting the process name.
473  if (GetContentClient()->plugin()) {
474    GetContentClient()->plugin()->PluginProcessStarted(
475        path.BaseName().RemoveExtension().LossyDisplayName());
476  }
477}
478
479void PpapiThread::ReportLoadResult(const base::FilePath& path,
480                                   LoadResult result) {
481  DCHECK_LT(result, LOAD_RESULT_MAX);
482
483  std::ostringstream histogram_name;
484  histogram_name << "Plugin.Ppapi" << (is_broker_ ? "Broker" : "Plugin")
485                 << "LoadResult_" << path.BaseName().MaybeAsASCII();
486
487  // Note: This leaks memory, which is expected behavior.
488  base::HistogramBase* histogram =
489      base::LinearHistogram::FactoryGet(
490          histogram_name.str(),
491          1,
492          LOAD_RESULT_MAX,
493          LOAD_RESULT_MAX + 1,
494          base::HistogramBase::kUmaTargetedHistogramFlag);
495
496  histogram->Add(result);
497}
498
499}  // namespace content
500