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