• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "components/nacl/renderer/ppb_nacl_private_impl.h"
6 
7 #include <numeric>
8 #include <string>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/containers/scoped_ptr_hash_map.h"
15 #include "base/cpu.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "components/nacl/common/nacl_host_messages.h"
20 #include "components/nacl/common/nacl_messages.h"
21 #include "components/nacl/common/nacl_nonsfi_util.h"
22 #include "components/nacl/common/nacl_switches.h"
23 #include "components/nacl/common/nacl_types.h"
24 #include "components/nacl/renderer/file_downloader.h"
25 #include "components/nacl/renderer/histogram.h"
26 #include "components/nacl/renderer/json_manifest.h"
27 #include "components/nacl/renderer/manifest_downloader.h"
28 #include "components/nacl/renderer/manifest_service_channel.h"
29 #include "components/nacl/renderer/nexe_load_manager.h"
30 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
31 #include "components/nacl/renderer/progress_event.h"
32 #include "components/nacl/renderer/sandbox_arch.h"
33 #include "components/nacl/renderer/trusted_plugin_channel.h"
34 #include "content/public/common/content_client.h"
35 #include "content/public/common/content_switches.h"
36 #include "content/public/common/sandbox_init.h"
37 #include "content/public/renderer/pepper_plugin_instance.h"
38 #include "content/public/renderer/render_thread.h"
39 #include "content/public/renderer/render_view.h"
40 #include "content/public/renderer/renderer_ppapi_host.h"
41 #include "native_client/src/public/imc_types.h"
42 #include "net/base/data_url.h"
43 #include "net/base/net_errors.h"
44 #include "net/http/http_util.h"
45 #include "ppapi/c/pp_bool.h"
46 #include "ppapi/c/private/pp_file_handle.h"
47 #include "ppapi/shared_impl/ppapi_globals.h"
48 #include "ppapi/shared_impl/ppapi_permissions.h"
49 #include "ppapi/shared_impl/ppapi_preferences.h"
50 #include "ppapi/shared_impl/var.h"
51 #include "ppapi/shared_impl/var_tracker.h"
52 #include "ppapi/thunk/enter.h"
53 #include "third_party/WebKit/public/platform/WebURLLoader.h"
54 #include "third_party/WebKit/public/web/WebDocument.h"
55 #include "third_party/WebKit/public/web/WebElement.h"
56 #include "third_party/WebKit/public/web/WebLocalFrame.h"
57 #include "third_party/WebKit/public/web/WebPluginContainer.h"
58 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
59 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
60 #include "third_party/jsoncpp/source/include/json/reader.h"
61 #include "third_party/jsoncpp/source/include/json/value.h"
62 
63 namespace nacl {
64 namespace {
65 
66 // The pseudo-architecture used to indicate portable native client.
67 const char* const kPortableArch = "portable";
68 
69 // The base URL for resources used by the PNaCl translator processes.
70 const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
71 
72 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
73     g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
74 
InitializePnaclResourceHost()75 bool InitializePnaclResourceHost() {
76   // Must run on the main thread.
77   content::RenderThread* render_thread = content::RenderThread::Get();
78   if (!render_thread)
79     return false;
80   if (!g_pnacl_resource_host.Get()) {
81     g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
82         render_thread->GetIOMessageLoopProxy());
83     render_thread->AddFilter(g_pnacl_resource_host.Get());
84   }
85   return true;
86 }
87 
88 struct InstanceInfo {
InstanceInfonacl::__anona9b72c950111::InstanceInfo89   InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
90   GURL url;
91   ppapi::PpapiPermissions permissions;
92   base::ProcessId plugin_pid;
93   int plugin_child_id;
94   IPC::ChannelHandle channel_handle;
95 };
96 
97 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
98 
99 base::LazyInstance<InstanceInfoMap> g_instance_info =
100     LAZY_INSTANCE_INITIALIZER;
101 
102 typedef base::ScopedPtrHashMap<PP_Instance, NexeLoadManager>
103     NexeLoadManagerMap;
104 
105 base::LazyInstance<NexeLoadManagerMap> g_load_manager_map =
106     LAZY_INSTANCE_INITIALIZER;
107 
GetNexeLoadManager(PP_Instance instance)108 nacl::NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
109   NexeLoadManagerMap& map = g_load_manager_map.Get();
110   NexeLoadManagerMap::iterator iter = map.find(instance);
111   if (iter != map.end())
112     return iter->second;
113   return NULL;
114 }
115 
GetRoutingID(PP_Instance instance)116 int GetRoutingID(PP_Instance instance) {
117   // Check that we are on the main renderer thread.
118   DCHECK(content::RenderThread::Get());
119   content::RendererPpapiHost *host =
120       content::RendererPpapiHost::GetForPPInstance(instance);
121   if (!host)
122     return 0;
123   return host->GetRoutingIDForWidget(instance);
124 }
125 
126 // Returns whether the channel_handle is valid or not.
IsValidChannelHandle(const IPC::ChannelHandle & channel_handle)127 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
128   if (channel_handle.name.empty()) {
129     return false;
130   }
131 
132 #if defined(OS_POSIX)
133   if (channel_handle.socket.fd == -1) {
134     return false;
135   }
136 #endif
137 
138   return true;
139 }
140 
PostPPCompletionCallback(PP_CompletionCallback callback,int32_t status)141 void PostPPCompletionCallback(PP_CompletionCallback callback,
142                               int32_t status) {
143   ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
144       FROM_HERE,
145       base::Bind(callback.func, callback.user_data, status));
146 }
147 
148 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
149 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
150 // also PP_ManifestService's comment for more details about resource
151 // management.
152 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
153  public:
ManifestServiceProxy(const PPP_ManifestService * manifest_service,void * user_data)154   ManifestServiceProxy(const PPP_ManifestService* manifest_service,
155                        void* user_data)
156       : manifest_service_(*manifest_service),
157         user_data_(user_data) {
158   }
159 
~ManifestServiceProxy()160   virtual ~ManifestServiceProxy() {
161     Quit();
162   }
163 
StartupInitializationComplete()164   virtual void StartupInitializationComplete() OVERRIDE {
165     if (!user_data_)
166       return;
167 
168     if (!PP_ToBool(
169             manifest_service_.StartupInitializationComplete(user_data_))) {
170       user_data_ = NULL;
171     }
172   }
173 
OpenResource(const std::string & key,const ManifestServiceChannel::OpenResourceCallback & callback)174   virtual void OpenResource(
175       const std::string& key,
176       const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
177     if (!user_data_)
178       return;
179 
180     // The allocated callback will be freed in DidOpenResource, which is always
181     // called regardless whether OpenResource() succeeds or fails.
182     if (!PP_ToBool(manifest_service_.OpenResource(
183             user_data_,
184             key.c_str(),
185             DidOpenResource,
186             new ManifestServiceChannel::OpenResourceCallback(callback)))) {
187       user_data_ = NULL;
188     }
189   }
190 
191  private:
DidOpenResource(void * user_data,PP_FileHandle file_handle)192   static void DidOpenResource(void* user_data, PP_FileHandle file_handle) {
193     scoped_ptr<ManifestServiceChannel::OpenResourceCallback> callback(
194         static_cast<ManifestServiceChannel::OpenResourceCallback*>(user_data));
195     callback->Run(file_handle);
196   }
197 
Quit()198   void Quit() {
199     if (!user_data_)
200       return;
201 
202     bool result = PP_ToBool(manifest_service_.Quit(user_data_));
203     DCHECK(!result);
204     user_data_ = NULL;
205   }
206 
207   PPP_ManifestService manifest_service_;
208   void* user_data_;
209   DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
210 };
211 
CreateWebURLLoader(const blink::WebDocument & document,const GURL & gurl)212 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
213                                         const GURL& gurl) {
214   blink::WebURLLoaderOptions options;
215   options.untrustedHTTP = true;
216 
217   // Options settings here follow the original behavior in the trusted
218   // plugin and PepperURLLoaderHost.
219   if (document.securityOrigin().canRequest(gurl)) {
220     options.allowCredentials = true;
221   } else {
222     // Allow CORS.
223     options.crossOriginRequestPolicy =
224         blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
225   }
226   return document.frame()->createAssociatedURLLoader(options);
227 }
228 
CreateWebURLRequest(const blink::WebDocument & document,const GURL & gurl)229 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
230                                          const GURL& gurl) {
231   blink::WebURLRequest request;
232   request.initialize();
233   request.setURL(gurl);
234   request.setFirstPartyForCookies(document.firstPartyForCookies());
235   return request;
236 }
237 
FileDownloaderToPepperError(FileDownloader::Status status)238 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
239   switch (status) {
240     case FileDownloader::SUCCESS:
241       return PP_OK;
242     case FileDownloader::ACCESS_DENIED:
243       return PP_ERROR_NOACCESS;
244     case FileDownloader::FAILED:
245       return PP_ERROR_FAILED;
246     // No default case, to catch unhandled Status values.
247   }
248   return PP_ERROR_FAILED;
249 }
250 
251 // Launch NaCl's sel_ldr process.
LaunchSelLdr(PP_Instance instance,PP_Bool main_service_runtime,const char * alleged_url,PP_Bool uses_irt,PP_Bool uses_ppapi,PP_Bool uses_nonsfi_mode,PP_Bool enable_ppapi_dev,PP_Bool enable_dyncode_syscalls,PP_Bool enable_exception_handling,PP_Bool enable_crash_throttling,const PPP_ManifestService * manifest_service_interface,void * manifest_service_user_data,void * imc_handle,PP_CompletionCallback callback)252 void LaunchSelLdr(PP_Instance instance,
253                   PP_Bool main_service_runtime,
254                   const char* alleged_url,
255                   PP_Bool uses_irt,
256                   PP_Bool uses_ppapi,
257                   PP_Bool uses_nonsfi_mode,
258                   PP_Bool enable_ppapi_dev,
259                   PP_Bool enable_dyncode_syscalls,
260                   PP_Bool enable_exception_handling,
261                   PP_Bool enable_crash_throttling,
262                   const PPP_ManifestService* manifest_service_interface,
263                   void* manifest_service_user_data,
264                   void* imc_handle,
265                   PP_CompletionCallback callback) {
266   CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
267             BelongsToCurrentThread());
268 
269   // Create the manifest service proxy here, so on error case, it will be
270   // destructed (without passing it to ManifestServiceChannel), and QUIT
271   // will be called in its destructor so that the caller of this function
272   // can free manifest_service_user_data properly.
273   scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
274       new ManifestServiceProxy(manifest_service_interface,
275                                manifest_service_user_data));
276 
277   FileDescriptor result_socket;
278   IPC::Sender* sender = content::RenderThread::Get();
279   DCHECK(sender);
280   int routing_id = 0;
281   // If the nexe uses ppapi APIs, we need a routing ID.
282   // To get the routing ID, we must be on the main thread.
283   // Some nexes do not use ppapi and launch from the background thread,
284   // so those nexes can skip finding a routing_id.
285   if (uses_ppapi) {
286     routing_id = GetRoutingID(instance);
287     if (!routing_id) {
288       ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
289           FROM_HERE,
290           base::Bind(callback.func, callback.user_data,
291                      static_cast<int32_t>(PP_ERROR_FAILED)));
292       return;
293     }
294   }
295 
296   InstanceInfo instance_info;
297   instance_info.url = GURL(alleged_url);
298 
299   uint32_t perm_bits = ppapi::PERMISSION_NONE;
300   // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
301   // it's clearer to developers when they are using 'Dev' inappropriately. We
302   // must also check on the trusted side of the proxy.
303   if (enable_ppapi_dev)
304     perm_bits |= ppapi::PERMISSION_DEV;
305   instance_info.permissions =
306       ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
307   std::string error_message_string;
308   NaClLaunchResult launch_result;
309 
310   if (!sender->Send(new NaClHostMsg_LaunchNaCl(
311           NaClLaunchParams(instance_info.url.spec(),
312                            routing_id,
313                            perm_bits,
314                            PP_ToBool(uses_irt),
315                            PP_ToBool(uses_nonsfi_mode),
316                            PP_ToBool(enable_dyncode_syscalls),
317                            PP_ToBool(enable_exception_handling),
318                            PP_ToBool(enable_crash_throttling)),
319           &launch_result,
320           &error_message_string))) {
321     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
322         FROM_HERE,
323         base::Bind(callback.func, callback.user_data,
324                    static_cast<int32_t>(PP_ERROR_FAILED)));
325     return;
326   }
327 
328   if (!error_message_string.empty()) {
329     if (PP_ToBool(main_service_runtime)) {
330       NexeLoadManager* load_manager = GetNexeLoadManager(instance);
331       if (load_manager) {
332         load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
333                                       "ServiceRuntime: failed to start",
334                                       error_message_string);
335       }
336     }
337     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
338         FROM_HERE,
339         base::Bind(callback.func, callback.user_data,
340                    static_cast<int32_t>(PP_ERROR_FAILED)));
341     return;
342   }
343   result_socket = launch_result.imc_channel_handle;
344   instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
345   instance_info.plugin_pid = launch_result.plugin_pid;
346   instance_info.plugin_child_id = launch_result.plugin_child_id;
347 
348   // Don't save instance_info if channel handle is invalid.
349   if (IsValidChannelHandle(instance_info.channel_handle))
350     g_instance_info.Get()[instance] = instance_info;
351 
352   *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
353 
354   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
355   DCHECK(load_manager);
356   if (!load_manager) {
357     PostPPCompletionCallback(callback, PP_ERROR_FAILED);
358     return;
359   }
360 
361   // Create the trusted plugin channel.
362   if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
363     scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
364         new TrustedPluginChannel(
365             launch_result.trusted_ipc_channel_handle));
366     load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
367   } else {
368     PostPPCompletionCallback(callback, PP_ERROR_FAILED);
369     return;
370   }
371 
372   // Create the manifest service handle as well.
373   // For security hardening, disable the IPCs for open_resource() when they
374   // aren't needed.  PNaCl doesn't expose open_resource(), and the new
375   // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
376   // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
377   // the plugin is a non-PNaCl plugin.
378   if (load_manager &&
379       enable_dyncode_syscalls &&
380       uses_nonsfi_mode &&
381       IsValidChannelHandle(
382           launch_result.manifest_service_ipc_channel_handle)) {
383     scoped_ptr<ManifestServiceChannel> manifest_service_channel(
384         new ManifestServiceChannel(
385             launch_result.manifest_service_ipc_channel_handle,
386             base::Bind(&PostPPCompletionCallback, callback),
387             manifest_service_proxy.Pass(),
388             content::RenderThread::Get()->GetShutdownEvent()));
389     load_manager->set_manifest_service_channel(
390         manifest_service_channel.Pass());
391   } else {
392     // Currently, manifest service works only on linux/non-SFI mode.
393     // On other platforms, the socket will not be created, and thus this
394     // condition needs to be handled as success.
395     PostPPCompletionCallback(callback, PP_OK);
396   }
397 }
398 
StartPpapiProxy(PP_Instance instance)399 PP_Bool StartPpapiProxy(PP_Instance instance) {
400   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
401   DCHECK(load_manager);
402   if (!load_manager)
403     return PP_FALSE;
404 
405   content::PepperPluginInstance* plugin_instance =
406       content::PepperPluginInstance::Get(instance);
407   if (!plugin_instance) {
408     DLOG(ERROR) << "GetInstance() failed";
409     return PP_FALSE;
410   }
411 
412   InstanceInfoMap& map = g_instance_info.Get();
413   InstanceInfoMap::iterator it = map.find(instance);
414   if (it == map.end()) {
415     DLOG(ERROR) << "Could not find instance ID";
416     return PP_FALSE;
417   }
418   InstanceInfo instance_info = it->second;
419   map.erase(it);
420 
421   PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
422       base::FilePath().AppendASCII(instance_info.url.spec()),
423       instance_info.permissions,
424       instance_info.channel_handle,
425       instance_info.plugin_pid,
426       instance_info.plugin_child_id);
427 
428   if (result == PP_EXTERNAL_PLUGIN_OK) {
429     // Log the amound of time that has passed between the trusted plugin being
430     // initialized and the untrusted plugin being initialized.  This is
431     // (roughly) the cost of using NaCl, in terms of startup time.
432     load_manager->ReportStartupOverhead();
433     return PP_TRUE;
434   } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
435     load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
436                                   "could not initialize module.");
437   } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
438     load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
439                                   "could not create instance.");
440   }
441   return PP_FALSE;
442 }
443 
UrandomFD(void)444 int UrandomFD(void) {
445 #if defined(OS_POSIX)
446   return base::GetUrandomFD();
447 #else
448   return -1;
449 #endif
450 }
451 
Are3DInterfacesDisabled()452 PP_Bool Are3DInterfacesDisabled() {
453   return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
454                          switches::kDisable3DAPIs));
455 }
456 
BrokerDuplicateHandle(PP_FileHandle source_handle,uint32_t process_id,PP_FileHandle * target_handle,uint32_t desired_access,uint32_t options)457 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
458                               uint32_t process_id,
459                               PP_FileHandle* target_handle,
460                               uint32_t desired_access,
461                               uint32_t options) {
462 #if defined(OS_WIN)
463   return content::BrokerDuplicateHandle(source_handle, process_id,
464                                         target_handle, desired_access,
465                                         options);
466 #else
467   return 0;
468 #endif
469 }
470 
471 // Convert a URL to a filename for GetReadonlyPnaclFd.
472 // Must be kept in sync with PnaclCanOpenFile() in
473 // components/nacl/browser/nacl_file_host.cc.
PnaclComponentURLToFilename(const std::string & url)474 std::string PnaclComponentURLToFilename(const std::string& url) {
475   // PNaCl component URLs aren't arbitrary URLs; they are always either
476   // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
477   // So, it's safe to just use string parsing operations here instead of
478   // URL-parsing ones.
479   DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
480   std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
481 
482   // Use white-listed-chars.
483   size_t replace_pos;
484   static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
485   replace_pos = r.find_first_not_of(white_list);
486   while(replace_pos != std::string::npos) {
487     r = r.replace(replace_pos, 1, "_");
488     replace_pos = r.find_first_not_of(white_list);
489   }
490   return r;
491 }
492 
GetReadonlyPnaclFd(const char * url)493 PP_FileHandle GetReadonlyPnaclFd(const char* url) {
494   std::string filename = PnaclComponentURLToFilename(url);
495   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
496   IPC::Sender* sender = content::RenderThread::Get();
497   DCHECK(sender);
498   if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
499           std::string(filename),
500           &out_fd))) {
501     return PP_kInvalidFileHandle;
502   }
503   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
504     return PP_kInvalidFileHandle;
505   }
506   return IPC::PlatformFileForTransitToPlatformFile(out_fd);
507 }
508 
CreateTemporaryFile(PP_Instance instance)509 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
510   IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
511   IPC::Sender* sender = content::RenderThread::Get();
512   DCHECK(sender);
513   if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
514           &transit_fd))) {
515     return PP_kInvalidFileHandle;
516   }
517 
518   if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
519     return PP_kInvalidFileHandle;
520   }
521 
522   return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
523 }
524 
GetNumberOfProcessors()525 int32_t GetNumberOfProcessors() {
526   int32_t num_processors;
527   IPC::Sender* sender = content::RenderThread::Get();
528   DCHECK(sender);
529   if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
530     return 1;
531   }
532   return num_processors;
533 }
534 
PPIsNonSFIModeEnabled()535 PP_Bool PPIsNonSFIModeEnabled() {
536   return PP_FromBool(IsNonSFIModeEnabled());
537 }
538 
GetNexeFd(PP_Instance instance,const char * pexe_url,uint32_t abi_version,uint32_t opt_level,const char * http_headers_param,const char * extra_flags,PP_Bool * is_hit,PP_FileHandle * handle,struct PP_CompletionCallback callback)539 int32_t GetNexeFd(PP_Instance instance,
540                   const char* pexe_url,
541                   uint32_t abi_version,
542                   uint32_t opt_level,
543                   const char* http_headers_param,
544                   const char* extra_flags,
545                   PP_Bool* is_hit,
546                   PP_FileHandle* handle,
547                   struct PP_CompletionCallback callback) {
548   ppapi::thunk::EnterInstance enter(instance, callback);
549   if (enter.failed())
550     return enter.retval();
551   if (!pexe_url || !is_hit || !handle)
552     return enter.SetResult(PP_ERROR_BADARGUMENT);
553   if (!InitializePnaclResourceHost())
554     return enter.SetResult(PP_ERROR_FAILED);
555 
556   std::string http_headers(http_headers_param);
557   net::HttpUtil::HeadersIterator iter(
558       http_headers.begin(), http_headers.end(), "\r\n");
559 
560   std::string last_modified;
561   std::string etag;
562   bool has_no_store_header = false;
563   while (iter.GetNext()) {
564     if (StringToLowerASCII(iter.name()) == "last-modified")
565       last_modified = iter.values();
566     if (StringToLowerASCII(iter.name()) == "etag")
567       etag = iter.values();
568     if (StringToLowerASCII(iter.name()) == "cache-control") {
569       net::HttpUtil::ValuesIterator values_iter(
570           iter.values_begin(), iter.values_end(), ',');
571       while (values_iter.GetNext()) {
572         if (StringToLowerASCII(values_iter.value()) == "no-store")
573           has_no_store_header = true;
574       }
575     }
576   }
577 
578   base::Time last_modified_time;
579   // If FromString fails, it doesn't touch last_modified_time and we just send
580   // the default-constructed null value.
581   base::Time::FromString(last_modified.c_str(), &last_modified_time);
582 
583   PnaclCacheInfo cache_info;
584   cache_info.pexe_url = GURL(pexe_url);
585   cache_info.abi_version = abi_version;
586   cache_info.opt_level = opt_level;
587   cache_info.last_modified = last_modified_time;
588   cache_info.etag = etag;
589   cache_info.has_no_store_header = has_no_store_header;
590   cache_info.sandbox_isa = GetSandboxArch();
591   cache_info.extra_flags = std::string(extra_flags);
592 
593   g_pnacl_resource_host.Get()->RequestNexeFd(
594       GetRoutingID(instance),
595       instance,
596       cache_info,
597       is_hit,
598       handle,
599       enter.callback());
600 
601   return enter.SetResult(PP_OK_COMPLETIONPENDING);
602 }
603 
ReportTranslationFinished(PP_Instance instance,PP_Bool success,int32_t opt_level,int64_t pexe_size,int64_t compile_time_us,int64_t total_time_us)604 void ReportTranslationFinished(PP_Instance instance,
605                                PP_Bool success,
606                                int32_t opt_level,
607                                int64_t pexe_size,
608                                int64_t compile_time_us,
609                                int64_t total_time_us) {
610   if (success == PP_TRUE) {
611     static const int32_t kUnknownOptLevel = 4;
612     if (opt_level < 0 || opt_level > 3)
613       opt_level = kUnknownOptLevel;
614     HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
615                        opt_level,
616                        kUnknownOptLevel + 1);
617     HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
618                       pexe_size / 1024,
619                       compile_time_us);
620     HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
621 
622     HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
623                              total_time_us / 1000);
624     HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
625                       pexe_size / 1024,
626                       total_time_us);
627   }
628 
629   // If the resource host isn't initialized, don't try to do that here.
630   // Just return because something is already very wrong.
631   if (g_pnacl_resource_host.Get() == NULL)
632     return;
633   g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
634 }
635 
OpenNaClExecutable(PP_Instance instance,const char * file_url,uint64_t * nonce_lo,uint64_t * nonce_hi)636 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
637                                  const char* file_url,
638                                  uint64_t* nonce_lo,
639                                  uint64_t* nonce_hi) {
640   // Fast path only works for installed file URLs.
641   GURL gurl(file_url);
642   if (!gurl.SchemeIs("chrome-extension"))
643     return PP_kInvalidFileHandle;
644 
645   content::PepperPluginInstance* plugin_instance =
646       content::PepperPluginInstance::Get(instance);
647   // IMPORTANT: Make sure the document can request the given URL. If we don't
648   // check, a malicious app could probe the extension system. This enforces a
649   // same-origin policy which prevents the app from requesting resources from
650   // another app.
651   blink::WebSecurityOrigin security_origin =
652       plugin_instance->GetContainer()->element().document().securityOrigin();
653   if (!security_origin.canRequest(gurl))
654     return PP_kInvalidFileHandle;
655 
656   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
657   IPC::Sender* sender = content::RenderThread::Get();
658   DCHECK(sender);
659   *nonce_lo = 0;
660   *nonce_hi = 0;
661   base::FilePath file_path;
662   if (!sender->Send(
663       new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
664                                          GURL(file_url),
665                                          &out_fd,
666                                          nonce_lo,
667                                          nonce_hi))) {
668     return PP_kInvalidFileHandle;
669   }
670 
671   if (out_fd == IPC::InvalidPlatformFileForTransit())
672     return PP_kInvalidFileHandle;
673 
674   return IPC::PlatformFileForTransitToPlatformFile(out_fd);
675 }
676 
DispatchEvent(PP_Instance instance,PP_NaClEventType event_type,const char * resource_url,PP_Bool length_is_computable,uint64_t loaded_bytes,uint64_t total_bytes)677 void DispatchEvent(PP_Instance instance,
678                    PP_NaClEventType event_type,
679                    const char *resource_url,
680                    PP_Bool length_is_computable,
681                    uint64_t loaded_bytes,
682                    uint64_t total_bytes) {
683   ProgressEvent event(event_type,
684                       resource_url,
685                       PP_ToBool(length_is_computable),
686                       loaded_bytes,
687                       total_bytes);
688   DispatchProgressEvent(instance, event);
689 }
690 
ReportLoadSuccess(PP_Instance instance,const char * url,uint64_t loaded_bytes,uint64_t total_bytes)691 void ReportLoadSuccess(PP_Instance instance,
692                        const char* url,
693                        uint64_t loaded_bytes,
694                        uint64_t total_bytes) {
695   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
696   if (load_manager)
697     load_manager->ReportLoadSuccess(url, loaded_bytes, total_bytes);
698 }
699 
ReportLoadError(PP_Instance instance,PP_NaClError error,const char * error_message)700 void ReportLoadError(PP_Instance instance,
701                      PP_NaClError error,
702                      const char* error_message) {
703   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
704   if (load_manager)
705     load_manager->ReportLoadError(error, error_message);
706 }
707 
ReportLoadAbort(PP_Instance instance)708 void ReportLoadAbort(PP_Instance instance) {
709   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
710   if (load_manager)
711     load_manager->ReportLoadAbort();
712 }
713 
NexeDidCrash(PP_Instance instance,const char * crash_log)714 void NexeDidCrash(PP_Instance instance, const char* crash_log) {
715   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
716   if (load_manager)
717     load_manager->NexeDidCrash(crash_log);
718 }
719 
InstanceCreated(PP_Instance instance)720 void InstanceCreated(PP_Instance instance) {
721   scoped_ptr<NexeLoadManager> new_load_manager(new NexeLoadManager(instance));
722   NexeLoadManagerMap& map = g_load_manager_map.Get();
723   DLOG_IF(ERROR, map.count(instance) != 0) << "Instance count should be 0";
724   map.add(instance, new_load_manager.Pass());
725 }
726 
InstanceDestroyed(PP_Instance instance)727 void InstanceDestroyed(PP_Instance instance) {
728   DeleteJsonManifest(instance);
729 
730   NexeLoadManagerMap& map = g_load_manager_map.Get();
731   DLOG_IF(ERROR, map.count(instance) == 0) << "Could not find instance ID";
732   // The erase may call NexeLoadManager's destructor prior to removing it from
733   // the map. In that case, it is possible for the trusted Plugin to re-enter
734   // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
735   // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
736   // from the map prior to the destructor being invoked.
737   scoped_ptr<NexeLoadManager> temp(map.take(instance));
738   map.erase(instance);
739 }
740 
NaClDebugEnabledForURL(const char * alleged_nmf_url)741 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
742   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
743     return PP_FALSE;
744   bool should_debug;
745   IPC::Sender* sender = content::RenderThread::Get();
746   DCHECK(sender);
747   if(!sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(
748          GURL(alleged_nmf_url),
749          &should_debug))) {
750     return PP_FALSE;
751   }
752   return PP_FromBool(should_debug);
753 }
754 
LogToConsole(PP_Instance instance,const char * message)755 void LogToConsole(PP_Instance instance, const char* message) {
756   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
757   DCHECK(load_manager);
758   if (load_manager)
759     load_manager->LogToConsole(std::string(message));
760 }
761 
GetNaClReadyState(PP_Instance instance)762 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
763   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
764   DCHECK(load_manager);
765   if (load_manager)
766     return load_manager->nacl_ready_state();
767   return PP_NACL_READY_STATE_UNSENT;
768 }
769 
GetExitStatus(PP_Instance instance)770 int32_t GetExitStatus(PP_Instance instance) {
771   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
772   DCHECK(load_manager);
773   if (load_manager)
774     return load_manager->exit_status();
775   return -1;
776 }
777 
SetExitStatus(PP_Instance instance,int32_t exit_status)778 void SetExitStatus(PP_Instance instance, int32_t exit_status) {
779   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
780   DCHECK(load_manager);
781   if (load_manager)
782     return load_manager->set_exit_status(exit_status);
783 }
784 
Vlog(const char * message)785 void Vlog(const char* message) {
786   VLOG(1) << message;
787 }
788 
InitializePlugin(PP_Instance instance,uint32_t argc,const char * argn[],const char * argv[])789 void InitializePlugin(PP_Instance instance,
790                       uint32_t argc,
791                       const char* argn[],
792                       const char* argv[]) {
793   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
794   DCHECK(load_manager);
795   if (load_manager)
796     load_manager->InitializePlugin(argc, argn, argv);
797 }
798 
GetNexeSize(PP_Instance instance)799 int64_t GetNexeSize(PP_Instance instance) {
800   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
801   DCHECK(load_manager);
802   if (load_manager)
803     return load_manager->nexe_size();
804   return 0;
805 }
806 
807 void DownloadManifestToBuffer(PP_Instance instance,
808                               struct PP_CompletionCallback callback);
809 
810 bool CreateJsonManifest(PP_Instance instance,
811                         const std::string& manifest_url,
812                         const std::string& manifest_data);
813 
RequestNaClManifest(PP_Instance instance,PP_CompletionCallback callback)814 void RequestNaClManifest(PP_Instance instance,
815                          PP_CompletionCallback callback) {
816   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
817   DCHECK(load_manager);
818   if (!load_manager) {
819     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
820         FROM_HERE,
821         base::Bind(callback.func, callback.user_data,
822                    static_cast<int32_t>(PP_ERROR_FAILED)));
823     return;
824   }
825 
826   std::string url = load_manager->GetManifestURLArgument();
827   if (url.empty() || !load_manager->RequestNaClManifest(url)) {
828     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
829         FROM_HERE,
830         base::Bind(callback.func, callback.user_data,
831                    static_cast<int32_t>(PP_ERROR_FAILED)));
832     return;
833   }
834 
835   const GURL& base_url = load_manager->manifest_base_url();
836   if (base_url.SchemeIs("data")) {
837     GURL gurl(base_url);
838     std::string mime_type;
839     std::string charset;
840     std::string data;
841     int32_t error = PP_ERROR_FAILED;
842     if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
843       if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
844         if (CreateJsonManifest(instance, base_url.spec(), data))
845           error = PP_OK;
846       } else {
847         load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
848                                       "manifest file too large.");
849       }
850     } else {
851       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
852                                     "could not load manifest url.");
853     }
854     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
855         FROM_HERE,
856         base::Bind(callback.func, callback.user_data, error));
857   } else {
858     DownloadManifestToBuffer(instance, callback);
859   }
860 }
861 
GetManifestBaseURL(PP_Instance instance)862 PP_Var GetManifestBaseURL(PP_Instance instance) {
863   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
864   DCHECK(load_manager);
865   if (!load_manager)
866     return PP_MakeUndefined();
867   const GURL& gurl = load_manager->manifest_base_url();
868   if (!gurl.is_valid())
869     return PP_MakeUndefined();
870   return ppapi::StringVar::StringToPPVar(gurl.spec());
871 }
872 
ProcessNaClManifest(PP_Instance instance,const char * program_url)873 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
874   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
875   if (load_manager)
876     load_manager->ProcessNaClManifest(program_url);
877 }
878 
DevInterfacesEnabled(PP_Instance instance)879 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
880   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
881   if (load_manager)
882     return PP_FromBool(load_manager->DevInterfacesEnabled());
883   return PP_FALSE;
884 }
885 
886 void DownloadManifestToBufferCompletion(PP_Instance instance,
887                                         struct PP_CompletionCallback callback,
888                                         base::Time start_time,
889                                         PP_NaClError pp_nacl_error,
890                                         const std::string& data);
891 
DownloadManifestToBuffer(PP_Instance instance,struct PP_CompletionCallback callback)892 void DownloadManifestToBuffer(PP_Instance instance,
893                               struct PP_CompletionCallback callback) {
894   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
895   DCHECK(load_manager);
896   content::PepperPluginInstance* plugin_instance =
897       content::PepperPluginInstance::Get(instance);
898   if (!load_manager || !plugin_instance) {
899     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
900         FROM_HERE,
901         base::Bind(callback.func, callback.user_data,
902                    static_cast<int32_t>(PP_ERROR_FAILED)));
903   }
904   const blink::WebDocument& document =
905       plugin_instance->GetContainer()->element().document();
906 
907   const GURL& gurl = load_manager->manifest_base_url();
908   scoped_ptr<blink::WebURLLoader> url_loader(
909       CreateWebURLLoader(document, gurl));
910   blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
911 
912   // ManifestDownloader deletes itself after invoking the callback.
913   ManifestDownloader* manifest_downloader = new ManifestDownloader(
914       url_loader.Pass(),
915       load_manager->is_installed(),
916       base::Bind(DownloadManifestToBufferCompletion,
917                  instance, callback, base::Time::Now()));
918   manifest_downloader->Load(request);
919 }
920 
DownloadManifestToBufferCompletion(PP_Instance instance,struct PP_CompletionCallback callback,base::Time start_time,PP_NaClError pp_nacl_error,const std::string & data)921 void DownloadManifestToBufferCompletion(PP_Instance instance,
922                                         struct PP_CompletionCallback callback,
923                                         base::Time start_time,
924                                         PP_NaClError pp_nacl_error,
925                                         const std::string& data) {
926   base::TimeDelta download_time = base::Time::Now() - start_time;
927   HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
928                      download_time.InMilliseconds());
929 
930   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
931   if (!load_manager) {
932     callback.func(callback.user_data, PP_ERROR_ABORTED);
933     return;
934   }
935 
936   int32_t pp_error;
937   switch (pp_nacl_error) {
938     case PP_NACL_ERROR_LOAD_SUCCESS:
939       pp_error = PP_OK;
940       break;
941     case PP_NACL_ERROR_MANIFEST_LOAD_URL:
942       pp_error = PP_ERROR_FAILED;
943       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
944                                     "could not load manifest url.");
945       break;
946     case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
947       pp_error = PP_ERROR_FILETOOBIG;
948       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
949                                     "manifest file too large.");
950       break;
951     case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
952       pp_error = PP_ERROR_NOACCESS;
953       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
954                                     "access to manifest url was denied.");
955       break;
956     default:
957       NOTREACHED();
958       pp_error = PP_ERROR_FAILED;
959       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
960                                     "could not load manifest url.");
961   }
962 
963   if (pp_error == PP_OK) {
964     std::string base_url = load_manager->manifest_base_url().spec();
965     if (!CreateJsonManifest(instance, base_url, data))
966       pp_error = PP_ERROR_FAILED;
967   }
968   callback.func(callback.user_data, pp_error);
969 }
970 
CreateJsonManifest(PP_Instance instance,const std::string & manifest_url,const std::string & manifest_data)971 bool CreateJsonManifest(PP_Instance instance,
972                         const std::string& manifest_url,
973                         const std::string& manifest_data) {
974   HistogramSizeKB("NaCl.Perf.Size.Manifest",
975                   static_cast<int32_t>(manifest_data.length() / 1024));
976 
977   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
978   if (!load_manager)
979     return false;
980 
981   const char* isa_type;
982   if (load_manager->IsPNaCl())
983     isa_type = kPortableArch;
984   else
985     isa_type = GetSandboxArch();
986 
987   scoped_ptr<nacl::JsonManifest> j(
988       new nacl::JsonManifest(
989           manifest_url.c_str(),
990           isa_type,
991           IsNonSFIModeEnabled(),
992           PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
993   JsonManifest::ErrorInfo error_info;
994   if (j->Init(manifest_data.c_str(), &error_info)) {
995     AddJsonManifest(instance, j.Pass());
996     return true;
997   }
998   load_manager->ReportLoadError(error_info.error, error_info.string);
999   return false;
1000 }
1001 
ManifestGetProgramURL(PP_Instance instance,PP_Var * pp_full_url,PP_PNaClOptions * pnacl_options,PP_Bool * pp_uses_nonsfi_mode)1002 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1003                               PP_Var* pp_full_url,
1004                               PP_PNaClOptions* pnacl_options,
1005                               PP_Bool* pp_uses_nonsfi_mode) {
1006   nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1007 
1008   JsonManifest* manifest = GetJsonManifest(instance);
1009   if (manifest == NULL)
1010     return PP_FALSE;
1011 
1012   bool uses_nonsfi_mode;
1013   std::string full_url;
1014   JsonManifest::ErrorInfo error_info;
1015   if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1016                               &error_info)) {
1017     *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1018     *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1019     return PP_TRUE;
1020   }
1021 
1022   if (load_manager)
1023     load_manager->ReportLoadError(error_info.error, error_info.string);
1024   return PP_FALSE;
1025 }
1026 
ManifestResolveKey(PP_Instance instance,bool is_helper_process,const std::string & key,std::string * full_url,PP_PNaClOptions * pnacl_options)1027 bool ManifestResolveKey(PP_Instance instance,
1028                         bool is_helper_process,
1029                         const std::string& key,
1030                         std::string* full_url,
1031                         PP_PNaClOptions* pnacl_options) {
1032   // For "helper" processes (llc and ld), we resolve keys manually as there is
1033   // no existing .nmf file to parse.
1034   if (is_helper_process) {
1035     pnacl_options->translate = PP_FALSE;
1036     // We can only resolve keys in the files/ namespace.
1037     const std::string kFilesPrefix = "files/";
1038     if (key.find(kFilesPrefix) == std::string::npos) {
1039       nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1040       if (load_manager)
1041         load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1042                                       "key did not start with files/");
1043       return false;
1044     }
1045     std::string key_basename = key.substr(kFilesPrefix.length());
1046     *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1047                 key_basename;
1048     return true;
1049   }
1050 
1051   JsonManifest* manifest = GetJsonManifest(instance);
1052   if (manifest == NULL)
1053     return false;
1054 
1055   return manifest->ResolveKey(key, full_url, pnacl_options);
1056 }
1057 
ExternalManifestResolveKey(PP_Instance instance,PP_Bool is_helper_process,const char * key,PP_Var * pp_full_url,PP_PNaClOptions * pnacl_options)1058 PP_Bool ExternalManifestResolveKey(PP_Instance instance,
1059                                    PP_Bool is_helper_process,
1060                                    const char* key,
1061                                    PP_Var* pp_full_url,
1062                                    PP_PNaClOptions* pnacl_options) {
1063   std::string full_url;
1064   bool ok = ManifestResolveKey(instance,
1065                                PP_ToBool(is_helper_process),
1066                                std::string(key),
1067                                &full_url,
1068                                pnacl_options);
1069   if (ok)
1070     *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1071   return PP_FromBool(ok);
1072 }
1073 
GetPNaClResourceInfo(PP_Instance instance,const char * filename,PP_Var * llc_tool_name,PP_Var * ld_tool_name)1074 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1075                              const char* filename,
1076                              PP_Var* llc_tool_name,
1077                              PP_Var* ld_tool_name) {
1078   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1079   DCHECK(load_manager);
1080   if (!load_manager)
1081     return PP_FALSE;
1082 
1083   base::File file(GetReadonlyPnaclFd(filename));
1084   if (!file.IsValid()) {
1085     load_manager->ReportLoadError(
1086         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1087         "The Portable Native Client (pnacl) component is not "
1088         "installed. Please consult chrome://components for more "
1089         "information.");
1090     return PP_FALSE;
1091   }
1092 
1093   base::File::Info file_info;
1094   if (!file.GetInfo(&file_info)) {
1095     load_manager->ReportLoadError(
1096         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1097         std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1098             filename);
1099     return PP_FALSE;
1100   }
1101 
1102   if (file_info.size > 1 << 20) {
1103     load_manager->ReportLoadError(
1104         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1105         std::string("GetPNaClResourceInfo, file too large: ") + filename);
1106     return PP_FALSE;
1107   }
1108 
1109   scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1110   if (buffer.get() == NULL) {
1111     load_manager->ReportLoadError(
1112         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1113         std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1114             filename);
1115     return PP_FALSE;
1116   }
1117 
1118   int rc = file.Read(0, buffer.get(), file_info.size);
1119   if (rc < 0) {
1120     load_manager->ReportLoadError(
1121         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1122         std::string("GetPNaClResourceInfo, reading failed for: ") + filename);
1123     return PP_FALSE;
1124   }
1125 
1126   // Null-terminate the bytes we we read from the file.
1127   buffer.get()[rc] = 0;
1128 
1129   // Expect the JSON file to contain a top-level object (dictionary).
1130   Json::Reader json_reader;
1131   Json::Value json_data;
1132   if (!json_reader.parse(buffer.get(), json_data)) {
1133     load_manager->ReportLoadError(
1134         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1135         std::string("Parsing resource info failed: JSON parse error: ") +
1136             json_reader.getFormattedErrorMessages());
1137     return PP_FALSE;
1138   }
1139 
1140   if (!json_data.isObject()) {
1141     load_manager->ReportLoadError(
1142         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1143         "Parsing resource info failed: Malformed JSON dictionary");
1144     return PP_FALSE;
1145   }
1146 
1147   if (json_data.isMember("pnacl-llc-name")) {
1148     Json::Value json_name = json_data["pnacl-llc-name"];
1149     if (json_name.isString()) {
1150       std::string llc_tool_name_str = json_name.asString();
1151       *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1152     }
1153   }
1154 
1155   if (json_data.isMember("pnacl-ld-name")) {
1156     Json::Value json_name = json_data["pnacl-ld-name"];
1157     if (json_name.isString()) {
1158       std::string ld_tool_name_str = json_name.asString();
1159       *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1160     }
1161   }
1162   return PP_TRUE;
1163 }
1164 
1165 // Helper to std::accumulate that creates a comma-separated list from the input.
CommaAccumulator(const std::string & lhs,const std::string & rhs)1166 std::string CommaAccumulator(const std::string &lhs, const std::string &rhs) {
1167   if (lhs.empty())
1168     return rhs;
1169   return lhs + "," + rhs;
1170 }
1171 
GetCpuFeatureAttrs()1172 PP_Var GetCpuFeatureAttrs() {
1173   // PNaCl's translator from pexe to nexe can be told exactly what
1174   // capabilities the user's machine has because the pexe to nexe
1175   // translation is specific to the machine, and CPU information goes
1176   // into the translation cache. This allows the translator to generate
1177   // faster code.
1178   //
1179   // Care must be taken to avoid instructions which aren't supported by
1180   // the NaCl sandbox. Ideally the translator would do this, but there's
1181   // no point in not doing the whitelist here.
1182   //
1183   // TODO(jfb) Some features are missing, either because the NaCl
1184   //           sandbox doesn't support them, because base::CPU doesn't
1185   //           detect them, or because they don't help vector shuffles
1186   //           (and we omit them because it simplifies testing). Add the
1187   //           other features.
1188   //
1189   // TODO(jfb) The following is x86-specific. The base::CPU class
1190   //           doesn't handle other architectures very well, and we
1191   //           should at least detect the presence of ARM's integer
1192   //           divide.
1193   std::vector<std::string> attrs;
1194   base::CPU cpu;
1195 
1196   // On x86, SSE features are ordered: the most recent one implies the
1197   // others. Care is taken here to only specify the latest SSE version,
1198   // whereas non-SSE features don't follow this model: POPCNT is
1199   // effectively always implied by SSE4.2 but has to be specified
1200   // separately.
1201   //
1202   // TODO: AVX2, AVX, SSE 4.2.
1203   if (cpu.has_sse41()) attrs.push_back("+sse4.1");
1204   // TODO: SSE 4A, SSE 4.
1205   else if (cpu.has_ssse3()) attrs.push_back("+ssse3");
1206   // TODO: SSE 3
1207   else if (cpu.has_sse2()) attrs.push_back("+sse2");
1208 
1209   // TODO: AES, POPCNT, LZCNT, ...
1210 
1211   return ppapi::StringVar::StringToPPVar(std::accumulate(
1212       attrs.begin(), attrs.end(), std::string(), CommaAccumulator));
1213 }
1214 
PostMessageToJavaScriptMainThread(PP_Instance instance,const std::string & message)1215 void PostMessageToJavaScriptMainThread(PP_Instance instance,
1216                                        const std::string& message) {
1217   content::PepperPluginInstance* plugin_instance =
1218       content::PepperPluginInstance::Get(instance);
1219   if (plugin_instance) {
1220     PP_Var message_var = ppapi::StringVar::StringToPPVar(message);
1221     plugin_instance->PostMessageToJavaScript(message_var);
1222     ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var);
1223   }
1224 }
1225 
PostMessageToJavaScript(PP_Instance instance,const char * message)1226 void PostMessageToJavaScript(PP_Instance instance, const char* message) {
1227   ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1228       FROM_HERE,
1229       base::Bind(&PostMessageToJavaScriptMainThread,
1230                  instance,
1231                  std::string(message)));
1232 }
1233 
1234 // Encapsulates some of the state for a call to DownloadNexe to prevent
1235 // argument lists from getting too long.
1236 struct DownloadNexeRequest {
1237   PP_Instance instance;
1238   std::string url;
1239   PP_CompletionCallback callback;
1240   base::Time start_time;
1241 };
1242 
1243 // A utility class to ensure that we don't send progress events more often than
1244 // every 10ms for a given file.
1245 class ProgressEventRateLimiter {
1246  public:
ProgressEventRateLimiter(PP_Instance instance)1247   explicit ProgressEventRateLimiter(PP_Instance instance)
1248       : instance_(instance) { }
1249 
ReportProgress(const std::string & url,int64_t total_bytes_received,int64_t total_bytes_to_be_received)1250   void ReportProgress(const std::string& url,
1251                       int64_t total_bytes_received,
1252                       int64_t total_bytes_to_be_received) {
1253     base::Time now = base::Time::Now();
1254     if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1255       DispatchProgressEvent(instance_,
1256                             ProgressEvent(PP_NACL_EVENT_PROGRESS,
1257                                           url,
1258                                           total_bytes_to_be_received >= 0,
1259                                           total_bytes_received,
1260                                           total_bytes_to_be_received));
1261       last_event_ = now;
1262     }
1263   }
1264 
1265  private:
1266   PP_Instance instance_;
1267   base::Time last_event_;
1268 };
1269 
1270 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1271                             PP_NaClFileInfo* out_file_info,
1272                             FileDownloader::Status status,
1273                             base::File target_file,
1274                             int http_status);
1275 
DownloadNexe(PP_Instance instance,const char * url,PP_NaClFileInfo * out_file_info,PP_CompletionCallback callback)1276 void DownloadNexe(PP_Instance instance,
1277                   const char* url,
1278                   PP_NaClFileInfo* out_file_info,
1279                   PP_CompletionCallback callback) {
1280   CHECK(url);
1281   CHECK(out_file_info);
1282   DownloadNexeRequest request;
1283   request.instance = instance;
1284   request.url = url;
1285   request.callback = callback;
1286   request.start_time = base::Time::Now();
1287 
1288   // Try the fast path for retrieving the file first.
1289   PP_FileHandle handle = OpenNaClExecutable(instance,
1290                                             url,
1291                                             &out_file_info->token_lo,
1292                                             &out_file_info->token_hi);
1293   if (handle != PP_kInvalidFileHandle) {
1294     DownloadNexeCompletion(request,
1295                            out_file_info,
1296                            FileDownloader::SUCCESS,
1297                            base::File(handle),
1298                            200);
1299     return;
1300   }
1301 
1302   // The fast path didn't work, we'll fetch the file using URLLoader and write
1303   // it to local storage.
1304   base::File target_file(CreateTemporaryFile(instance));
1305   GURL gurl(url);
1306 
1307   content::PepperPluginInstance* plugin_instance =
1308       content::PepperPluginInstance::Get(instance);
1309   if (!plugin_instance) {
1310     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1311         FROM_HERE,
1312         base::Bind(callback.func, callback.user_data,
1313                    static_cast<int32_t>(PP_ERROR_FAILED)));
1314   }
1315   const blink::WebDocument& document =
1316       plugin_instance->GetContainer()->element().document();
1317   scoped_ptr<blink::WebURLLoader> url_loader(
1318       CreateWebURLLoader(document, gurl));
1319   blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1320 
1321   ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1322 
1323   // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1324   FileDownloader* file_downloader = new FileDownloader(
1325       url_loader.Pass(),
1326       target_file.Pass(),
1327       base::Bind(&DownloadNexeCompletion, request, out_file_info),
1328       base::Bind(&ProgressEventRateLimiter::ReportProgress,
1329                  base::Owned(tracker), url));
1330   file_downloader->Load(url_request);
1331 }
1332 
DownloadNexeCompletion(const DownloadNexeRequest & request,PP_NaClFileInfo * out_file_info,FileDownloader::Status status,base::File target_file,int http_status)1333 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1334                             PP_NaClFileInfo* out_file_info,
1335                             FileDownloader::Status status,
1336                             base::File target_file,
1337                             int http_status) {
1338   int32_t pp_error = FileDownloaderToPepperError(status);
1339   int64_t bytes_read = -1;
1340   if (pp_error == PP_OK && target_file.IsValid()) {
1341     base::File::Info info;
1342     if (target_file.GetInfo(&info))
1343       bytes_read = info.size;
1344   }
1345 
1346   if (bytes_read == -1) {
1347     target_file.Close();
1348     pp_error = PP_ERROR_FAILED;
1349   }
1350 
1351   base::TimeDelta download_time = base::Time::Now() - request.start_time;
1352 
1353   NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1354   if (load_manager) {
1355     load_manager->NexeFileDidOpen(pp_error,
1356                                   target_file,
1357                                   http_status,
1358                                   bytes_read,
1359                                   request.url,
1360                                   download_time);
1361   }
1362 
1363   if (pp_error == PP_OK && target_file.IsValid())
1364     out_file_info->handle = target_file.TakePlatformFile();
1365   else
1366     out_file_info->handle = PP_kInvalidFileHandle;
1367 
1368   request.callback.func(request.callback.user_data, pp_error);
1369 }
1370 
DownloadFileCompletion(PP_NaClFileInfo * file_info,PP_CompletionCallback callback,FileDownloader::Status status,base::File file,int http_status)1371 void DownloadFileCompletion(PP_NaClFileInfo* file_info,
1372                             PP_CompletionCallback callback,
1373                             FileDownloader::Status status,
1374                             base::File file,
1375                             int http_status) {
1376   int32_t pp_error = FileDownloaderToPepperError(status);
1377   if (pp_error == PP_OK) {
1378     file_info->handle = file.TakePlatformFile();
1379     file_info->token_lo = 0;
1380     file_info->token_hi = 0;
1381   }
1382   callback.func(callback.user_data, pp_error);
1383 }
1384 
DownloadFile(PP_Instance instance,const char * url,struct PP_NaClFileInfo * file_info,struct PP_CompletionCallback callback)1385 void DownloadFile(PP_Instance instance,
1386                   const char* url,
1387                   struct PP_NaClFileInfo* file_info,
1388                   struct PP_CompletionCallback callback) {
1389   CHECK(url);
1390   CHECK(file_info);
1391 
1392   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1393   DCHECK(load_manager);
1394   if (!load_manager) {
1395     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1396         FROM_HERE,
1397         base::Bind(callback.func, callback.user_data,
1398                    static_cast<int32_t>(PP_ERROR_FAILED)));
1399     return;
1400   }
1401 
1402   // Handle special PNaCl support files which are installed on the user's
1403   // machine.
1404   std::string url_string(url);
1405   if (url_string.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1406     PP_FileHandle handle = GetReadonlyPnaclFd(url);
1407     if (handle == PP_kInvalidFileHandle) {
1408       ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1409           FROM_HERE,
1410           base::Bind(callback.func, callback.user_data,
1411                      static_cast<int32_t>(PP_ERROR_FAILED)));
1412       return;
1413     }
1414     // TODO(ncbray): enable the fast loading and validation paths for this type
1415     // of file.
1416     file_info->handle = handle;
1417     file_info->token_lo = 0;
1418     file_info->token_hi = 0;
1419     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1420         FROM_HERE,
1421         base::Bind(callback.func, callback.user_data,
1422                    static_cast<int32_t>(PP_OK)));
1423     return;
1424   }
1425 
1426   // We have to ensure that this url resolves relative to the plugin base url
1427   // before downloading it.
1428   const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1429   if (!test_gurl.is_valid()) {
1430     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1431         FROM_HERE,
1432         base::Bind(callback.func, callback.user_data,
1433                    static_cast<int32_t>(PP_ERROR_FAILED)));
1434     return;
1435   }
1436 
1437   // Try the fast path for retrieving the file first.
1438   uint64_t file_token_lo = 0;
1439   uint64_t file_token_hi = 0;
1440   PP_FileHandle file_handle = OpenNaClExecutable(instance,
1441                                                  url,
1442                                                  &file_token_lo,
1443                                                  &file_token_hi);
1444   if (file_handle != PP_kInvalidFileHandle) {
1445     file_info->handle = file_handle;
1446     file_info->token_lo = file_token_lo;
1447     file_info->token_hi = file_token_hi;
1448     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1449         FROM_HERE,
1450         base::Bind(callback.func, callback.user_data,
1451                    static_cast<int32_t>(PP_OK)));
1452     return;
1453   }
1454 
1455   // The fast path didn't work, we'll fetch the file using URLLoader and write
1456   // it to local storage.
1457   base::File target_file(CreateTemporaryFile(instance));
1458   GURL gurl(url);
1459 
1460   content::PepperPluginInstance* plugin_instance =
1461       content::PepperPluginInstance::Get(instance);
1462   if (!plugin_instance) {
1463     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1464         FROM_HERE,
1465         base::Bind(callback.func, callback.user_data,
1466                    static_cast<int32_t>(PP_ERROR_FAILED)));
1467   }
1468   const blink::WebDocument& document =
1469       plugin_instance->GetContainer()->element().document();
1470   scoped_ptr<blink::WebURLLoader> url_loader(
1471       CreateWebURLLoader(document, gurl));
1472   blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1473 
1474   ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1475 
1476   // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1477   FileDownloader* file_downloader = new FileDownloader(
1478       url_loader.Pass(),
1479       target_file.Pass(),
1480       base::Bind(&DownloadFileCompletion, file_info, callback),
1481       base::Bind(&ProgressEventRateLimiter::ReportProgress,
1482                  base::Owned(tracker), url));
1483   file_downloader->Load(url_request);
1484 }
1485 
ReportSelLdrStatus(PP_Instance instance,int32_t load_status,int32_t max_status)1486 void ReportSelLdrStatus(PP_Instance instance,
1487                         int32_t load_status,
1488                         int32_t max_status) {
1489   HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1490   NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1491   DCHECK(load_manager);
1492   if (!load_manager)
1493     return;
1494 
1495   // Gather data to see if being installed changes load outcomes.
1496   const char* name = load_manager->is_installed() ?
1497       "NaCl.LoadStatus.SelLdr.InstalledApp" :
1498       "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1499   HistogramEnumerate(name, load_status, max_status);
1500 }
1501 
LogTranslateTime(const char * histogram_name,int64_t time_in_us)1502 void LogTranslateTime(const char* histogram_name,
1503                       int64_t time_in_us) {
1504   ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1505       FROM_HERE,
1506       base::Bind(&HistogramTimeTranslation,
1507                  std::string(histogram_name),
1508                  time_in_us / 1000));
1509 }
1510 
1511 const PPB_NaCl_Private nacl_interface = {
1512   &LaunchSelLdr,
1513   &StartPpapiProxy,
1514   &UrandomFD,
1515   &Are3DInterfacesDisabled,
1516   &BrokerDuplicateHandle,
1517   &GetReadonlyPnaclFd,
1518   &CreateTemporaryFile,
1519   &GetNumberOfProcessors,
1520   &PPIsNonSFIModeEnabled,
1521   &GetNexeFd,
1522   &ReportTranslationFinished,
1523   &DispatchEvent,
1524   &ReportLoadSuccess,
1525   &ReportLoadError,
1526   &ReportLoadAbort,
1527   &NexeDidCrash,
1528   &InstanceCreated,
1529   &InstanceDestroyed,
1530   &NaClDebugEnabledForURL,
1531   &GetSandboxArch,
1532   &LogToConsole,
1533   &GetNaClReadyState,
1534   &GetExitStatus,
1535   &SetExitStatus,
1536   &Vlog,
1537   &InitializePlugin,
1538   &GetNexeSize,
1539   &RequestNaClManifest,
1540   &GetManifestBaseURL,
1541   &ProcessNaClManifest,
1542   &DevInterfacesEnabled,
1543   &ManifestGetProgramURL,
1544   &ExternalManifestResolveKey,
1545   &GetPNaClResourceInfo,
1546   &GetCpuFeatureAttrs,
1547   &PostMessageToJavaScript,
1548   &DownloadNexe,
1549   &DownloadFile,
1550   &ReportSelLdrStatus,
1551   &LogTranslateTime
1552 };
1553 
1554 }  // namespace
1555 
GetNaClPrivateInterface()1556 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1557   return &nacl_interface;
1558 }
1559 
1560 }  // namespace nacl
1561