blob: c132cf2f549ba5bf4b8ea6a008dce5791cf0d71c [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.
#ifndef SRC_LIB_FUZZING_FIDL_SHARED_MEMORY_H_
#define SRC_LIB_FUZZING_FIDL_SHARED_MEMORY_H_
#include <fuchsia/mem/cpp/fidl.h>
#include <lib/fxl/macros.h>
#include <lib/zx/vmo.h>
#include <stddef.h>
#include <stdint.h>
#include <zircon/types.h>
namespace fuzzing {
namespace {
using ::fuchsia::mem::Buffer;
} // namespace
// This class can be used to share VMOs mapped into multiple processes. For example, one process
// may create a |fuchsia.mem.Buffer| with a certain capacity using:
// SharedMemory shmem;
// fuchsia.mem.Buffer buffer;
// shmem.Create(capacity, &buffer);
//
// It can then send it to another process via FIDL, which can link it:
// SharedMemory shmem;
// shmem.Link(buffer);
//
// This buffer can be used to share fixed-length data, e.g. coverage data.
//
// For variable-length data, both callers should set the optional |inline_size| parameter to true,
// e.g.
// shmem.Create(capacity, &buffer, /* inline_size= */ true);
// and
// shmem.Link(buffer, /* inline_size= */ true);
//
// This will allocate |sizeof(uint64_t)| additional bytes to store the size of valid data in the
// VMO. This size can be updated using |write| or |Clear| and retrieved with |size|, allowing
// callers to send or receive variable-length data. Reading and writing this size is not guaranteed
// to be atomic, so callers should use some other method to coordinate when the size changes, e.g.
// with a SignalCoordinator.
//
class SharedMemory final {
public:
SharedMemory() = default;
SharedMemory(SharedMemory&& other) { *this = std::move(other); }
SharedMemory& operator=(SharedMemory&& other);
~SharedMemory();
const zx::vmo& vmo() const { return vmo_; }
zx_vaddr_t addr() const { return addr_; }
size_t capacity() const { return capacity_; }
bool is_mapped() const { return addr_ != 0; }
// Describes the memory region, e.g. like inline 8-bit counters (uint8_t) for
// __sanitizer_cov_inline_8bit_counters_init or PC tables (uintptr_t) for
// __sanitizer_cov_pc_tables_init.
template <typename T = void>
T* begin() const {
return reinterpret_cast<T*>(Begin());
}
template <typename T = void>
T* end() const {
return reinterpret_cast<T*>(End());
}
// Describes the memory region like a fuzzer test input, e.g. for LLVMFuzzerTestOneInput.
uint8_t* data() const { return begin<uint8_t>(); }
size_t size() const { return GetSize(); }
// Resets this object, then creates a VMO of at least |capacity| bytes, maps it, and returns a
// duplicate handle in |out|. If |inline_size| is true, this object can be used to send or receive
// variable-length data as described in the class description.
void Create(size_t capacity, Buffer* out, bool inline_size = false);
// Like |Create|, but determines the capacity and initial contents automatically from the memory
// region described by |begin| and |end|. |begin| MUST be less than |end|, i.e. the region must be
// valid and non-empty. This method cannot be used to send variable-length data. The pointers are
// saved and used by |Update|; they MUST remain valid until |Reset| is called.
void Share(const void* begin, const void* end, Buffer* out);
// Resets this object, then takes ownership of the VMO handle in |buffer| and maps it. If
// |inline_size| is true, this object can be used to send or receive variable-length data as
// described in the class description.
void Link(Buffer buffer, bool inline_size = false);
// Writes data to the VMO. If unmapped, returns ZX_ERR_BAD_STATE. If the data is truncated due to
// insufficient remaining capacity, writes as much as it can and returns ZX_ERR_BUFFER_TOO_SMALL.
zx_status_t Write(const void* data, size_t size);
// If this object was |Share|d, copies the data from the original memory region to this objects
// shared memory; otherwise, does nothing.
void Update();
// Sets the amount valid date to 0.
void Clear();
// Unmaps and resets the VMO if mapped.
void Reset();
private:
// Manages the (possibly inlined) size.
size_t GetSize() const;
void SetSize(size_t size);
// Gets memory region pointers, accounting for the possibly inlined header.
void* Begin() const;
void* End() const;
zx::vmo vmo_;
zx_vaddr_t addr_ = 0;
size_t capacity_ = 0;
const void* source_ = nullptr;
size_t size_ = 0;
FXL_DISALLOW_COPY_AND_ASSIGN(SharedMemory);
};
} // namespace fuzzing
#endif // SRC_LIB_FUZZING_FIDL_SHARED_MEMORY_H_