1 // Copyright 2018 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 <stddef.h>
6 
7 #include "base/at_exit.h"
8 #include "base/base_switches.h"
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/debug/stack_trace.h"
12 #include "base/feature_list.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/message_loop/message_pump_type.h"
16 #include "base/no_destructor.h"
17 #include "base/rand_util.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/threading/thread.h"
20 #include "base/time/time.h"
21 #include "build/build_config.h"
22 #include "mojo/core/configuration.h"
23 #include "mojo/core/core.h"
24 #include "mojo/core/entrypoints.h"
25 #include "mojo/public/c/system/core.h"
26 #include "mojo/public/c/system/macros.h"
27 #include "mojo/public/c/system/thunks.h"
28 
29 namespace {
30 
31 class IPCSupport {
32  public:
IPCSupport()33   IPCSupport() : ipc_thread_("Mojo IPC") {
34     base::Thread::Options options(base::MessagePumpType::IO, 0);
35     ipc_thread_.StartWithOptions(options);
36     mojo::core::Core::Get()->SetIOTaskRunner(ipc_thread_.task_runner());
37   }
38 
~IPCSupport()39   ~IPCSupport() {
40     base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
41                              base::WaitableEvent::InitialState::NOT_SIGNALED);
42     mojo::core::Core::Get()->RequestShutdown(
43         base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&wait)));
44     wait.Wait();
45   }
46 
47  private:
48 #if !defined(COMPONENT_BUILD)
49   // NOTE: For component builds, we assume the consumer is always a target in
50   // the Chromium tree which already depends on base initialization stuff and
51   // therefore already has an AtExitManager. For non-component builds, use of
52   // this AtExitManager is strictly isolated to Mojo Core internals, so running
53   // hooks on |MojoShutdown()| (where |this| is destroyed) makes sense.
54   base::AtExitManager at_exit_manager_;
55 #endif  // !defined(COMPONENT_BUILD)
56 
57   base::Thread ipc_thread_;
58 
59   DISALLOW_COPY_AND_ASSIGN(IPCSupport);
60 };
61 
GetIPCSupport()62 std::unique_ptr<IPCSupport>& GetIPCSupport() {
63   static base::NoDestructor<std::unique_ptr<IPCSupport>> state;
64   return *state;
65 }
66 
67 // This helper is only called from within the context of a newly loaded Mojo
68 // Core shared library, where various bits of static state (e.g. //base globals)
69 // will not yet be initialized. Base library initialization steps are thus
70 // consolidated here so that base APIs work as expected from within the loaded
71 // Mojo Core implementation.
72 //
73 // NOTE: This is a no-op in component builds, as we expect both the client
74 // application and the Mojo Core library to have been linked against the same
75 // base component library, and we furthermore expect that the client application
76 // has already initialized base globals by this point.
77 class GlobalStateInitializer {
78  public:
79   GlobalStateInitializer() = default;
80 
Initialize(int argc,const char * const * argv)81   bool Initialize(int argc, const char* const* argv) {
82     if (initialized_)
83       return false;
84     initialized_ = true;
85 #if !defined(COMPONENT_BUILD)
86     base::CommandLine::Init(argc, argv);
87 
88     logging::LoggingSettings settings;
89     settings.logging_dest =
90         logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
91     logging::InitLogging(settings);
92     logging::SetLogItems(true,   // Process ID
93                          true,   // Thread ID
94                          true,   // Timestamp
95                          true);  // Tick count
96 
97 #if !defined(OFFICIAL_BUILD) && !defined(OS_WIN)
98     // Correct stack dumping behavior requires symbol names in all loaded
99     // libraries to be cached. We do this here in case the calling process will
100     // imminently enter a sandbox.
101     base::debug::EnableInProcessStackDumping();
102 #endif
103 
104 #if defined(OS_POSIX)
105     // Tickle base's PRNG. This lazily opens a static handle to /dev/urandom.
106     // Mojo Core uses the API internally, so it's important to warm the handle
107     // before potentially entering a sandbox.
108     base::RandUint64();
109 #endif
110 
111     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
112     base::FeatureList::InitializeInstance(
113         command_line->GetSwitchValueASCII(switches::kEnableFeatures),
114         command_line->GetSwitchValueASCII(switches::kDisableFeatures));
115 #endif  // !defined(COMPONENT_BUILD)
116     return true;
117   }
118 
119  private:
120   bool initialized_ = false;
121 };
122 
123 }  // namespace
124 
125 extern "C" {
126 
127 namespace {
128 
InitializeImpl(const struct MojoInitializeOptions * options)129 MojoResult InitializeImpl(const struct MojoInitializeOptions* options) {
130   std::unique_ptr<IPCSupport>& ipc_support = GetIPCSupport();
131   if (ipc_support) {
132     // Already fully initialized, so there's nothing to do.
133     return MOJO_RESULT_FAILED_PRECONDITION;
134   }
135 
136   // NOTE: |MojoInitialize()| may be called more than once if the caller wishes
137   // to separate basic initialization from IPC support initialization. We only
138   // do basic initialization the first time this is called.
139   const bool should_initialize_ipc_support =
140       !options || ((options->flags & MOJO_INITIALIZE_FLAG_LOAD_ONLY) == 0);
141 
142   int argc = 0;
143   const char* const* argv = nullptr;
144   if (options && MOJO_IS_STRUCT_FIELD_PRESENT(options, argv)) {
145     argc = options->argc;
146     argv = options->argv;
147   }
148 
149   static base::NoDestructor<GlobalStateInitializer> global_state_initializer;
150   const bool was_global_state_already_initialized =
151       !global_state_initializer->Initialize(argc, argv);
152 
153   if (!should_initialize_ipc_support) {
154     if (was_global_state_already_initialized)
155       return MOJO_RESULT_ALREADY_EXISTS;
156     else
157       return MOJO_RESULT_OK;
158   }
159 
160   DCHECK(!mojo::core::Core::Get());
161   mojo::core::Configuration config;
162   config.is_broker_process =
163       options && options->flags & MOJO_INITIALIZE_FLAG_AS_BROKER;
164   config.force_direct_shared_memory_allocation =
165       options && options->flags &
166                      MOJO_INITIALIZE_FLAG_FORCE_DIRECT_SHARED_MEMORY_ALLOCATION;
167   mojo::core::internal::g_configuration = config;
168   mojo::core::InitializeCore();
169   ipc_support = std::make_unique<IPCSupport>();
170 
171   return MOJO_RESULT_OK;
172 }
173 
ShutdownImpl(const struct MojoShutdownOptions * options)174 MojoResult ShutdownImpl(const struct MojoShutdownOptions* options) {
175   if (options && options->struct_size < sizeof(*options))
176     return MOJO_RESULT_INVALID_ARGUMENT;
177 
178   std::unique_ptr<IPCSupport>& ipc_support = GetIPCSupport();
179   if (!ipc_support)
180     return MOJO_RESULT_FAILED_PRECONDITION;
181 
182   ipc_support.reset();
183   return MOJO_RESULT_OK;
184 }
185 
186 MojoSystemThunks g_thunks = {0};
187 
188 }  // namespace
189 
190 #if defined(WIN32)
191 #define EXPORT_FROM_MOJO_CORE __declspec(dllexport)
192 #else
193 #define EXPORT_FROM_MOJO_CORE __attribute__((visibility("default")))
194 #endif
195 
MojoGetSystemThunks(MojoSystemThunks * thunks)196 EXPORT_FROM_MOJO_CORE void MojoGetSystemThunks(MojoSystemThunks* thunks) {
197   if (!g_thunks.size) {
198     g_thunks = mojo::core::GetSystemThunks();
199     g_thunks.Initialize = InitializeImpl;
200     g_thunks.Shutdown = ShutdownImpl;
201   }
202 
203   // Caller must provide a thunk structure at least large enough to hold Core
204   // ABI version 0. SetQuota is the first function introduced in ABI version 1.
205   CHECK_GE(thunks->size, offsetof(MojoSystemThunks, SetQuota));
206 
207   // NOTE: This also overrites |thunks->size| with the actual size of our own
208   // thunks if smaller than the caller's. This informs the caller that we
209   // implement an older version of the ABI.
210   if (thunks->size > g_thunks.size)
211     thunks->size = g_thunks.size;
212   memcpy(thunks, &g_thunks, thunks->size);
213 }
214 
215 }  // extern "C"
216