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/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)
     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->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 
    124 PpapiThread::~PpapiThread() {
    125 }
    126 
    127 void 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 
    137 bool 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.
    148 bool 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 
    161 void 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 
    169 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
    170   return ChildProcess::current()->io_message_loop_proxy();
    171 }
    172 
    173 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
    174   return ChildProcess::current()->GetShutDownEvent();
    175 }
    176 
    177 IPC::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 
    193 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
    194   return &globally_seen_instance_ids_;
    195 }
    196 
    197 IPC::Sender* PpapiThread::GetBrowserSender() {
    198   return this;
    199 }
    200 
    201 std::string PpapiThread::GetUILanguage() {
    202   CommandLine* command_line = CommandLine::ForCurrentProcess();
    203   return command_line->GetSwitchValueASCII(switches::kLang);
    204 }
    205 
    206 void 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 
    213 void PpapiThread::SetActiveURL(const std::string& url) {
    214   GetContentClient()->SetActiveURL(GURL(url));
    215 }
    216 
    217 PP_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 
    228 uint32 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 
    245 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
    246   plugin_dispatchers_.erase(plugin_dispatcher_id);
    247 }
    248 
    249 void 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 
    413 void 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 
    428 void 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 
    440 void PpapiThread::OnCrash() {
    441   // Intentionally crash upon the request of the browser.
    442   volatile int* null_pointer = NULL;
    443   *null_pointer = 0;
    444 }
    445 
    446 void PpapiThread::OnHang() {
    447   // Intentionally hang upon the request of the browser.
    448   for (;;)
    449     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
    450 }
    451 
    452 bool 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 
    505 void 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 
    517 void 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 
    536 void 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