blob: 8f62f510d426e5d80588dd2a78302bb58c4c490a [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/sys/fuzzing/common/shared-memory.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/vmar.h>
#include <stdint.h>
#include <string.h>
#include <zircon/errors.h>
#include <zircon/rights.h>
#include <zircon/status.h>
#include <limits>
#include <fbl/algorithm.h>
#include <sanitizer/asan_interface.h>
namespace fuzzing {
// Public methods
SharedMemory::~SharedMemory() { Reset(); }
SharedMemory& SharedMemory::operator=(SharedMemory&& other) noexcept {
vmo_ = std::move(other.vmo_);
mirror_ = other.mirror_;
data_ = other.data_;
size_ = other.size_;
mapped_size_ = other.mapped_size_;
other.mapped_size_ = 0;
other.Reset();
return *this;
}
zx_status_t SharedMemory::Reserve(size_t capacity) {
if (auto status = Create(capacity); status != ZX_OK) {
return status;
}
return Resize(0);
}
zx_status_t SharedMemory::Mirror(void* data, size_t size) {
if (!data || !size) {
FX_LOGS(ERROR) << "Cannot mirror empty buffer.";
return ZX_ERR_INVALID_ARGS;
}
if (auto status = Create(size); status != ZX_OK) {
return status;
}
mirror_ = data;
if (auto status = Resize(size); status != ZX_OK) {
return status;
}
Update();
return ZX_OK;
}
zx_status_t SharedMemory::Share(zx::vmo* out) const {
if (auto status = vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, out); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to duplicate VMO: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
}
zx_status_t SharedMemory::Link(zx::vmo vmo) {
Reset();
vmo_ = std::move(vmo);
if (auto status = Map(); status != ZX_OK) {
return status;
}
if (auto status = Read(); status != ZX_OK) {
return status;
}
return ZX_OK;
}
zx_status_t SharedMemory::Read() {
FX_DCHECK(!mirror_);
size_t size;
if (auto status = vmo_.get_property(ZX_PROP_VMO_CONTENT_SIZE, &size, sizeof(size));
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to get size from VMO: " << zx_status_get_string(status);
return status;
}
Resize(size);
return ZX_OK;
}
zx_status_t SharedMemory::Write(const void* data, size_t size) {
FX_DCHECK(!mirror_);
if (mapped_size_ < size) {
FX_LOGS(ERROR) << "Failed to write to VMO: need " << size << " bytes, have " << mapped_size_
<< ".";
return ZX_ERR_BUFFER_TOO_SMALL;
}
if (auto status = Resize(size); status != ZX_OK) {
return status;
}
if (size) {
memcpy(data_, data, size);
}
return ZX_OK;
}
void SharedMemory::Update() {
FX_DCHECK(mirror_);
memcpy(data_, mirror_, size_);
}
void SharedMemory::Clear() {
FX_DCHECK(mirror_);
memset(mirror_, 0, size_);
}
// Private methods
void SharedMemory::Reset() {
vmo_.reset();
if (mapped_size_) {
Unpoison(mapped_size_);
auto mapped_addr = reinterpret_cast<zx_vaddr_t>(data_);
zx::vmar::root_self()->unmap(mapped_addr, mapped_size_);
}
mirror_ = nullptr;
data_ = nullptr;
size_ = 0;
mapped_size_ = 0;
}
zx_status_t SharedMemory::Create(size_t capacity) {
Reset();
auto mapped_size = fbl::round_up(capacity, zx_system_get_page_size());
if (auto status = zx::vmo::create(mapped_size, 0, &vmo_); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to create VMO: " << zx_status_get_string(status);
return status;
}
return Map();
}
zx_status_t SharedMemory::Map() {
if (auto status = vmo_.get_size(&mapped_size_); status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to get size of VMO: " << zx_status_get_string(status);
return status;
}
zx_vaddr_t mapped_addr;
if (auto status = zx::vmar::root_self()->map(
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_MAP_RANGE | ZX_VM_REQUIRE_NON_RESIZABLE, 0,
vmo_, 0, mapped_size_, &mapped_addr);
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to map VMO: " << zx_status_get_string(status);
return status;
}
data_ = reinterpret_cast<uint8_t*>(mapped_addr);
size_ = mapped_size_;
return ZX_OK;
}
zx_status_t SharedMemory::Resize(size_t size) {
if (size == size_) {
return ZX_OK;
}
Unpoison(size);
size_ = size;
if (auto status = vmo_.set_property(ZX_PROP_VMO_CONTENT_SIZE, &size_, sizeof(size_));
status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to set size for VMO: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
}
void SharedMemory::Unpoison(size_t size) {
if (size_ < mapped_size_) {
ASAN_UNPOISON_MEMORY_REGION(data_ + size_, mapped_size_ - size_);
}
if (size < mapped_size_) {
ASAN_POISON_MEMORY_REGION(data_ + size, mapped_size_ - size);
}
}
} // namespace fuzzing