nacl_listener.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. #include "components/nacl/loader/nacl_listener.h"
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <memory>
  10. #include <utility>
  11. #include "base/bind.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "build/build_config.h"
  14. #if BUILDFLAG(IS_POSIX)
  15. #include <unistd.h>
  16. #endif
  17. #include "base/command_line.h"
  18. #include "base/logging.h"
  19. #include "base/memory/read_only_shared_memory_region.h"
  20. #include "base/message_loop/message_pump_type.h"
  21. #include "base/rand_util.h"
  22. #include "base/run_loop.h"
  23. #include "base/synchronization/waitable_event.h"
  24. #include "base/task/single_thread_task_runner.h"
  25. #include "base/threading/thread_task_runner_handle.h"
  26. #include "build/build_config.h"
  27. #include "components/nacl/common/nacl.mojom.h"
  28. #include "components/nacl/common/nacl_messages.h"
  29. #include "components/nacl/common/nacl_service.h"
  30. #include "components/nacl/common/nacl_switches.h"
  31. #include "components/nacl/loader/nacl_ipc_adapter.h"
  32. #include "components/nacl/loader/nacl_validation_db.h"
  33. #include "components/nacl/loader/nacl_validation_query.h"
  34. #include "ipc/ipc_channel_handle.h"
  35. #include "ipc/ipc_sync_channel.h"
  36. #include "ipc/ipc_sync_message_filter.h"
  37. #include "mojo/public/cpp/bindings/pending_remote.h"
  38. #include "native_client/src/public/chrome_main.h"
  39. #include "native_client/src/public/nacl_app.h"
  40. #include "native_client/src/public/nacl_desc.h"
  41. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  42. #include "content/public/common/zygote/sandbox_support_linux.h"
  43. #endif
  44. #if BUILDFLAG(IS_POSIX)
  45. #include "base/posix/eintr_wrapper.h"
  46. #endif
  47. #if BUILDFLAG(IS_WIN)
  48. #include <io.h>
  49. #endif
  50. namespace {
  51. NaClListener* g_listener;
  52. void FatalLogHandler(const char* data, size_t bytes) {
  53. // We use uint32_t rather than size_t for the case when the browser and NaCl
  54. // processes are a mix of 32-bit and 64-bit processes.
  55. uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes),
  56. nacl::kNaClCrashInfoMaxLogSize);
  57. // We copy the length of the crash data to the start of the shared memory
  58. // segment so we know how much to copy.
  59. memcpy(g_listener->crash_info_shmem_memory(), &copy_bytes, sizeof(uint32_t));
  60. memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t),
  61. data,
  62. copy_bytes);
  63. }
  64. void LoadStatusCallback(int load_status) {
  65. g_listener->trusted_listener()->renderer_host()->ReportLoadStatus(
  66. static_cast<NaClErrorCode>(load_status));
  67. }
  68. #if BUILDFLAG(IS_WIN)
  69. int AttachDebugExceptionHandler(const void* info, size_t info_size) {
  70. std::string info_string(reinterpret_cast<const char*>(info), info_size);
  71. bool result = false;
  72. if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
  73. info_string, &result)))
  74. return false;
  75. return result;
  76. }
  77. void DebugStubPortSelectedHandler(uint16_t port) {
  78. g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
  79. }
  80. #endif
  81. // Creates the PPAPI IPC channel between the NaCl IRT and the host
  82. // (browser/renderer) process, and starts to listen it on the thread where
  83. // the given task runner runs.
  84. // Also, creates and sets the corresponding NaClDesc to the given nap with
  85. // the FD #.
  86. void SetUpIPCAdapter(
  87. IPC::ChannelHandle* handle,
  88. scoped_refptr<base::SingleThreadTaskRunner> task_runner,
  89. struct NaClApp* nap,
  90. int nacl_fd,
  91. NaClIPCAdapter::ResolveFileTokenCallback resolve_file_token_cb,
  92. NaClIPCAdapter::OpenResourceCallback open_resource_cb) {
  93. mojo::MessagePipe pipe;
  94. scoped_refptr<NaClIPCAdapter> ipc_adapter(new NaClIPCAdapter(
  95. pipe.handle0.release(), task_runner, std::move(resolve_file_token_cb),
  96. std::move(open_resource_cb)));
  97. ipc_adapter->ConnectChannel();
  98. *handle = pipe.handle1.release();
  99. // Pass a NaClDesc to the untrusted side. This will hold a ref to the
  100. // NaClIPCAdapter.
  101. NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
  102. }
  103. } // namespace
  104. class BrowserValidationDBProxy : public NaClValidationDB {
  105. public:
  106. explicit BrowserValidationDBProxy(NaClListener* listener)
  107. : listener_(listener) {
  108. }
  109. bool QueryKnownToValidate(const std::string& signature) override {
  110. // Initialize to false so that if the Send fails to write to the return
  111. // value we're safe. For example if the message is (for some reason)
  112. // dispatched as an async message the return parameter will not be written.
  113. bool result = false;
  114. if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
  115. &result))) {
  116. LOG(ERROR) << "Failed to query NaCl validation cache.";
  117. result = false;
  118. }
  119. return result;
  120. }
  121. void SetKnownToValidate(const std::string& signature) override {
  122. // Caching is optional: NaCl will still work correctly if the IPC fails.
  123. if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
  124. LOG(ERROR) << "Failed to update NaCl validation cache.";
  125. }
  126. }
  127. private:
  128. // The listener never dies, otherwise this might be a dangling reference.
  129. raw_ptr<NaClListener> listener_;
  130. };
  131. NaClListener::NaClListener()
  132. : shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
  133. base::WaitableEvent::InitialState::NOT_SIGNALED),
  134. io_thread_("NaCl_IOThread"),
  135. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  136. prereserved_sandbox_size_(0),
  137. #endif
  138. #if BUILDFLAG(IS_POSIX)
  139. number_of_cores_(-1), // unknown/error
  140. #endif
  141. is_started_(false) {
  142. io_thread_.StartWithOptions(
  143. base::Thread::Options(base::MessagePumpType::IO, 0));
  144. DCHECK(g_listener == NULL);
  145. g_listener = this;
  146. }
  147. NaClListener::~NaClListener() {
  148. NOTREACHED();
  149. shutdown_event_.Signal();
  150. g_listener = NULL;
  151. }
  152. bool NaClListener::Send(IPC::Message* msg) {
  153. DCHECK(!!main_task_runner_);
  154. if (main_task_runner_->BelongsToCurrentThread()) {
  155. // This thread owns the channel.
  156. return channel_->Send(msg);
  157. }
  158. // This thread does not own the channel.
  159. return filter_->Send(msg);
  160. }
  161. // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be
  162. // processed in a MessageFilter so it can be handled on the IO thread.
  163. // The main thread used by NaClListener is busy in
  164. // NaClChromeMainAppStart(), so it can't be used for servicing messages.
  165. class FileTokenMessageFilter : public IPC::MessageFilter {
  166. public:
  167. bool OnMessageReceived(const IPC::Message& msg) override {
  168. bool handled = true;
  169. IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg)
  170. IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenReply,
  171. OnResolveFileTokenReply)
  172. IPC_MESSAGE_UNHANDLED(handled = false)
  173. IPC_END_MESSAGE_MAP()
  174. return handled;
  175. }
  176. void OnResolveFileTokenReply(
  177. uint64_t token_lo,
  178. uint64_t token_hi,
  179. IPC::PlatformFileForTransit ipc_fd,
  180. base::FilePath file_path) {
  181. CHECK(g_listener);
  182. g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path);
  183. }
  184. private:
  185. ~FileTokenMessageFilter() override {}
  186. };
  187. void NaClListener::Listen() {
  188. NaClService service(io_thread_.task_runner());
  189. channel_ = IPC::SyncChannel::Create(this, io_thread_.task_runner().get(),
  190. base::ThreadTaskRunnerHandle::Get(),
  191. &shutdown_event_);
  192. filter_ = channel_->CreateSyncMessageFilter();
  193. channel_->AddFilter(new FileTokenMessageFilter());
  194. channel_->Init(service.TakeChannelPipe().release(), IPC::Channel::MODE_CLIENT,
  195. true);
  196. main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
  197. base::RunLoop().Run();
  198. }
  199. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  200. // static
  201. int NaClListener::MakeSharedMemorySegment(size_t length, int executable) {
  202. return content::SharedMemoryIPCSupport::MakeSharedMemorySegment(length,
  203. executable);
  204. }
  205. #endif
  206. bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
  207. bool handled = true;
  208. IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
  209. IPC_MESSAGE_HANDLER(NaClProcessMsg_AddPrefetchedResource,
  210. OnAddPrefetchedResource)
  211. IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
  212. IPC_MESSAGE_UNHANDLED(handled = false)
  213. IPC_END_MESSAGE_MAP()
  214. return handled;
  215. }
  216. bool NaClListener::OnOpenResource(
  217. const IPC::Message& msg,
  218. const std::string& key,
  219. NaClIPCAdapter::OpenResourceReplyCallback cb) {
  220. // This callback is executed only on |io_thread_| with NaClIPCAdapter's
  221. // |lock_| not being held.
  222. DCHECK(!cb.is_null());
  223. auto it = prefetched_resource_files_.find(key);
  224. if (it != prefetched_resource_files_.end()) {
  225. // Fast path for prefetched FDs.
  226. IPC::PlatformFileForTransit file = it->second.first;
  227. base::FilePath path = it->second.second;
  228. prefetched_resource_files_.erase(it);
  229. // A pre-opened resource descriptor is available. Run the reply callback
  230. // and return true.
  231. std::move(cb).Run(msg, file, path);
  232. return true;
  233. }
  234. // Return false to fall back to the slow path. Let NaClIPCAdapter issue an
  235. // IPC to the renderer.
  236. return false;
  237. }
  238. void NaClListener::OnAddPrefetchedResource(
  239. const nacl::NaClResourcePrefetchResult& prefetched_resource_file) {
  240. DCHECK(!is_started_);
  241. if (is_started_)
  242. return;
  243. bool result = prefetched_resource_files_.insert(std::make_pair(
  244. prefetched_resource_file.file_key,
  245. std::make_pair(
  246. prefetched_resource_file.file,
  247. prefetched_resource_file.file_path_metadata))).second;
  248. if (!result) {
  249. LOG(FATAL) << "Duplicated open_resource key: "
  250. << prefetched_resource_file.file_key;
  251. }
  252. }
  253. void NaClListener::OnStart(nacl::NaClStartParams params) {
  254. is_started_ = true;
  255. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE)
  256. int urandom_fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
  257. if (urandom_fd < 0) {
  258. LOG(FATAL) << "Failed to dup() the urandom FD";
  259. }
  260. NaClChromeMainSetUrandomFd(urandom_fd);
  261. #endif
  262. struct NaClApp* nap = NULL;
  263. NaClChromeMainInit();
  264. CHECK(params.crash_info_shmem_region.IsValid());
  265. crash_info_shmem_mapping_ = params.crash_info_shmem_region.Map();
  266. base::ReadOnlySharedMemoryRegion ro_shmem_region =
  267. base::WritableSharedMemoryRegion::ConvertToReadOnly(
  268. std::move(params.crash_info_shmem_region));
  269. CHECK(crash_info_shmem_mapping_.IsValid());
  270. CHECK(ro_shmem_region.IsValid());
  271. NaClSetFatalErrorCallback(&FatalLogHandler);
  272. nap = NaClAppCreate();
  273. if (nap == NULL) {
  274. LOG(FATAL) << "NaClAppCreate() failed";
  275. }
  276. IPC::ChannelHandle browser_handle;
  277. IPC::ChannelHandle ppapi_renderer_handle;
  278. IPC::ChannelHandle manifest_service_handle;
  279. // Create the PPAPI IPC channels between the NaCl IRT and the host
  280. // (browser/renderer) processes. The IRT uses these channels to
  281. // communicate with the host and to initialize the IPC dispatchers.
  282. SetUpIPCAdapter(&browser_handle, io_thread_.task_runner(), nap,
  283. NACL_CHROME_DESC_BASE,
  284. NaClIPCAdapter::ResolveFileTokenCallback(),
  285. NaClIPCAdapter::OpenResourceCallback());
  286. SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.task_runner(), nap,
  287. NACL_CHROME_DESC_BASE + 1,
  288. NaClIPCAdapter::ResolveFileTokenCallback(),
  289. NaClIPCAdapter::OpenResourceCallback());
  290. SetUpIPCAdapter(&manifest_service_handle, io_thread_.task_runner(), nap,
  291. NACL_CHROME_DESC_BASE + 2,
  292. base::BindRepeating(&NaClListener::ResolveFileToken,
  293. base::Unretained(this)),
  294. base::BindRepeating(&NaClListener::OnOpenResource,
  295. base::Unretained(this)));
  296. mojo::PendingRemote<nacl::mojom::NaClRendererHost> renderer_host;
  297. if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
  298. browser_handle, ppapi_renderer_handle,
  299. renderer_host.InitWithNewPipeAndPassReceiver().PassPipe().release(),
  300. manifest_service_handle, ro_shmem_region)))
  301. LOG(FATAL) << "Failed to send IPC channel handle to NaClProcessHost.";
  302. trusted_listener_ = std::make_unique<NaClTrustedListener>(
  303. std::move(renderer_host), io_thread_.task_runner().get());
  304. struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
  305. if (args == NULL) {
  306. LOG(FATAL) << "NaClChromeMainArgsCreate() failed";
  307. }
  308. #if BUILDFLAG(IS_POSIX)
  309. args->number_of_cores = number_of_cores_;
  310. #endif
  311. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  312. args->create_memory_object_func = &MakeSharedMemorySegment;
  313. #endif
  314. DCHECK(params.process_type != nacl::kUnknownNaClProcessType);
  315. CHECK(params.irt_handle != IPC::InvalidPlatformFileForTransit());
  316. base::PlatformFile irt_handle =
  317. IPC::PlatformFileForTransitToPlatformFile(params.irt_handle);
  318. #if BUILDFLAG(IS_WIN)
  319. args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
  320. _O_RDONLY | _O_BINARY);
  321. if (args->irt_fd < 0) {
  322. LOG(FATAL) << "_open_osfhandle() failed";
  323. }
  324. #else
  325. args->irt_fd = irt_handle;
  326. #endif
  327. if (params.validation_cache_enabled) {
  328. // SHA256 block size.
  329. CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
  330. // The cache structure is not freed and exists until the NaCl process exits.
  331. args->validation_cache = CreateValidationCache(
  332. new BrowserValidationDBProxy(this), params.validation_cache_key,
  333. params.version);
  334. }
  335. args->enable_debug_stub = params.enable_debug_stub;
  336. // Now configure parts that depend on process type.
  337. // Start with stricter settings.
  338. args->enable_exception_handling = 0;
  339. args->enable_dyncode_syscalls = 0;
  340. // pnacl_mode=1 mostly disables things (IRT interfaces and syscalls).
  341. args->pnacl_mode = 1;
  342. // Bound the initial nexe's code segment size under PNaCl to reduce the
  343. // chance of a code spraying attack succeeding (see
  344. // https://code.google.com/p/nativeclient/issues/detail?id=3572).
  345. // We can't apply this arbitrary limit outside of PNaCl because it might
  346. // break existing NaCl apps, and this limit is only useful if the dyncode
  347. // syscalls are disabled.
  348. args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB.
  349. if (params.process_type == nacl::kNativeNaClProcessType) {
  350. args->enable_exception_handling = 1;
  351. args->enable_dyncode_syscalls = 1;
  352. args->pnacl_mode = 0;
  353. args->initial_nexe_max_code_bytes = 0;
  354. } else if (params.process_type == nacl::kPNaClTranslatorProcessType) {
  355. args->pnacl_mode = 0;
  356. }
  357. #if BUILDFLAG(IS_POSIX)
  358. args->debug_stub_server_bound_socket_fd =
  359. IPC::PlatformFileForTransitToPlatformFile(
  360. params.debug_stub_server_bound_socket);
  361. #endif
  362. #if BUILDFLAG(IS_WIN)
  363. args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
  364. args->debug_stub_server_port_selected_handler_func =
  365. DebugStubPortSelectedHandler;
  366. #endif
  367. args->load_status_handler_func = LoadStatusCallback;
  368. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  369. args->prereserved_sandbox_size = prereserved_sandbox_size_;
  370. #endif
  371. base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
  372. params.nexe_file);
  373. std::string file_path_str = params.nexe_file_path_metadata.AsUTF8Unsafe();
  374. args->nexe_desc = NaClDescCreateWithFilePathMetadata(nexe_file,
  375. file_path_str.c_str());
  376. int exit_status;
  377. if (!NaClChromeMainStart(nap, args, &exit_status))
  378. NaClExit(1);
  379. // Report the plugin's exit status if the application started successfully.
  380. trusted_listener_->renderer_host()->ReportExitStatus(exit_status);
  381. NaClExit(exit_status);
  382. }
  383. void NaClListener::ResolveFileToken(
  384. uint64_t token_lo,
  385. uint64_t token_hi,
  386. NaClIPCAdapter::ResolveFileTokenReplyCallback cb) {
  387. if (!Send(new NaClProcessMsg_ResolveFileToken(token_lo, token_hi))) {
  388. std::move(cb).Run(IPC::PlatformFileForTransit(), base::FilePath());
  389. return;
  390. }
  391. resolved_cb_ = std::move(cb);
  392. }
  393. void NaClListener::OnFileTokenResolved(
  394. uint64_t token_lo,
  395. uint64_t token_hi,
  396. IPC::PlatformFileForTransit ipc_fd,
  397. base::FilePath file_path) {
  398. if (resolved_cb_)
  399. std::move(resolved_cb_).Run(ipc_fd, file_path);
  400. }