Skip to content

Commit

Permalink
ipcz: OS primitives for reference drivers
Browse files Browse the repository at this point in the history
In preparation for landing an async multiprocess reference driver, this
lands some foundational primitives first. These are essentially cheesier
versions of what we find in Chromium for scoped handles and shared
memory.

As with all reference driver code, these are not intended for production
use, but for documentation and use in tests.

Bug: 1299283
Change-Id: I18357f3401073b2c4d4f9711737ab6a67ec64e43
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3648395
Commit-Queue: Ken Rockot <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1005512}
NOKEYCHECK=True
GitOrigin-RevId: a0ce43fb810d0f706b921663ca7b8793c1bb4e1e
  • Loading branch information
krockot authored and copybara-github committed May 20, 2022
1 parent f3ee75f commit fc046c5
Show file tree
Hide file tree
Showing 18 changed files with 957 additions and 0 deletions.
59 changes: 59 additions & 0 deletions ipcz/src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import("//build_overrides/ipcz.gni")
import("//testing/test.gni")

# ipcz will not implement a multiprocess reference driver or its supporting
# primitives for iOS or NaCl platforms. Note that this only affects ipcz tests.
enable_ipcz_multiprocess_test_support = !is_ios && !is_nacl

shared_library("ipcz_shared") {
output_name = "ipcz"
sources = [
Expand Down Expand Up @@ -126,6 +130,8 @@ ipcz_source_set("ipcz") {
}

ipcz_source_set("reference_drivers") {
testonly = true

public = [ "reference_drivers/single_process_reference_driver.h" ]

sources = [
Expand All @@ -136,9 +142,58 @@ ipcz_source_set("reference_drivers") {
"reference_drivers/single_process_reference_driver.cc",
]

if (enable_ipcz_multiprocess_test_support) {
public += [
"reference_drivers/memory.h",
"reference_drivers/os_handle.h",
]
sources += [ "reference_drivers/memory.cc" ]

if (is_android) {
sources += [
"reference_drivers/memory_android.cc",
"reference_drivers/os_handle_posix.cc",
"reference_drivers/os_handle_posix.h",
]
} else if (is_mac) {
sources += [
"reference_drivers/memory_mac.cc",
"reference_drivers/os_handle_mac.cc",
"reference_drivers/os_handle_mac.h",
]
} else if (is_win) {
sources += [
"reference_drivers/memory_win.cc",
"reference_drivers/os_handle_win.cc",
"reference_drivers/os_handle_win.h",
]
} else if (is_fuchsia) {
sources += [
"reference_drivers/memory_fuchsia.cc",
"reference_drivers/os_handle_fuchsia.cc",
"reference_drivers/os_handle_fuchsia.h",
]
} else if (is_posix) {
sources += [
"reference_drivers/memory_posix.cc",
"reference_drivers/os_handle_posix.cc",
"reference_drivers/os_handle_posix.h",
]
}
}

ipcz_deps = [ ":util" ]
public_deps = [ ":ipcz_header" ]
configs = [ ":ipcz_include_src_dir" ]

deps = []
if (is_fuchsia) {
public_deps += [ "//third_party/fuchsia-sdk/sdk/pkg/zx" ]
}

if (is_android) {
deps += [ "//third_party/ashmem" ]
}
}

ipcz_source_set("util") {
Expand Down Expand Up @@ -295,6 +350,10 @@ ipcz_source_set("ipcz_tests_sources") {
"util/stack_trace_test.cc",
]

if (enable_ipcz_multiprocess_test_support) {
sources += [ "reference_drivers/memory_test.cc" ]
}

deps = [
"//testing/gmock",
"//testing/gtest",
Expand Down
50 changes: 50 additions & 0 deletions ipcz/src/reference_drivers/memory.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2022 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 "reference_drivers/memory.h"

#include <utility>

#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/base/macros.h"

namespace ipcz::reference_drivers {

Memory::Mapping::Mapping() = default;

Memory::Mapping::Mapping(void* base_address, size_t size)
: base_address_(base_address), size_(size) {}

Memory::Mapping::Mapping(Mapping&& other)
: base_address_(std::exchange(other.base_address_, nullptr)),
size_(std::exchange(other.size_, 0)) {}

Memory::Mapping& Memory::Mapping::operator=(Mapping&& other) {
Reset();
base_address_ = std::exchange(other.base_address_, nullptr);
size_ = std::exchange(other.size_, 0);
return *this;
}

Memory::Mapping::~Mapping() {
Reset();
}

Memory::Memory() = default;

Memory::Memory(OSHandle handle, size_t size)
: handle_(std::move(handle)), size_(size) {}

Memory::Memory(Memory&&) = default;

Memory& Memory::operator=(Memory&&) = default;

Memory::~Memory() = default;

Memory Memory::Clone() {
ABSL_ASSERT(is_valid());
return Memory(handle_.Clone(), size_);
}

} // namespace ipcz::reference_drivers
95 changes: 95 additions & 0 deletions ipcz/src/reference_drivers/memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2022 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.

#ifndef IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
#define IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_

#include "reference_drivers/os_handle.h"
#include "third_party/abseil-cpp/absl/types/span.h"

namespace ipcz::reference_drivers {

// Cross-platform abstraction for a shared memory region.
class Memory {
public:
// Cross-platform abstraction for an active mapping of a shared memory region.
//
// Instances of this object should be acquired from Memory::Map().
class Mapping {
public:
Mapping();
Mapping(void* base_address, size_t size);
Mapping(Mapping&&);
Mapping& operator=(Mapping&&);
Mapping(const Mapping&) = delete;
Mapping& operator=(const Mapping&) = delete;
~Mapping();

bool is_valid() const { return base_address_ != nullptr; }

size_t size() const { return size_; }
void* base() const { return base_address_; }

absl::Span<uint8_t> bytes() const {
return {static_cast<uint8_t*>(base()), size_};
}

template <typename T>
T* As() const {
return static_cast<T*>(base());
}

void Reset();

private:
void* base_address_ = nullptr;
size_t size_ = 0;
};

// Constructs an invalid Memory object which cannot be mapped.
Memory();

// Constructs a new Memory object over `handle`, an OSHandle which should have
// been previously taken from some other valid Memory object. `size` must
// correspond to the size of that original region.
Memory(OSHandle handle, size_t size);

// Constructs a new Memory object over a newly allocated shared memory region
// of at least `size` bytes.
explicit Memory(size_t size);

Memory(Memory&&);
Memory& operator=(Memory&&);
Memory(const Memory&) = delete;
Memory& operator=(const Memory&) = delete;
~Memory();

size_t size() const { return size_; }
bool is_valid() const { return handle_.is_valid(); }
const OSHandle& handle() const { return handle_; }

// Invalidates this Memory object and returns an OSHandle which can be used
// later to reconstruct an equivalent Memory object, given the same size().
OSHandle TakeHandle() { return std::move(handle_); }

// Resets this object, closing its handle to the underlying region.
void reset() { handle_.reset(); }

// Returns a new Memory object with its own handle to the same underlying
// region as `this`. Must only be called on a valid Memory object (i.e.
// is_valid() must be true.)
Memory Clone();

// Maps the entire region owned by Memory and returns a Mapping for it. Must
// only be called on a valid Memory object (i.e. is_valid() must be true.)
Mapping Map();

private:
OSHandle handle_;
size_t size_ = 0;
};

} // namespace ipcz::reference_drivers

#endif // IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
45 changes: 45 additions & 0 deletions ipcz/src/reference_drivers/memory_android.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2022 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 "reference_drivers/memory.h"

#include <sys/mman.h>
#include <unistd.h>

#include <cstddef>

#include "reference_drivers/os_handle.h"
#include "third_party/abseil-cpp/absl/base/macros.h"
#include "third_party/ashmem/ashmem.h"

namespace ipcz::reference_drivers {

void Memory::Mapping::Reset() {
if (base_address_) {
munmap(base_address_, size_);
base_address_ = nullptr;
size_ = 0;
}
}

Memory::Memory(size_t size) {
const size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
int fd = ashmem_create_region("ipcz-memory", rounded_size);
ABSL_ASSERT(fd >= 0);
int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
ABSL_ASSERT(err == 0);
handle_ = OSHandle(fd);
size_ = size;
}

Memory::Mapping Memory::Map() {
ABSL_ASSERT(is_valid());
void* addr =
mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, handle_.fd(), 0);
ABSL_ASSERT(addr && addr != MAP_FAILED);
return Mapping(addr, size_);
}

} // namespace ipcz::reference_drivers
54 changes: 54 additions & 0 deletions ipcz/src/reference_drivers/memory_fuchsia.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2022 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 "reference_drivers/memory.h"

#include <lib/zx/vmar.h>
#include <zircon/syscalls.h>

#include <cstddef>
#include <cstdint>
#include <utility>

#include "reference_drivers/os_handle.h"
#include "third_party/abseil-cpp/absl/base/macros.h"

namespace ipcz::reference_drivers {

void Memory::Mapping::Reset() {
if (base_address_) {
uintptr_t addr = reinterpret_cast<uintptr_t>(base_address_);
zx_status_t status = zx::vmar::root_self()->unmap(addr, size_);
ABSL_ASSERT(status == ZX_OK);
}
}

Memory::Memory(size_t size) {
const uint32_t page_size = zx_system_get_page_size();
const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
zx::vmo vmo;
zx_status_t status = zx::vmo::create(rounded_size, 0, &vmo);
ABSL_ASSERT(status == ZX_OK);
const int kNoExec = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE;
status = vmo.replace(kNoExec, &vmo);
ABSL_ASSERT(status == ZX_OK);
handle_ = OSHandle(std::move(vmo));
size_ = size;
}

Memory::Mapping Memory::Map() {
ABSL_ASSERT(is_valid());
uintptr_t addr;
zx_vm_option_t options =
ZX_VM_REQUIRE_NON_RESIZABLE | ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
zx_status_t status = zx::vmar::root_self()->map(
options, /*vmar_offset=*/0, *zx::unowned_vmo(handle_.handle().get()), 0,
size_, &addr);
if (status != ZX_OK) {
return {};
}
return Mapping(reinterpret_cast<void*>(addr), size_);
}

} // namespace ipcz::reference_drivers
47 changes: 47 additions & 0 deletions ipcz/src/reference_drivers/memory_mac.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022 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 "reference_drivers/memory.h"

#include <mach/mach_vm.h>

#include "reference_drivers/os_handle.h"
#include "third_party/abseil-cpp/absl/base/macros.h"

namespace ipcz::reference_drivers {

void Memory::Mapping::Reset() {
if (base_address_) {
kern_return_t kr = mach_vm_deallocate(
mach_task_self(), reinterpret_cast<mach_vm_address_t>(base_address_),
size_);
ABSL_ASSERT(kr == KERN_SUCCESS);
}
}

Memory::Memory(size_t size) {
mach_vm_size_t vm_size = size;
mach_port_t named_right;
kern_return_t kr = mach_make_memory_entry_64(
mach_task_self(), &vm_size, 0,
MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, &named_right,
MACH_PORT_NULL);
ABSL_ASSERT(kr == KERN_SUCCESS);
ABSL_ASSERT(vm_size >= size);
handle_ = OSHandle(OSHandle::MachSendRight(named_right));
size_ = size;
}

Memory::Mapping Memory::Map() {
ABSL_ASSERT(is_valid());
mach_vm_address_t address = 0;
kern_return_t kr = mach_vm_map(mach_task_self(), &address, size_, 0,
VM_FLAGS_ANYWHERE, handle_.mach_send_right(),
0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
ABSL_ASSERT(kr == KERN_SUCCESS);
return Mapping(reinterpret_cast<void*>(address), size_);
}

} // namespace ipcz::reference_drivers
Loading

0 comments on commit fc046c5

Please sign in to comment.