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