// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/c/system/thunks.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/protected_memory.h"
#include "base/memory/protected_memory_cfi.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "mojo/public/c/system/core.h"
#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN)
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/optional.h"
#include "base/scoped_native_library.h"
#include "base/threading/thread_restrictions.h"
#endif
namespace {
typedef void (*MojoGetSystemThunksFunction)(MojoSystemThunks* thunks);
#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN)
PROTECTED_MEMORY_SECTION
base::ProtectedMemory<MojoGetSystemThunksFunction> g_get_thunks;
#endif
PROTECTED_MEMORY_SECTION base::ProtectedMemory<MojoSystemThunks> g_thunks;
MojoResult NotImplemented(const char* name) {
DLOG(ERROR) << "Function 'Mojo" << name
<< "()' not supported in this version of Mojo Core.";
return MOJO_RESULT_UNIMPLEMENTED;
}
} // namespace
// Macro to verify that the thunk symbol |name| is actually present in the
// runtime version of Mojo Core that is currently in use.
#define FUNCTION_IS_IMPLEMENTED(name) \
(reinterpret_cast<uintptr_t>(static_cast<const void*>(&g_thunks->name)) - \
reinterpret_cast<uintptr_t>(static_cast<const void*>(&g_thunks)) < \
g_thunks->size)
#define INVOKE_THUNK(name, ...) \
FUNCTION_IS_IMPLEMENTED(name) \
? base::UnsanitizedCfiCall(g_thunks, &MojoSystemThunks::name)(__VA_ARGS__) \
: NotImplemented(#name)
namespace mojo {
// NOTE: This is defined within the global mojo namespace so that it can be
// referenced as a friend to base::ScopedAllowBlocking when library support is
// enabled.
class CoreLibraryInitializer {
public:
CoreLibraryInitializer(const MojoInitializeOptions* options) {
#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN)
bool application_provided_path = false;
base::Optional<base::FilePath> library_path;
if (options && options->struct_size >= sizeof(*options) &&
options->mojo_core_path) {
base::StringPiece utf8_path(options->mojo_core_path,
options->mojo_core_path_length);
library_path.emplace(base::FilePath::FromUTF8Unsafe(utf8_path));
application_provided_path = true;
} else {
auto environment = base::Environment::Create();
std::string library_path_value;
const char kLibraryPathEnvironmentVar[] = "MOJO_CORE_LIBRARY_PATH";
if (environment->GetVar(kLibraryPathEnvironmentVar, &library_path_value))
library_path = base::FilePath::FromUTF8Unsafe(library_path_value);
}
if (!library_path) {
// Default to looking for the library in the current working directory.
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
const base::FilePath::CharType kDefaultLibraryPathValue[] =
FILE_PATH_LITERAL("./libmojo_core.so");
#elif defined(OS_WIN)
const base::FilePath::CharType kDefaultLibraryPathValue[] =
FILE_PATH_LITERAL("mojo_core.dll");
#endif
library_path.emplace(kDefaultLibraryPathValue);
}
base::ScopedAllowBlocking allow_blocking;
library_.emplace(*library_path);
if (!application_provided_path) {
CHECK(library_->is_valid())
<< "Unable to load the mojo_core library. Make sure the library is "
<< "in the working directory or is correctly pointed to by the "
<< "MOJO_CORE_LIBRARY_PATH environment variable.";
} else {
CHECK(library_->is_valid())
<< "Unable to locate mojo_core library. This application expects to "
<< "find it at " << library_path->value();
}
const char kGetThunksFunctionName[] = "MojoGetSystemThunks";
{
auto writer = base::AutoWritableMemory::Create(g_get_thunks);
*g_get_thunks = reinterpret_cast<MojoGetSystemThunksFunction>(
library_->GetFunctionPointer(kGetThunksFunctionName));
}
CHECK(*g_get_thunks) << "Invalid mojo_core library: "
<< library_path->value();
DCHECK_EQ(g_thunks->size, 0u);
{
auto writer = base::AutoWritableMemory::Create(g_thunks);
g_thunks->size = sizeof(*g_thunks);
base::UnsanitizedCfiCall(g_get_thunks)(&*g_thunks);
}
CHECK_GT(g_thunks->size, 0u)
<< "Invalid mojo_core library: " << library_path->value();
#else // defined(OS_CHROMEOS) || defined(OS_LINUX)
NOTREACHED()
<< "Dynamic mojo_core loading is not supported on this platform.";
#endif // defined(OS_CHROMEOS) || defined(OS_LINUX)
}
~CoreLibraryInitializer() = default;
private:
#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN)
base::Optional<base::ScopedNativeLibrary> library_;
#endif
DISALLOW_COPY_AND_ASSIGN(CoreLibraryInitializer);
};
} // namespace mojo
extern "C" {
MojoResult MojoInitialize(const struct MojoInitializeOptions* options) {
static base::NoDestructor<mojo::CoreLibraryInitializer> initializer(options);
ALLOW_UNUSED_LOCAL(initializer);
DCHECK(g_thunks->Initialize);
return INVOKE_THUNK(Initialize, options);
}
MojoTimeTicks MojoGetTimeTicksNow() {
return INVOKE_THUNK(GetTimeTicksNow);
}
MojoResult MojoClose(MojoHandle handle) {
return INVOKE_THUNK(Close, handle);
}
MojoResult MojoQueryHandleSignalsState(
MojoHandle handle,
struct MojoHandleSignalsState* signals_state) {
return INVOKE_THUNK(QueryHandleSignalsState, handle, signals_state);
}
MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
MojoHandle* message_pipe_handle0,
MojoHandle* message_pipe_handle1) {
return INVOKE_THUNK(CreateMessagePipe, options, message_pipe_handle0,
message_pipe_handle1);
}
MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
MojoMessageHandle message_handle,
const MojoWriteMessageOptions* options) {
return INVOKE_THUNK(WriteMessage, message_pipe_handle, message_handle,
options);
}
MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
const MojoReadMessageOptions* options,
MojoMessageHandle* message_handle) {
return INVOKE_THUNK(ReadMessage, message_pipe_handle, options,
message_handle);
}
MojoResult MojoFuseMessagePipes(MojoHandle handle0,
MojoHandle handle1,
const MojoFuseMessagePipesOptions* options) {
return INVOKE_THUNK(FuseMessagePipes, handle0, handle1, options);
}
MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
MojoHandle* data_pipe_producer_handle,
MojoHandle* data_pipe_consumer_handle) {
return INVOKE_THUNK(CreateDataPipe, options, data_pipe_producer_handle,
data_pipe_consumer_handle);
}
MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
const void* elements,
uint32_t* num_elements,
const MojoWriteDataOptions* options) {
return INVOKE_THUNK(WriteData, data_pipe_producer_handle, elements,
num_elements, options);
}
MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
const MojoBeginWriteDataOptions* options,
void** buffer,
uint32_t* buffer_num_elements) {
return INVOKE_THUNK(BeginWriteData, data_pipe_producer_handle, options,
buffer, buffer_num_elements);
}
MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
uint32_t num_elements_written,
const MojoEndWriteDataOptions* options) {
return INVOKE_THUNK(EndWriteData, data_pipe_producer_handle,
num_elements_written, options);
}
MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
const MojoReadDataOptions* options,
void* elements,
uint32_t* num_elements) {
return INVOKE_THUNK(ReadData, data_pipe_consumer_handle, options, elements,
num_elements);
}
MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
const MojoBeginReadDataOptions* options,
const void** buffer,
uint32_t* buffer_num_elements) {
return INVOKE_THUNK(BeginReadData, data_pipe_consumer_handle, options, buffer,
buffer_num_elements);
}
MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
uint32_t num_elements_read,
const MojoEndReadDataOptions* options) {
return INVOKE_THUNK(EndReadData, data_pipe_consumer_handle, num_elements_read,
options);
}
MojoResult MojoCreateSharedBuffer(uint64_t num_bytes,
const MojoCreateSharedBufferOptions* options,
MojoHandle* shared_buffer_handle) {
return INVOKE_THUNK(CreateSharedBuffer, num_bytes, options,
shared_buffer_handle);
}
MojoResult MojoDuplicateBufferHandle(
MojoHandle buffer_handle,
const MojoDuplicateBufferHandleOptions* options,
MojoHandle* new_buffer_handle) {
return INVOKE_THUNK(DuplicateBufferHandle, buffer_handle, options,
new_buffer_handle);
}
MojoResult MojoMapBuffer(MojoHandle buffer_handle,
uint64_t offset,
uint64_t num_bytes,
const MojoMapBufferOptions* options,
void** buffer) {
return INVOKE_THUNK(MapBuffer, buffer_handle, offset, num_bytes, options,
buffer);
}
MojoResult MojoUnmapBuffer(void* buffer) {
return INVOKE_THUNK(UnmapBuffer, buffer);
}
MojoResult MojoGetBufferInfo(MojoHandle buffer_handle,
const MojoGetBufferInfoOptions* options,
MojoSharedBufferInfo* info) {
return INVOKE_THUNK(GetBufferInfo, buffer_handle, options, info);
}
MojoResult MojoCreateTrap(MojoTrapEventHandler handler,
const MojoCreateTrapOptions* options,
MojoHandle* trap_handle) {
return INVOKE_THUNK(CreateTrap, handler, options, trap_handle);
}
MojoResult MojoAddTrigger(MojoHandle trap_handle,
MojoHandle handle,
MojoHandleSignals signals,
MojoTriggerCondition condition,
uintptr_t context,
const MojoAddTriggerOptions* options) {
return INVOKE_THUNK(AddTrigger, trap_handle, handle, signals, condition,
context, options);
}
MojoResult MojoRemoveTrigger(MojoHandle trap_handle,
uintptr_t context,
const MojoRemoveTriggerOptions* options) {
return INVOKE_THUNK(RemoveTrigger, trap_handle, context, options);
}
MojoResult MojoArmTrap(MojoHandle trap_handle,
const MojoArmTrapOptions* options,
uint32_t* num_blocking_events,
MojoTrapEvent* blocking_events) {
return INVOKE_THUNK(ArmTrap, trap_handle, options, num_blocking_events,
blocking_events);
}
MojoResult MojoCreateMessage(const MojoCreateMessageOptions* options,
MojoMessageHandle* message) {
return INVOKE_THUNK(CreateMessage, options, message);
}
MojoResult MojoDestroyMessage(MojoMessageHandle message) {
return INVOKE_THUNK(DestroyMessage, message);
}
MojoResult MojoSerializeMessage(MojoMessageHandle message,
const MojoSerializeMessageOptions* options) {
return INVOKE_THUNK(SerializeMessage, message, options);
}
MojoResult MojoAppendMessageData(MojoMessageHandle message,
uint32_t payload_size,
const MojoHandle* handles,
uint32_t num_handles,
const MojoAppendMessageDataOptions* options,
void** buffer,
uint32_t* buffer_size) {
return INVOKE_THUNK(AppendMessageData, message, payload_size, handles,
num_handles, options, buffer, buffer_size);
}
MojoResult MojoGetMessageData(MojoMessageHandle message,
const MojoGetMessageDataOptions* options,
void** buffer,
uint32_t* num_bytes,
MojoHandle* handles,
uint32_t* num_handles) {
return INVOKE_THUNK(GetMessageData, message, options, buffer, num_bytes,
handles, num_handles);
}
MojoResult MojoSetMessageContext(MojoMessageHandle message,
uintptr_t context,
MojoMessageContextSerializer serializer,
MojoMessageContextDestructor destructor,
const MojoSetMessageContextOptions* options) {
return INVOKE_THUNK(SetMessageContext, message, context, serializer,
destructor, options);
}
MojoResult MojoGetMessageContext(MojoMessageHandle message,
const MojoGetMessageContextOptions* options,
uintptr_t* context) {
return INVOKE_THUNK(GetMessageContext, message, options, context);
}
MojoResult MojoNotifyBadMessage(MojoMessageHandle message,
const char* error,
uint32_t error_num_bytes,
const MojoNotifyBadMessageOptions* options) {
return INVOKE_THUNK(NotifyBadMessage, message, error, error_num_bytes,
options);
}
MojoResult MojoWrapPlatformHandle(const MojoPlatformHandle* platform_handle,
const MojoWrapPlatformHandleOptions* options,
MojoHandle* mojo_handle) {
return INVOKE_THUNK(WrapPlatformHandle, platform_handle, options,
mojo_handle);
}
MojoResult MojoUnwrapPlatformHandle(
MojoHandle mojo_handle,
const MojoUnwrapPlatformHandleOptions* options,
MojoPlatformHandle* platform_handle) {
return INVOKE_THUNK(UnwrapPlatformHandle, mojo_handle, options,
platform_handle);
}
MojoResult MojoWrapPlatformSharedMemoryRegion(
const struct MojoPlatformHandle* platform_handles,
uint32_t num_platform_handles,
uint64_t num_bytes,
const MojoSharedBufferGuid* guid,
MojoPlatformSharedMemoryRegionAccessMode access_mode,
const MojoWrapPlatformSharedMemoryRegionOptions* options,
MojoHandle* mojo_handle) {
return INVOKE_THUNK(WrapPlatformSharedMemoryRegion, platform_handles,
num_platform_handles, num_bytes, guid, access_mode,
options, mojo_handle);
}
MojoResult MojoUnwrapPlatformSharedMemoryRegion(
MojoHandle mojo_handle,
const MojoUnwrapPlatformSharedMemoryRegionOptions* options,
struct MojoPlatformHandle* platform_handles,
uint32_t* num_platform_handles,
uint64_t* num_bytes,
struct MojoSharedBufferGuid* guid,
MojoPlatformSharedMemoryRegionAccessMode* access_mode) {
return INVOKE_THUNK(UnwrapPlatformSharedMemoryRegion, mojo_handle, options,
platform_handles, num_platform_handles, num_bytes, guid,
access_mode);
}
MojoResult MojoCreateInvitation(const MojoCreateInvitationOptions* options,
MojoHandle* invitation_handle) {
return INVOKE_THUNK(CreateInvitation, options, invitation_handle);
}
MojoResult MojoAttachMessagePipeToInvitation(
MojoHandle invitation_handle,
const void* name,
uint32_t name_num_bytes,
const MojoAttachMessagePipeToInvitationOptions* options,
MojoHandle* message_pipe_handle) {
return INVOKE_THUNK(AttachMessagePipeToInvitation, invitation_handle, name,
name_num_bytes, options, message_pipe_handle);
}
MojoResult MojoExtractMessagePipeFromInvitation(
MojoHandle invitation_handle,
const void* name,
uint32_t name_num_bytes,
const MojoExtractMessagePipeFromInvitationOptions* options,
MojoHandle* message_pipe_handle) {
return INVOKE_THUNK(ExtractMessagePipeFromInvitation, invitation_handle, name,
name_num_bytes, options, message_pipe_handle);
}
MojoResult MojoSendInvitation(
MojoHandle invitation_handle,
const MojoPlatformProcessHandle* process_handle,
const MojoInvitationTransportEndpoint* transport_endpoint,
MojoProcessErrorHandler error_handler,
uintptr_t error_handler_context,
const MojoSendInvitationOptions* options) {
return INVOKE_THUNK(SendInvitation, invitation_handle, process_handle,
transport_endpoint, error_handler, error_handler_context,
options);
}
MojoResult MojoAcceptInvitation(
const MojoInvitationTransportEndpoint* transport_endpoint,
const MojoAcceptInvitationOptions* options,
MojoHandle* invitation_handle) {
return INVOKE_THUNK(AcceptInvitation, transport_endpoint, options,
invitation_handle);
}
MojoResult MojoSetQuota(MojoHandle handle,
MojoQuotaType type,
uint64_t limit,
const MojoSetQuotaOptions* options) {
return INVOKE_THUNK(SetQuota, handle, type, limit, options);
}
MojoResult MojoQueryQuota(MojoHandle handle,
MojoQuotaType type,
const MojoQueryQuotaOptions* options,
uint64_t* limit,
uint64_t* usage) {
return INVOKE_THUNK(QueryQuota, handle, type, options, limit, usage);
}
} // extern "C"
void MojoEmbedderSetSystemThunks(const MojoSystemThunks* thunks) {
// Assume embedders will always use matching versions of the Mojo Core and
// public APIs.
DCHECK_EQ(thunks->size, sizeof(*g_thunks));
// This should only have to check that the |g_thunks->size| is zero, but we
// have multiple Mojo Core initializations in some test suites still. For now
// we allow double calls as long as they're the same thunks as before.
DCHECK(g_thunks->size == 0 || !memcmp(&*g_thunks, thunks, sizeof(*g_thunks)))
<< "Cannot set embedder thunks after Mojo API calls have been made.";
auto writer = base::AutoWritableMemory::Create(g_thunks);
*g_thunks = *thunks;
}