// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_MEMORY_SHARED_MEMORY_MAPPING_H_ #define BASE_MEMORY_SHARED_MEMORY_MAPPING_H_ #include #include #include "base/base_export.h" #include "base/check.h" #include "base/containers/span.h" #include "base/memory/raw_ptr.h" #include "base/memory/shared_memory_mapper.h" #include "base/unguessable_token.h" namespace base { namespace subtle { class PlatformSharedMemoryRegion; } // namespace subtle // Base class for scoped handles to a shared memory mapping created from a // shared memory region. Created shared memory mappings remain valid even if the // creator region is transferred or destroyed. // // Each mapping has an UnguessableToken that identifies the shared memory region // it was created from. This is used for memory metrics, to avoid overcounting // shared memory. class BASE_EXPORT SharedMemoryMapping { public: // Default constructor initializes an invalid instance. SharedMemoryMapping(); // Move operations are allowed. SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept; SharedMemoryMapping& operator=(SharedMemoryMapping&& mapping) noexcept; SharedMemoryMapping(const SharedMemoryMapping&) = delete; SharedMemoryMapping& operator=(const SharedMemoryMapping&) = delete; // Unmaps the region if the mapping is valid. virtual ~SharedMemoryMapping(); // Returns true iff the mapping is valid. False means there is no // corresponding area of memory. bool IsValid() const { return !mapped_span_.empty(); } // Returns the logical size of the mapping in bytes. This is precisely the // size requested by whoever created the mapping, and it is always less than // or equal to |mapped_size()|. This is undefined for invalid instances. size_t size() const { DCHECK(IsValid()); return size_; } // Returns the actual size of the mapping in bytes. This is always at least // as large as |size()| but may be larger due to platform mapping alignment // constraints. This is undefined for invalid instances. size_t mapped_size() const { DCHECK(IsValid()); return mapped_span_.size(); } // Returns 128-bit GUID of the region this mapping belongs to. const UnguessableToken& guid() const { DCHECK(IsValid()); return guid_; } protected: SharedMemoryMapping(span mapped_span, size_t size, const UnguessableToken& guid, SharedMemoryMapper* mapper); void* raw_memory_ptr() const { return reinterpret_cast(mapped_span_.data()); } private: friend class SharedMemoryTracker; void Unmap(); span mapped_span_; size_t size_ = 0; UnguessableToken guid_; raw_ptr mapper_ = nullptr; }; // Class modeling a read-only mapping of a shared memory region into the // current process' address space. This is created by ReadOnlySharedMemoryRegion // instances. class BASE_EXPORT ReadOnlySharedMemoryMapping : public SharedMemoryMapping { public: // Default constructor initializes an invalid instance. ReadOnlySharedMemoryMapping(); ReadOnlySharedMemoryMapping(const ReadOnlySharedMemoryMapping&) = delete; ReadOnlySharedMemoryMapping& operator=(const ReadOnlySharedMemoryMapping&) = delete; // Move operations are allowed. ReadOnlySharedMemoryMapping(ReadOnlySharedMemoryMapping&&) noexcept; ReadOnlySharedMemoryMapping& operator=( ReadOnlySharedMemoryMapping&&) noexcept; // Returns the base address of the read-only mapping. Returns nullptr for // invalid instances. const void* memory() const { return raw_memory_ptr(); } // Returns a pointer to a page-aligned const T if the mapping is valid and // large enough to contain a T, or nullptr otherwise. template const T* GetMemoryAs() const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return nullptr; if (sizeof(T) > size()) return nullptr; return static_cast(raw_memory_ptr()); } // Returns a span of const T. The number of elements is autodeduced from the // size of the shared memory mapping. The number of elements may be // autodeduced as zero, i.e. the mapping is invalid or the size of the mapping // isn't large enough to contain even one T: in that case, an empty span // will be returned. The first element, if any, is guaranteed to be // page-aligned. template span GetMemoryAsSpan() const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return span(); size_t count = size() / sizeof(T); return GetMemoryAsSpan(count); } // Returns a span of const T with |count| elements if the mapping is valid and // large enough to contain |count| elements, or an empty span otherwise. The // first element, if any, is guaranteed to be page-aligned. template span GetMemoryAsSpan(size_t count) const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return span(); if (size() / sizeof(T) < count) return span(); return span(static_cast(raw_memory_ptr()), count); } private: friend class ReadOnlySharedMemoryRegion; ReadOnlySharedMemoryMapping(span mapped_span, size_t size, const UnguessableToken& guid, SharedMemoryMapper* mapper); }; // Class modeling a writable mapping of a shared memory region into the // current process' address space. This is created by *SharedMemoryRegion // instances. class BASE_EXPORT WritableSharedMemoryMapping : public SharedMemoryMapping { public: // Default constructor initializes an invalid instance. WritableSharedMemoryMapping(); WritableSharedMemoryMapping(const WritableSharedMemoryMapping&) = delete; WritableSharedMemoryMapping& operator=(const WritableSharedMemoryMapping&) = delete; // Move operations are allowed. WritableSharedMemoryMapping(WritableSharedMemoryMapping&&) noexcept; WritableSharedMemoryMapping& operator=( WritableSharedMemoryMapping&&) noexcept; // Returns the base address of the writable mapping. Returns nullptr for // invalid instances. void* memory() const { return raw_memory_ptr(); } // Returns a pointer to a page-aligned T if the mapping is valid and large // enough to contain a T, or nullptr otherwise. template T* GetMemoryAs() const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return nullptr; if (sizeof(T) > size()) return nullptr; return static_cast(raw_memory_ptr()); } // Returns a span of T. The number of elements is autodeduced from the size of // the shared memory mapping. The number of elements may be autodeduced as // zero, i.e. the mapping is invalid or the size of the mapping isn't large // enough to contain even one T: in that case, an empty span will be returned. // The first element, if any, is guaranteed to be page-aligned. template span GetMemoryAsSpan() const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return span(); size_t count = size() / sizeof(T); return GetMemoryAsSpan(count); } // Returns a span of T with |count| elements if the mapping is valid and large // enough to contain |count| elements, or an empty span otherwise. The first // element, if any, is guaranteed to be page-aligned. template span GetMemoryAsSpan(size_t count) const { static_assert(std::is_trivially_copyable::value, "Copying non-trivially-copyable object across memory spaces " "is dangerous"); if (!IsValid()) return span(); if (size() / sizeof(T) < count) return span(); return span(static_cast(raw_memory_ptr()), count); } private: friend WritableSharedMemoryMapping MapAtForTesting( subtle::PlatformSharedMemoryRegion* region, uint64_t offset, size_t size); friend class ReadOnlySharedMemoryRegion; friend class WritableSharedMemoryRegion; friend class UnsafeSharedMemoryRegion; WritableSharedMemoryMapping(span mapped_span, size_t size, const UnguessableToken& guid, SharedMemoryMapper* mapper); }; } // namespace base #endif // BASE_MEMORY_SHARED_MEMORY_MAPPING_H_