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