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