1 // Copyright (c) 2012 The Native Client 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.
6 #include "native_client/src/trusted/sel_universal/reverse_emulate.h"
15 #include "native_client/src/include/portability_io.h"
16 #include "native_client/src/public/secure_service.h"
17 #include "native_client/src/shared/platform/nacl_check.h"
18 #include "native_client/src/shared/platform/nacl_log.h"
19 #include "native_client/src/shared/platform/nacl_sync.h"
20 #include "native_client/src/shared/platform/nacl_sync_checked.h"
21 #include "native_client/src/shared/platform/nacl_sync_raii.h"
22 #include "native_client/src/shared/platform/nacl_threads.h"
23 #include "native_client/src/shared/platform/scoped_ptr_refcount.h"
24 #include "native_client/src/shared/srpc/nacl_srpc.h"
25 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
26 #include "native_client/src/trusted/nonnacl_util/launcher_factory.h"
27 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
28 #include "native_client/src/trusted/reverse_service/reverse_service.h"
29 #include "native_client/src/trusted/sel_universal/rpc_universal.h"
30 #include "native_client/src/trusted/sel_universal/srpc_helper.h"
31 #include "native_client/src/trusted/validator/nacl_file_info.h"
34 // Mock of ReverseInterface for use by nexes.
35 class ReverseEmulate : public nacl::ReverseInterface {
38 nacl::SelLdrLauncherStandaloneFactory* factory,
39 const vector<nacl::string>& prefix,
40 const vector<nacl::string>& sel_ldr_argv);
41 virtual ~ReverseEmulate();
44 virtual void StartupInitializationComplete();
47 virtual bool OpenManifestEntry(nacl::string url_key,
48 struct NaClFileInfo* info);
49 virtual void ReportCrash();
51 // The low-order 8 bits of the |exit_status| should be reported to
52 // any interested parties.
53 virtual void ReportExitStatus(int exit_status);
55 // Send a string as a PostMessage to the browser.
56 virtual void DoPostMessage(nacl::string message);
58 // Create new service runtime process and return secure command
59 // channel and untrusted application channel socket addresses.
60 virtual int CreateProcess(nacl::DescWrapper** out_sock_addr,
61 nacl::DescWrapper** out_app_addr);
63 virtual void CreateProcessFunctorResult(
64 nacl::CreateProcessFunctorInterface* functor);
66 virtual void FinalizeProcess(int32_t pid);
68 // Request quota for a write to a file.
69 virtual int64_t RequestQuotaForWrite(nacl::string file_id,
73 // covariant impl of Ref()
74 ReverseEmulate* Ref() { // down_cast
75 return reinterpret_cast<ReverseEmulate*>(RefCountBase::Ref());
79 int32_t ReserveProcessSlot();
80 void SaveToProcessSlot(int32_t pid,
81 nacl::SelLdrLauncherStandalone *launcher);
82 bool FreeProcessSlot(int32_t pid);
85 std::vector<std::pair<bool, nacl::SelLdrLauncherStandalone*> > subprocesses_;
87 nacl::SelLdrLauncherStandaloneFactory* factory_;
89 std::vector<nacl::string> prefix_;
90 std::vector<nacl::string> sel_ldr_argv_;
92 NACL_DISALLOW_COPY_AND_ASSIGN(ReverseEmulate);
97 typedef std::map<nacl::string, string> KeyToFileMap;
99 KeyToFileMap g_key_to_file;
101 nacl::scoped_ptr_refcount<nacl::ReverseService> g_reverse_service;
104 * TODO(phosek): These variables should be instance variables of Reverse
105 * Emulate. However, we cannot make them such at the moment because they're
106 * also being used by command handlers. This will require more significant
107 * redesign/refactoring.
111 NaClCondVar g_exit_cv;
116 bool ReverseEmulateInit(NaClSrpcChannel* command_channel,
117 nacl::SelLdrLauncherStandalone* launcher,
118 nacl::SelLdrLauncherStandaloneFactory* factory,
119 const std::vector<nacl::string>& prefix,
120 const std::vector<nacl::string>& sel_ldr_argv) {
121 // Do the SRPC to the command channel to set up the reverse channel.
122 // This returns a NaClDesc* containing a socket address.
123 NaClLog(1, "ReverseEmulateInit: launching reverse RPC service\n");
125 NaClSrpcResultCodes rpc_result =
126 NaClSrpcInvokeBySignature(command_channel,
127 NACL_SECURE_SERVICE_REVERSE_SETUP,
129 if (NACL_SRPC_RESULT_OK != rpc_result) {
130 NaClLog(LOG_ERROR, "ReverseEmulateInit: reverse setup failed\n");
133 // Make a nacl::DescWrapper* from the NaClDesc*
134 nacl::scoped_ptr<nacl::DescWrapper> conn_cap(launcher->WrapCleanup(h));
135 if (conn_cap == NULL) {
136 NaClLog(LOG_ERROR, "ReverseEmulateInit: reverse desc wrap failed\n");
139 // The implementation of the ReverseInterface is our emulator class.
140 nacl::scoped_ptr<ReverseEmulate> reverse_interface(new ReverseEmulate(
141 factory, prefix, sel_ldr_argv));
142 // Construct locks guarding exit status.
143 NaClXMutexCtor(&g_exit_mu);
144 NaClXCondVarCtor(&g_exit_cv);
146 // Create an instance of ReverseService, which connects to the socket
147 // address and exports the services from our emulator.
148 g_reverse_service.reset(new nacl::ReverseService(conn_cap.get(),
149 reverse_interface->Ref()));
150 if (g_reverse_service == NULL) {
151 NaClLog(LOG_ERROR, "ReverseEmulateInit: reverse service ctor failed\n");
154 // Successful creation of ReverseService took ownership of these.
155 reverse_interface.release();
157 // Starts the RPC handler for the reverse interface.
158 if (!g_reverse_service->Start()) {
159 NaClLog(LOG_ERROR, "ReverseEmulateInit: reverse service start failed\n");
165 void ReverseEmulateFini() {
166 CHECK(g_reverse_service != NULL);
167 NaClLog(1, "Waiting for service threads to exit...\n");
168 g_reverse_service->WaitForServiceThreadsToExit();
169 NaClLog(1, "...service threads done\n");
170 g_reverse_service.reset(NULL);
171 NaClMutexDtor(&g_exit_mu);
172 NaClCondVarDtor(&g_exit_cv);
175 bool HandlerReverseEmuAddManifestMapping(NaClCommandLoop* ncl,
176 const std::vector<string>& args) {
177 UNREFERENCED_PARAMETER(ncl);
178 if (args.size() < 3) {
179 NaClLog(LOG_ERROR, "not enough args\n");
182 NaClLog(1, "HandlerReverseEmulateAddManifestMapping(%s) -> %s\n",
183 args[1].c_str(), args[2].c_str());
184 // Set the mapping for the key.
185 g_key_to_file[args[1]] = args[2];
189 bool HandlerReverseEmuDumpManifestMappings(NaClCommandLoop* ncl,
190 const std::vector<string>& args) {
191 UNREFERENCED_PARAMETER(ncl);
192 if (args.size() != 1) {
193 NaClLog(LOG_ERROR, "unexpected args\n");
196 printf("ReverseEmulate manifest mappings:\n");
197 for (KeyToFileMap::iterator i = g_key_to_file.begin();
198 i != g_key_to_file.end();
200 printf("'%s': '%s'\n", i->first.c_str(), i->second.c_str());
205 bool HandlerWaitForExit(NaClCommandLoop* ncl,
206 const std::vector<string>& args) {
207 UNREFERENCED_PARAMETER(ncl);
208 UNREFERENCED_PARAMETER(args);
210 nacl::MutexLocker take(&g_exit_mu);
212 NaClXCondVarWait(&g_exit_cv, &g_exit_mu);
218 ReverseEmulate::ReverseEmulate(
219 nacl::SelLdrLauncherStandaloneFactory* factory,
220 const vector<nacl::string>& prefix,
221 const vector<nacl::string>& sel_ldr_argv)
224 sel_ldr_argv_(sel_ldr_argv) {
225 NaClLog(1, "ReverseEmulate::ReverseEmulate\n");
226 NaClXMutexCtor(&mu_);
229 ReverseEmulate::~ReverseEmulate() {
230 NaClLog(1, "ReverseEmulate::~ReverseEmulate\n");
231 for (size_t ix = 0; ix < subprocesses_.size(); ++ix) {
232 if (subprocesses_[ix].first) {
233 delete subprocesses_[ix].second;
234 subprocesses_[ix].second = NULL;
235 subprocesses_[ix].first = false;
241 void ReverseEmulate::StartupInitializationComplete() {
242 NaClLog(1, "ReverseEmulate::StartupInitializationComplete ()\n");
245 bool ReverseEmulate::OpenManifestEntry(nacl::string url_key,
246 struct NaClFileInfo* info) {
247 NaClLog(1, "ReverseEmulate::OpenManifestEntry (url_key=%s)\n",
249 memset(info, 0, sizeof(*info));
251 // Find the pathname for the key.
252 if (g_key_to_file.find(url_key) == g_key_to_file.end()) {
253 NaClLog(1, "ReverseEmulate::OpenManifestEntry: no pathname for key.\n");
256 nacl::string pathname = g_key_to_file[url_key];
257 NaClLog(1, "ReverseEmulate::OpenManifestEntry: pathname is %s.\n",
259 // TODO(ncbray): provide more information so that fast validation caching and
260 // mmaping can be enabled.
261 info->desc = OPEN(pathname.c_str(), O_RDONLY);
262 return info->desc >= 0;
265 void ReverseEmulate::ReportCrash() {
266 NaClLog(1, "ReverseEmulate::ReportCrash\n");
267 nacl::MutexLocker take(&g_exit_mu);
269 NaClXCondVarBroadcast(&g_exit_cv);
272 void ReverseEmulate::ReportExitStatus(int exit_status) {
273 NaClLog(1, "ReverseEmulate::ReportExitStatus (exit_status=%d)\n",
275 nacl::MutexLocker take(&g_exit_mu);
277 NaClXCondVarBroadcast(&g_exit_cv);
280 void ReverseEmulate::DoPostMessage(nacl::string message) {
281 NaClLog(1, "ReverseEmulate::DoPostMessage (message=%s)\n", message.c_str());
284 class CreateProcessBinder : public nacl::CreateProcessFunctorInterface {
286 CreateProcessBinder(nacl::DescWrapper** out_sock_addr,
287 nacl::DescWrapper** out_app_addr,
289 : sock_addr_(out_sock_addr)
290 , app_addr_(out_app_addr)
292 void Results(nacl::DescWrapper* res_sock_addr,
293 nacl::DescWrapper* res_app_addr,
296 *sock_addr_ = res_sock_addr;
297 *app_addr_ = res_app_addr;
305 nacl::DescWrapper** sock_addr_;
306 nacl::DescWrapper** app_addr_;
311 int ReverseEmulate::CreateProcess(nacl::DescWrapper** out_sock_addr,
312 nacl::DescWrapper** out_app_addr) {
313 NaClLog(1, "ReverseEmulate::CreateProcess)\n");
317 CreateProcessBinder binder(out_sock_addr, out_app_addr, &pid);
318 CreateProcessFunctorResult(&binder);
319 // race condition here, since we did not take a ref on *out_sock_addr etc
320 // so until the response is sent, some other thread might unref it.
322 return (pid < 0) ? pid : 0;
325 void ReverseEmulate::CreateProcessFunctorResult(
326 nacl::CreateProcessFunctorInterface* functor) {
327 NaClLog(1, "ReverseEmulate::CreateProcessFunctorResult)\n");
328 // We are passing in empty list of application arguments as the real
329 // arguments should be provided over the command channel.
330 vector<nacl::string> app_argv;
332 nacl::scoped_ptr<nacl::SelLdrLauncherStandalone> launcher(
333 factory_->MakeSelLdrLauncherStandalone());
334 if (!launcher->StartViaCommandLine(prefix_, sel_ldr_argv_, app_argv)) {
336 "ReverseEmulate::CreateProcess: failed to launch sel_ldr\n");
338 if (!launcher->ConnectBootstrapSocket()) {
340 "ReverseEmulate::CreateProcess:"
341 " failed to connect boostrap socket\n");
342 functor->Results(NULL, NULL, -NACL_ABI_EAGAIN);
346 if (!launcher->RetrieveSockAddr()) {
348 "ReverseEmulate::CreateProcess: failed to obtain socket addr\n");
349 functor->Results(NULL, NULL, -NACL_ABI_EAGAIN);
352 // We use a 2-phase allocate-then-store scheme so that the process
353 // slot does not actually hold a copy of the launcher object pointer
354 // while we might need to use launcher->secure_sock_addr() or
355 // launcher->socket_addr(). This is because otherwise the untrusted
356 // code, by guessing pid values, could invoke FinalizeProcess to
357 // cause the launcher to be deleted, causing the
358 // launcher->socket_addr() etc expressions to use deallocated
361 // This race condition is not currently a real threat. We use
362 // ReverseEmulate with sel_universal, under which we run tests. All
363 // tests are written by the NaCl team and are not malicious.
364 // However, this may change in the future, e.g., use sel_universal
365 // to analyze in-the-wild NaCl modules.
366 int32_t pid = ReserveProcessSlot();
368 functor->Results(NULL, NULL, -NACL_ABI_EAGAIN);
371 functor->Results(launcher->secure_socket_addr(),
372 launcher->socket_addr(), pid);
373 SaveToProcessSlot(pid, launcher.release());
376 void ReverseEmulate::FinalizeProcess(int32_t pid) {
377 if (!FreeProcessSlot(pid)) {
378 NaClLog(LOG_WARNING, "FinalizeProcess(%d) failed\n", pid);
382 int64_t ReverseEmulate::RequestQuotaForWrite(nacl::string file_id,
385 NaClLog(1, "ReverseEmulate::RequestQuotaForWrite (file_id=%s, offset=%"
386 NACL_PRId64 ", length=%" NACL_PRId64 ")\n", file_id.c_str(), offset,
391 int32_t ReverseEmulate::ReserveProcessSlot() {
392 nacl::MutexLocker take(&mu_);
394 if (subprocesses_.size() > INT32_MAX) {
395 return -NACL_ABI_EAGAIN;
398 int32_t container_size = static_cast<int32_t>(subprocesses_.size());
399 for (pid = 0; pid < container_size; ++pid) {
400 if (!subprocesses_[pid].first) {
404 if (pid == container_size) {
406 if (pid == INT32_MAX) {
408 return -NACL_ABI_EAGAIN;
410 subprocesses_.resize(container_size + 1);
412 subprocesses_[pid].first = true; // allocated/reserved...
413 subprocesses_[pid].second = NULL; // ... but still not yet in use
417 void ReverseEmulate::SaveToProcessSlot(
419 nacl::SelLdrLauncherStandalone *launcher) {
420 nacl::MutexLocker take(&mu_);
422 CHECK(subprocesses_[pid].first);
423 CHECK(subprocesses_[pid].second == NULL);
425 subprocesses_[pid].second = launcher;
428 bool ReverseEmulate::FreeProcessSlot(int32_t pid) {
432 nacl::MutexLocker take(&mu_);
433 CHECK(subprocesses_.size() <= INT32_MAX);
434 int32_t container_size = static_cast<int32_t>(subprocesses_.size());
435 if (pid > container_size) {
438 if (!subprocesses_[pid].first || subprocesses_[pid].second == NULL) {
441 subprocesses_[pid].first = false;
442 delete subprocesses_[pid].second;
443 subprocesses_[pid].second = NULL;