| // 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 |