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