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 #ifndef DISABLE_NACL 8 9 #include "base/command_line.h" 10 #include "base/lazy_instance.h" 11 #include "base/logging.h" 12 #include "base/rand_util.h" 13 #include "components/nacl/common/nacl_host_messages.h" 14 #include "components/nacl/common/nacl_types.h" 15 #include "components/nacl/renderer/pnacl_translation_resource_host.h" 16 #include "content/public/common/content_client.h" 17 #include "content/public/common/content_switches.h" 18 #include "content/public/common/sandbox_init.h" 19 #include "content/public/renderer/pepper_plugin_instance.h" 20 #include "content/public/renderer/render_thread.h" 21 #include "content/public/renderer/render_view.h" 22 #include "content/public/renderer/renderer_ppapi_host.h" 23 #include "ppapi/c/pp_bool.h" 24 #include "ppapi/c/private/pp_file_handle.h" 25 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h" 26 #include "ppapi/shared_impl/ppapi_permissions.h" 27 #include "ppapi/shared_impl/ppapi_preferences.h" 28 #include "ppapi/shared_impl/var.h" 29 #include "ppapi/thunk/enter.h" 30 #include "third_party/WebKit/public/web/WebDOMResourceProgressEvent.h" 31 #include "third_party/WebKit/public/web/WebDocument.h" 32 #include "third_party/WebKit/public/web/WebElement.h" 33 #include "third_party/WebKit/public/web/WebFrame.h" 34 #include "third_party/WebKit/public/web/WebPluginContainer.h" 35 #include "third_party/WebKit/public/web/WebView.h" 36 #include "v8/include/v8.h" 37 38 namespace { 39 40 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> > 41 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER; 42 43 static bool InitializePnaclResourceHost() { 44 // Must run on the main thread. 45 content::RenderThread* render_thread = content::RenderThread::Get(); 46 if (!render_thread) 47 return false; 48 if (!g_pnacl_resource_host.Get()) { 49 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost( 50 render_thread->GetIOMessageLoopProxy()); 51 render_thread->AddFilter(g_pnacl_resource_host.Get()); 52 } 53 return true; 54 } 55 56 struct InstanceInfo { 57 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {} 58 GURL url; 59 ppapi::PpapiPermissions permissions; 60 base::ProcessId plugin_pid; 61 int plugin_child_id; 62 IPC::ChannelHandle channel_handle; 63 }; 64 65 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap; 66 67 base::LazyInstance<InstanceInfoMap> g_instance_info = 68 LAZY_INSTANCE_INITIALIZER; 69 70 static int GetRoutingID(PP_Instance instance) { 71 // Check that we are on the main renderer thread. 72 DCHECK(content::RenderThread::Get()); 73 content::RendererPpapiHost *host = 74 content::RendererPpapiHost::GetForPPInstance(instance); 75 if (!host) 76 return 0; 77 return host->GetRoutingIDForWidget(instance); 78 } 79 80 // Launch NaCl's sel_ldr process. 81 PP_ExternalPluginResult LaunchSelLdr(PP_Instance instance, 82 const char* alleged_url, 83 PP_Bool uses_irt, 84 PP_Bool uses_ppapi, 85 PP_Bool enable_ppapi_dev, 86 PP_Bool enable_dyncode_syscalls, 87 PP_Bool enable_exception_handling, 88 PP_Bool enable_crash_throttling, 89 void* imc_handle, 90 struct PP_Var* error_message) { 91 nacl::FileDescriptor result_socket; 92 IPC::Sender* sender = content::RenderThread::Get(); 93 DCHECK(sender); 94 *error_message = PP_MakeUndefined(); 95 int routing_id = 0; 96 // If the nexe uses ppapi APIs, we need a routing ID. 97 // To get the routing ID, we must be on the main thread. 98 // Some nexes do not use ppapi and launch from the background thread, 99 // so those nexes can skip finding a routing_id. 100 if (uses_ppapi) { 101 routing_id = GetRoutingID(instance); 102 if (!routing_id) 103 return PP_EXTERNAL_PLUGIN_FAILED; 104 } 105 106 InstanceInfo instance_info; 107 instance_info.url = GURL(alleged_url); 108 109 uint32_t perm_bits = ppapi::PERMISSION_NONE; 110 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so 111 // it's clearer to developers when they are using 'Dev' inappropriately. We 112 // must also check on the trusted side of the proxy. 113 if (enable_ppapi_dev) 114 perm_bits |= ppapi::PERMISSION_DEV; 115 instance_info.permissions = 116 ppapi::PpapiPermissions::GetForCommandLine(perm_bits); 117 std::string error_message_string; 118 nacl::NaClLaunchResult launch_result; 119 120 if (!sender->Send(new NaClHostMsg_LaunchNaCl( 121 nacl::NaClLaunchParams(instance_info.url.spec(), 122 routing_id, 123 perm_bits, 124 PP_ToBool(uses_irt), 125 PP_ToBool(enable_dyncode_syscalls), 126 PP_ToBool(enable_exception_handling), 127 PP_ToBool(enable_crash_throttling)), 128 &launch_result, 129 &error_message_string))) { 130 return PP_EXTERNAL_PLUGIN_FAILED; 131 } 132 if (!error_message_string.empty()) { 133 *error_message = ppapi::StringVar::StringToPPVar(error_message_string); 134 return PP_EXTERNAL_PLUGIN_FAILED; 135 } 136 result_socket = launch_result.imc_channel_handle; 137 instance_info.channel_handle = launch_result.ipc_channel_handle; 138 instance_info.plugin_pid = launch_result.plugin_pid; 139 instance_info.plugin_child_id = launch_result.plugin_child_id; 140 // Don't save instance_info if channel handle is invalid. 141 bool invalid_handle = instance_info.channel_handle.name.empty(); 142 #if defined(OS_POSIX) 143 if (!invalid_handle) 144 invalid_handle = (instance_info.channel_handle.socket.fd == -1); 145 #endif 146 if (!invalid_handle) 147 g_instance_info.Get()[instance] = instance_info; 148 149 *(static_cast<NaClHandle*>(imc_handle)) = 150 nacl::ToNativeHandle(result_socket); 151 152 return PP_EXTERNAL_PLUGIN_OK; 153 } 154 155 PP_ExternalPluginResult StartPpapiProxy(PP_Instance instance) { 156 InstanceInfoMap& map = g_instance_info.Get(); 157 InstanceInfoMap::iterator it = map.find(instance); 158 if (it == map.end()) { 159 DLOG(ERROR) << "Could not find instance ID"; 160 return PP_EXTERNAL_PLUGIN_FAILED; 161 } 162 InstanceInfo instance_info = it->second; 163 map.erase(it); 164 165 content::PepperPluginInstance* plugin_instance = 166 content::PepperPluginInstance::Get(instance); 167 if (!plugin_instance) { 168 DLOG(ERROR) << "GetInstance() failed"; 169 return PP_EXTERNAL_PLUGIN_ERROR_MODULE; 170 } 171 172 return plugin_instance->SwitchToOutOfProcessProxy( 173 base::FilePath().AppendASCII(instance_info.url.spec()), 174 instance_info.permissions, 175 instance_info.channel_handle, 176 instance_info.plugin_pid, 177 instance_info.plugin_child_id); 178 } 179 180 int UrandomFD(void) { 181 #if defined(OS_POSIX) 182 return base::GetUrandomFD(); 183 #else 184 return -1; 185 #endif 186 } 187 188 PP_Bool Are3DInterfacesDisabled() { 189 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch( 190 switches::kDisable3DAPIs)); 191 } 192 193 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle, 194 uint32_t process_id, 195 PP_FileHandle* target_handle, 196 uint32_t desired_access, 197 uint32_t options) { 198 #if defined(OS_WIN) 199 return content::BrokerDuplicateHandle(source_handle, process_id, 200 target_handle, desired_access, 201 options); 202 #else 203 return 0; 204 #endif 205 } 206 207 PP_FileHandle GetReadonlyPnaclFD(const char* filename) { 208 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit(); 209 IPC::Sender* sender = content::RenderThread::Get(); 210 DCHECK(sender); 211 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD( 212 std::string(filename), 213 &out_fd))) { 214 return base::kInvalidPlatformFileValue; 215 } 216 if (out_fd == IPC::InvalidPlatformFileForTransit()) { 217 return base::kInvalidPlatformFileValue; 218 } 219 base::PlatformFile handle = 220 IPC::PlatformFileForTransitToPlatformFile(out_fd); 221 return handle; 222 } 223 224 PP_FileHandle CreateTemporaryFile(PP_Instance instance) { 225 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit(); 226 IPC::Sender* sender = content::RenderThread::Get(); 227 DCHECK(sender); 228 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile( 229 &transit_fd))) { 230 return base::kInvalidPlatformFileValue; 231 } 232 233 if (transit_fd == IPC::InvalidPlatformFileForTransit()) { 234 return base::kInvalidPlatformFileValue; 235 } 236 237 base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile( 238 transit_fd); 239 return handle; 240 } 241 242 int32_t GetNexeFd(PP_Instance instance, 243 const char* pexe_url, 244 uint32_t abi_version, 245 uint32_t opt_level, 246 const char* last_modified, 247 const char* etag, 248 PP_Bool has_no_store_header, 249 PP_Bool* is_hit, 250 PP_FileHandle* handle, 251 struct PP_CompletionCallback callback) { 252 ppapi::thunk::EnterInstance enter(instance, callback); 253 if (enter.failed()) 254 return enter.retval(); 255 if (!pexe_url || !last_modified || !etag || !is_hit || !handle) 256 return enter.SetResult(PP_ERROR_BADARGUMENT); 257 if (!InitializePnaclResourceHost()) 258 return enter.SetResult(PP_ERROR_FAILED); 259 260 base::Time last_modified_time; 261 // If FromString fails, it doesn't touch last_modified_time and we just send 262 // the default-constructed null value. 263 base::Time::FromString(last_modified, &last_modified_time); 264 265 nacl::PnaclCacheInfo cache_info; 266 cache_info.pexe_url = GURL(pexe_url); 267 cache_info.abi_version = abi_version; 268 cache_info.opt_level = opt_level; 269 cache_info.last_modified = last_modified_time; 270 cache_info.etag = std::string(etag); 271 cache_info.has_no_store_header = PP_ToBool(has_no_store_header); 272 273 g_pnacl_resource_host.Get()->RequestNexeFd( 274 GetRoutingID(instance), 275 instance, 276 cache_info, 277 is_hit, 278 handle, 279 enter.callback()); 280 281 return enter.SetResult(PP_OK_COMPLETIONPENDING); 282 } 283 284 void ReportTranslationFinished(PP_Instance instance, PP_Bool success) { 285 // If the resource host isn't initialized, don't try to do that here. 286 // Just return because something is already very wrong. 287 if (g_pnacl_resource_host.Get() == NULL) 288 return; 289 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success); 290 } 291 292 PP_ExternalPluginResult ReportNaClError(PP_Instance instance, 293 PP_NaClError error_id) { 294 IPC::Sender* sender = content::RenderThread::Get(); 295 296 if (!sender->Send( 297 new NaClHostMsg_NaClErrorStatus( 298 // TODO(dschuff): does this enum need to be sent as an int, 299 // or is it safe to include the appropriate headers in 300 // render_messages.h? 301 GetRoutingID(instance), static_cast<int>(error_id)))) { 302 return PP_EXTERNAL_PLUGIN_FAILED; 303 } 304 return PP_EXTERNAL_PLUGIN_OK; 305 } 306 307 PP_FileHandle OpenNaClExecutable(PP_Instance instance, 308 const char* file_url, 309 uint64_t* nonce_lo, 310 uint64_t* nonce_hi) { 311 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit(); 312 IPC::Sender* sender = content::RenderThread::Get(); 313 DCHECK(sender); 314 *nonce_lo = 0; 315 *nonce_hi = 0; 316 base::FilePath file_path; 317 if (!sender->Send( 318 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance), 319 GURL(file_url), 320 &out_fd, 321 nonce_lo, 322 nonce_hi))) { 323 return base::kInvalidPlatformFileValue; 324 } 325 326 if (out_fd == IPC::InvalidPlatformFileForTransit()) { 327 return base::kInvalidPlatformFileValue; 328 } 329 330 base::PlatformFile handle = 331 IPC::PlatformFileForTransitToPlatformFile(out_fd); 332 return handle; 333 } 334 335 blink::WebString EventTypeToString(PP_NaClEventType event_type) { 336 switch (event_type) { 337 case PP_NACL_EVENT_LOADSTART: 338 return blink::WebString::fromUTF8("loadstart"); 339 case PP_NACL_EVENT_PROGRESS: 340 return blink::WebString::fromUTF8("progress"); 341 case PP_NACL_EVENT_ERROR: 342 return blink::WebString::fromUTF8("error"); 343 case PP_NACL_EVENT_ABORT: 344 return blink::WebString::fromUTF8("abort"); 345 case PP_NACL_EVENT_LOAD: 346 return blink::WebString::fromUTF8("load"); 347 case PP_NACL_EVENT_LOADEND: 348 return blink::WebString::fromUTF8("loadend"); 349 case PP_NACL_EVENT_CRASH: 350 return blink::WebString::fromUTF8("crash"); 351 } 352 NOTIMPLEMENTED(); 353 return blink::WebString(); 354 } 355 356 void DispatchEvent(PP_Instance instance, 357 PP_NaClEventType event_type, 358 struct PP_Var resource_url, 359 PP_Bool length_is_computable, 360 uint64_t loaded_bytes, 361 uint64_t total_bytes) { 362 content::PepperPluginInstance* plugin_instance = 363 content::PepperPluginInstance::Get(instance); 364 if (!plugin_instance) { 365 NOTREACHED(); 366 return; 367 } 368 blink::WebPluginContainer* container = plugin_instance->GetContainer(); 369 // It's possible that container() is NULL if the plugin has been removed from 370 // the DOM (but the PluginInstance is not destroyed yet). 371 if (!container) 372 return; 373 blink::WebFrame* frame = container->element().document().frame(); 374 if (!frame) 375 return; 376 v8::HandleScope handle_scope(plugin_instance->GetIsolate()); 377 v8::Local<v8::Context> context( 378 plugin_instance->GetIsolate()->GetCurrentContext()); 379 if (context.IsEmpty()) { 380 // If there's no JavaScript on the stack, we have to make a new Context. 381 context = v8::Context::New(plugin_instance->GetIsolate()); 382 } 383 v8::Context::Scope context_scope(context); 384 385 ppapi::StringVar* url_var = ppapi::StringVar::FromPPVar(resource_url); 386 if (url_var) { 387 blink::WebString url_string = blink::WebString::fromUTF8( 388 url_var->value().data(), url_var->value().size()); 389 blink::WebDOMResourceProgressEvent event(EventTypeToString(event_type), 390 PP_ToBool(length_is_computable), 391 loaded_bytes, 392 total_bytes, 393 url_string); 394 container->element().dispatchEvent(event); 395 } else { 396 blink::WebDOMProgressEvent event(EventTypeToString(event_type), 397 PP_ToBool(length_is_computable), 398 loaded_bytes, 399 total_bytes); 400 container->element().dispatchEvent(event); 401 } 402 } 403 404 void SetReadOnlyProperty(PP_Instance instance, 405 struct PP_Var key, 406 struct PP_Var value) { 407 content::PepperPluginInstance* plugin_instance = 408 content::PepperPluginInstance::Get(instance); 409 plugin_instance->SetEmbedProperty(key, value); 410 } 411 412 const PPB_NaCl_Private nacl_interface = { 413 &LaunchSelLdr, 414 &StartPpapiProxy, 415 &UrandomFD, 416 &Are3DInterfacesDisabled, 417 &BrokerDuplicateHandle, 418 &GetReadonlyPnaclFD, 419 &CreateTemporaryFile, 420 &GetNexeFd, 421 &ReportTranslationFinished, 422 &ReportNaClError, 423 &OpenNaClExecutable, 424 &DispatchEvent, 425 &SetReadOnlyProperty 426 }; 427 428 } // namespace 429 430 namespace nacl { 431 432 const PPB_NaCl_Private* GetNaClPrivateInterface() { 433 return &nacl_interface; 434 } 435 436 } // namespace nacl 437 438 #endif // DISABLE_NACL 439