Index: components/nacl/browser/nacl_process_host.cc |
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc |
index 5634c6f97c4c33c3ae17fb985bb6bbe8aba8b512..f040d6279b6773f12f2ad428bc2c9e47f93160ec 100644 |
--- a/components/nacl/browser/nacl_process_host.cc |
+++ b/components/nacl/browser/nacl_process_host.cc |
@@ -194,8 +194,98 @@ void SetCloseOnExec(NaClHandle fd) { |
#endif |
} |
-void CloseFile(base::File file) { |
+void PostCloseFile(base::File file) { |
+ if (!file.IsValid()) { |
+ return; |
+ } |
+ |
// The base::File destructor will close the file for us. |
+ content::BrowserThread::GetBlockingPool()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ignore_result<base::File>, base::Passed(&file))); |
+} |
+ |
+// The maximum number of resource file handles the browser process accepts. Use |
+// 200 because ARC's nmf has ~128 resource files as of May 2015. This prevents |
+// untrusted code filling the FD/handle table. |
+const size_t kMaxPreOpenResourceFiles = 200; |
+ |
+void BatchOpenResourceFiles( |
+ const std::vector<NaClResourcePrefetchRequest>& request_list, |
+ const base::FilePath& profile_directory, |
+ base::ProcessHandle target_process, |
+ bool uses_nonsfi_mode, |
+ std::vector<NaClResourcePrefetchResult>* result_list) { |
+ if (!result_list) { |
+ // If output buffer is null, it means there is no resource to be opened. |
+ // Do nothing, then. |
+ DCHECK(request_list.empty()); |
+ return; |
+ } |
+ |
+ NaClBrowserDelegate* browser_delegate = NaClBrowser::GetDelegate(); |
+ for (size_t i = 0; |
+ i < request_list.size() && |
+ result_list->size() < kMaxPreOpenResourceFiles; |
+ ++i) { |
+ base::FilePath file_path; |
+ if (!browser_delegate->MapUrlToLocalFilePath( |
+ GURL(request_list[i].resource_url), |
+ true, // Use blocking API. |
+ profile_directory, |
+ &file_path)) { |
+ continue; |
+ } |
+ |
+ base::File file = OpenNaClReadExecImpl(file_path, true /* executable */); |
+ if (!file.IsValid()) |
+ continue; |
+ |
+ // There's no validation caching in Non-SFI mode, so |file_path_metadata| |
+ // is unnecessary, and would expose the file path to the plugin. |
+ result_list->push_back(NaClResourcePrefetchResult( |
+ IPC::TakeFileHandleForProcess(file.Pass(), target_process), |
+ uses_nonsfi_mode ? base::FilePath() : file_path, |
+ request_list[i].file_key)); |
+ } |
+} |
+ |
+base::File ReopenNexeFile( |
+ base::File original_nexe_file, base::FilePath* nexe_file_path) { |
+ // If no path is specified, use the original |nexe_file|. |
+ if (!nexe_file_path || nexe_file_path->empty()) |
+ return original_nexe_file.Pass(); |
+ |
+ // Reopen the nexe file. |
+ base::File reopened_nexe_file = OpenNaClReadExecImpl( |
+ *nexe_file_path, true /* executable */); |
+ if (!reopened_nexe_file.IsValid()) { |
+ // On fail, clear the path, which will eventually passed to the loader. |
+ nexe_file_path->clear(); |
+ return original_nexe_file.Pass(); |
+ } |
+ |
+ // Note that, the |original_nexe_file| will be closed automatically. |
+ return reopened_nexe_file.Pass(); |
+} |
+ |
+// StartNaClExecution needs file operations. This function takes it, and |
+// should run on blocking pool. |
+base::File ResolveNaClFile( |
+ base::FilePath* nexe_file_path, |
+ base::File nexe_file, |
+ const std::vector<NaClResourcePrefetchRequest>& |
+ resource_prefetch_request_list, |
+ const base::FilePath& profile_directory, |
+ base::ProcessHandle target_process, |
+ bool uses_nonsfi_mode, |
+ std::vector<NaClResourcePrefetchResult>* prefetched_resource_files) { |
+ BatchOpenResourceFiles(resource_prefetch_request_list, |
+ profile_directory, |
+ target_process, |
+ uses_nonsfi_mode, |
+ prefetched_resource_files); |
+ return ReopenNexeFile(nexe_file.Pass(), nexe_file_path); |
} |
} // namespace |
@@ -273,7 +363,8 @@ NaClProcessHost::NaClProcessHost( |
const GURL& manifest_url, |
base::File nexe_file, |
const NaClFileToken& nexe_token, |
- const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files, |
+ const std::vector<NaClResourcePrefetchRequest>& |
+ resource_prefetch_request_list, |
ppapi::PpapiPermissions permissions, |
int render_view_id, |
uint32 permission_bits, |
@@ -284,7 +375,7 @@ NaClProcessHost::NaClProcessHost( |
: manifest_url_(manifest_url), |
nexe_file_(nexe_file.Pass()), |
nexe_token_(nexe_token), |
- prefetched_resource_files_(prefetched_resource_files), |
+ resource_prefetch_request_list_(resource_prefetch_request_list), |
permissions_(permissions), |
#if defined(OS_WIN) |
process_launched_by_broker_(false), |
@@ -317,6 +408,10 @@ NaClProcessHost::NaClProcessHost( |
} |
NaClProcessHost::~NaClProcessHost() { |
+ PostCloseFile(nexe_file_.Pass()); |
+ PostCloseFile(socket_for_renderer_.Pass()); |
+ PostCloseFile(socket_for_sel_ldr_.Pass()); |
+ |
// Report exit status only if the process was successfully started. |
if (process_->GetData().handle != base::kNullProcessHandle) { |
int exit_code = 0; |
@@ -332,22 +427,6 @@ NaClProcessHost::~NaClProcessHost() { |
NaClBrowser::GetInstance()->OnProcessEnd(process_->GetData().id); |
} |
- // Note: this does not work on Windows, though we currently support this |
- // prefetching feature only on POSIX platforms, so it should be ok. |
-#if defined(OS_WIN) |
- DCHECK(prefetched_resource_files_.empty()); |
-#else |
- for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) { |
- // The process failed to launch for some reason. Close resource file |
- // handles. |
- base::File file(IPC::PlatformFileForTransitToFile( |
- prefetched_resource_files_[i].file)); |
- content::BrowserThread::GetBlockingPool()->PostTask( |
- FROM_HERE, |
- base::Bind(&CloseFile, base::Passed(file.Pass()))); |
- } |
-#endif |
- |
if (reply_msg_) { |
// The process failed to launch for some reason. |
// Don't keep the renderer hanging. |
@@ -865,6 +944,13 @@ bool NaClProcessHost::StartNaClExecution() { |
params.process_type = process_type_; |
bool enable_nacl_debug = enable_debug_stub_ && |
NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(manifest_url_); |
+#if defined(OS_MACOSX) |
+ base::ScopedFD memory_fd; |
+#endif // defined(OS_MACOSX) |
+#if defined(OS_POSIX) |
+ base::ScopedFD debug_stub_server_bound_socket; |
+#endif // defined(OS_POSIX) |
+ |
if (uses_nonsfi_mode_) { |
// Currently, non-SFI mode is supported only on Linux. |
#if defined(OS_LINUX) |
@@ -882,21 +968,7 @@ bool NaClProcessHost::StartNaClExecution() { |
params.version = NaClBrowser::GetDelegate()->GetVersionString(); |
params.enable_debug_stub = enable_nacl_debug; |
- const ChildProcessData& data = process_->GetData(); |
- params.imc_bootstrap_handle = |
- IPC::TakeFileHandleForProcess(socket_for_sel_ldr_.Pass(), data.handle); |
- if (params.imc_bootstrap_handle == IPC::InvalidPlatformFileForTransit()) { |
- return false; |
- } |
- |
- const base::File& irt_file = nacl_browser->IrtFile(); |
- CHECK(irt_file.IsValid()); |
- // Send over the IRT file handle. We don't close our own copy! |
- params.irt_handle = IPC::GetFileHandleForProcess( |
- irt_file.GetPlatformFile(), data.handle, false); |
- if (params.irt_handle == IPC::InvalidPlatformFileForTransit()) { |
- return false; |
- } |
+ CHECK(nacl_browser->IrtFile().IsValid()); |
#if defined(OS_MACOSX) |
// For dynamic loading support, NaCl requires a file descriptor that |
@@ -911,94 +983,117 @@ bool NaClProcessHost::StartNaClExecution() { |
DLOG(ERROR) << "Failed to allocate memory buffer"; |
return false; |
} |
- base::ScopedFD memory_fd(dup(memory_buffer.handle().fd)); |
+ memory_fd.reset(dup(memory_buffer.handle().fd)); |
if (!memory_fd.is_valid()) { |
DLOG(ERROR) << "Failed to dup() a file descriptor"; |
return false; |
} |
- params.mac_shm_fd = IPC::GetFileHandleForProcess( |
- memory_fd.release(), data.handle, true); |
#endif |
#if defined(OS_POSIX) |
- if (params.enable_debug_stub) { |
- net::SocketDescriptor server_bound_socket = GetDebugStubSocketHandle(); |
- if (server_bound_socket != net::kInvalidSocket) { |
- params.debug_stub_server_bound_socket = IPC::GetFileHandleForProcess( |
- server_bound_socket, data.handle, true); |
- } |
- } |
+ if (params.enable_debug_stub) |
+ debug_stub_server_bound_socket.reset(GetDebugStubSocketHandle()); |
#endif |
} |
- if (!crash_info_shmem_.ShareToProcess(process_->GetData().handle, |
- ¶ms.crash_info_shmem_handle)) { |
- DLOG(ERROR) << "Failed to ShareToProcess() a shared memory buffer"; |
- return false; |
+ // Transfer resources to |params|. |
+ // Hereafter, we should never return false, and should always send an IPC |
+ // to NaCl plugin process. Otherwise, the resources passed to |params| may |
+ // be leaked. Note that these are resources for Plugin process, so we |
+ // cannot properly release those in this (browser) process. |
+ { |
+ const ChildProcessData& data = process_->GetData(); |
+ if (!uses_nonsfi_mode_) { |
+ params.imc_bootstrap_handle = IPC::TakeFileHandleForProcess( |
+ socket_for_sel_ldr_.Pass(), data.handle); |
+ // Send over the IRT file handle. We don't close our own copy! |
+ params.irt_handle = IPC::GetFileHandleForProcess( |
+ nacl_browser->IrtFile().GetPlatformFile(), data.handle, false); |
+ |
+#if defined(OS_MACOSX) |
+ params.mac_shm_fd = IPC::GetFileHandleForProcess( |
+ memory_fd.release(), data.handle, true); |
+#endif |
+#if defined(OS_POSIX) |
+ params.debug_stub_server_bound_socket = IPC::GetFileHandleForProcess( |
+ debug_stub_server_bound_socket.release(), data.handle, true); |
+#endif |
+ } |
+ |
+ if (!crash_info_shmem_.ShareToProcess(data.handle, |
+ ¶ms.crash_info_shmem_handle)) { |
+ DLOG(ERROR) << "Failed to ShareToProcess() a shared memory buffer"; |
+ // Do not return here. |
+ } |
} |
- // Pass the pre-opened resource files to the loader. We do not have to reopen |
- // resource files here even for SFI mode because the descriptors are not from |
- // a renderer. |
- for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) { |
- process_->Send(new NaClProcessMsg_AddPrefetchedResource( |
- NaClResourcePrefetchResult( |
- prefetched_resource_files_[i].file, |
- // For the same reason as the comment below, always use an empty |
- // base::FilePath for non-SFI mode. |
- (uses_nonsfi_mode_ ? base::FilePath() : |
- prefetched_resource_files_[i].file_path_metadata), |
- prefetched_resource_files_[i].file_key))); |
+ // We have to reopen the file in the browser process; we don't want a |
+ // compromised renderer to pass an arbitrary fd that could get loaded |
+ // into the plugin process. |
+ // Don't retrieve the file path when using nonsfi mode; there's no |
+ // validation caching in that case, so it's unnecessary work, and would |
+ // expose the file path to the plugin. |
+ scoped_ptr<base::FilePath> nexe_file_path; |
+ if (!uses_nonsfi_mode_) { |
+ nexe_file_path.reset(new base::FilePath); |
+ if (!NaClBrowser::GetInstance()->GetFilePath( |
+ nexe_token_.lo, nexe_token_.hi, nexe_file_path.get())) { |
+ // Failed. Reset the pointer. |
+ nexe_file_path.reset(); |
+ } |
} |
- prefetched_resource_files_.clear(); |
- base::FilePath file_path; |
- if (uses_nonsfi_mode_) { |
- // Don't retrieve the file path when using nonsfi mode; there's no |
- // validation caching in that case, so it's unnecessary work, and would |
- // expose the file path to the plugin. |
- } else { |
- if (NaClBrowser::GetInstance()->GetFilePath(nexe_token_.lo, |
- nexe_token_.hi, |
- &file_path)) { |
- // We have to reopen the file in the browser process; we don't want a |
- // compromised renderer to pass an arbitrary fd that could get loaded |
- // into the plugin process. |
- if (base::PostTaskAndReplyWithResult( |
- content::BrowserThread::GetBlockingPool(), |
- FROM_HERE, |
- base::Bind(OpenNaClReadExecImpl, |
- file_path, |
- true /* is_executable */), |
- base::Bind(&NaClProcessHost::StartNaClFileResolved, |
- weak_factory_.GetWeakPtr(), |
- params, |
- file_path))) { |
- return true; |
- } |
+ // Pass the pre-opened resource files to the loader. |
+ scoped_ptr<std::vector<NaClResourcePrefetchResult> > |
+ prefetched_resource_files; |
+ if (!resource_prefetch_request_list_.empty()) { |
+ prefetched_resource_files.reset( |
+ new std::vector<NaClResourcePrefetchResult>); |
+ } |
+ |
+ // If file operation is necessary, run it on a blocking pool, where file |
+ // operations are allowed. |
+ if (nexe_file_path.get() || prefetched_resource_files.get()) { |
+ if (base::PostTaskAndReplyWithResult( |
+ content::BrowserThread::GetBlockingPool(), |
+ FROM_HERE, |
+ base::Bind(&ResolveNaClFile, |
+ nexe_file_path.get(), |
+ base::Passed(&nexe_file_), |
+ resource_prefetch_request_list_, |
+ profile_directory_, |
+ process_->GetData().handle, |
+ uses_nonsfi_mode_, |
+ prefetched_resource_files.get()), |
+ base::Bind(&NaClProcessHost::StartNaClExecutionAfterFileResolved, |
+ weak_factory_.GetWeakPtr(), |
+ params, |
+ base::Passed(&nexe_file_path), |
+ base::Passed(&prefetched_resource_files)))) { |
+ return true; |
} |
} |
- StartNaClFileResolved(params, base::FilePath(), base::File()); |
+ StartNaClExecutionAfterFileResolved( |
+ params, |
+ scoped_ptr<base::FilePath>(), |
+ scoped_ptr<std::vector<NaClResourcePrefetchResult> >(), |
+ nexe_file_.Pass()); |
return true; |
} |
-void NaClProcessHost::StartNaClFileResolved( |
+void NaClProcessHost::StartNaClExecutionAfterFileResolved( |
NaClStartParams params, |
- const base::FilePath& file_path, |
- base::File checked_nexe_file) { |
- if (checked_nexe_file.IsValid()) { |
- // Release the file received from the renderer. This has to be done on a |
- // thread where IO is permitted, though. |
- content::BrowserThread::GetBlockingPool()->PostTask( |
- FROM_HERE, |
- base::Bind(&CloseFile, base::Passed(nexe_file_.Pass()))); |
- params.nexe_file_path_metadata = file_path; |
- params.nexe_file = IPC::TakeFileHandleForProcess( |
- checked_nexe_file.Pass(), process_->GetData().handle); |
- } else { |
- params.nexe_file = IPC::TakeFileHandleForProcess( |
- nexe_file_.Pass(), process_->GetData().handle); |
+ scoped_ptr<base::FilePath> nexe_file_path, |
+ scoped_ptr<std::vector<NaClResourcePrefetchResult> > |
+ prefetched_resource_files, |
+ base::File nexe_file) { |
+ // Pass the pre-opened resource files to the loader. |
+ if (prefetched_resource_files.get()) { |
+ for (size_t i = 0; i < prefetched_resource_files->size(); ++i) { |
+ process_->Send(new NaClProcessMsg_AddPrefetchedResource( |
+ (*prefetched_resource_files)[i])); |
+ } |
} |
#if defined(OS_LINUX) |
@@ -1063,6 +1158,13 @@ void NaClProcessHost::StartNaClFileResolved( |
} |
#endif |
+ // Pass the nexe file and its path to params. |
+ params.nexe_file = IPC::TakeFileHandleForProcess( |
+ nexe_file.Pass(), process_->GetData().handle); |
+ if (nexe_file_path.get()) { |
+ params.nexe_file_path_metadata = *nexe_file_path; |
+ } |
+ |
process_->Send(new NaClProcessMsg_Start(params)); |
} |