/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright (c) 2006-2008 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 BASE_SHARED_MEMORY_H_ #define BASE_SHARED_MEMORY_H_ #include "build/build_config.h" #if defined(OS_POSIX) # include # include #endif #include #include "base/basictypes.h" #include "base/process.h" #include "mozilla/Attributes.h" #include "mozilla/UniquePtrExtensions.h" namespace base { // SharedMemoryHandle is a platform specific type which represents // the underlying OS handle to a shared memory segment. typedef mozilla::UniqueFileHandle SharedMemoryHandle; // Platform abstraction for shared memory. Provides a C++ wrapper // around the OS primitive for a memory mapped file. class SharedMemory { public: // Create a new SharedMemory object. SharedMemory() = default; // Create a new SharedMemory object from an existing, open // shared memory file. SharedMemory(SharedMemoryHandle init_handle, bool read_only) : SharedMemory() { SetHandle(std::move(init_handle), read_only); } // Move constructor; transfers ownership. SharedMemory(SharedMemory&& other) = default; // Destructor. Will close any open files. ~SharedMemory(); // Initialize a new SharedMemory object from an existing, open // shared memory file. bool SetHandle(SharedMemoryHandle handle, bool read_only); // Return true iff the given handle is valid (i.e. not the distingished // invalid value; NULL for a HANDLE and -1 for a file descriptor) static bool IsHandleValid(const SharedMemoryHandle& handle); // IsHandleValid applied to this object's handle. bool IsValid() const { return static_cast(mapped_file_); } // Return invalid handle (see comment above for exact definition). static SharedMemoryHandle NULLHandle(); // Creates a shared memory segment. // Returns true on success, false on failure. bool Create(size_t size) { return CreateInternal(size, false); } // Creates a shared memory segment that supports the Freeze() // method; see below. (Warning: creating freezeable shared memory // within a sandboxed process isn't possible on some platforms.) // Returns true on success, false on failure. bool CreateFreezeable(size_t size) { return CreateInternal(size, true); } // Maps the shared memory into the caller's address space. // Returns true on success, false otherwise. The memory address // is accessed via the memory() accessor. // // If the specified fixed address is not null, it is the address that the // shared memory must be mapped at. Returns false if the shared memory // could not be mapped at that address. bool Map(size_t bytes, void* fixed_address = nullptr); // Unmaps the shared memory from the caller's address space. void Unmap() { memory_ = nullptr; } // Get the size of the opened shared memory backing file. // Note: This size is only available to the creator of the // shared memory, and not to those that opened shared memory // created externally. // Returns 0 if not opened or unknown. size_t max_size() const { return max_size_; } // Gets a pointer to the opened memory space if it has been // Mapped via Map(). Returns NULL if it is not mapped. void* memory() const { return memory_.get(); } // Extracts the underlying file handle, returning a RAII type. // This unmaps the memory as a side-effect (and cleans up any OS-specific // resources). mozilla::UniqueFileHandle TakeHandle() { mozilla::UniqueFileHandle handle = std::move(mapped_file_); Close(); return handle; } // Creates a copy of the underlying file handle, returning a RAII type. // This operation may fail, in which case the returned file handle will be // invalid. mozilla::UniqueFileHandle CloneHandle(); // Make the shared memory object read-only, such that it cannot be // written even if it's sent to an untrusted process. If it was // mapped in this process, it will be unmapped. The object must // have been created with CreateFreezeable(), and must not have // already been shared to another process. // // (See bug 1479960 comment #0 for OS-specific implementation // details.) [[nodiscard]] bool Freeze() { Unmap(); return ReadOnlyCopy(this); } // Similar to Freeze(), but assigns the read-only capability to // another SharedMemory object and leaves this object's mapping in // place and writeable. This can be used to broadcast data to // several untrusted readers without copying. // // The other constraints of Freeze() still apply: this object closes // its handle (as if by `Close(false)`), it cannot have been // previously shared, and the read-only handle cannot be used to // write the memory even by a malicious process. // // (The out parameter can currently be the same as `this` if and // only if the memory was unmapped, but this is an implementation // detail and shouldn't be relied on; for that use case Freeze() // should be used instead.) [[nodiscard]] bool ReadOnlyCopy(SharedMemory* ro_out); // Closes the open shared memory segment. // It is safe to call Close repeatedly. void Close(bool unmap_view = true); // Returns a page-aligned address at which the given number of bytes could // probably be mapped. Returns NULL on error or if there is insufficient // contiguous address space to map the required number of pages. // // Note that there is no guarantee that the given address space will actually // be free by the time this function returns, since another thread might map // something there in the meantime. static void* FindFreeAddressSpace(size_t size); #ifdef OS_POSIX // If named POSIX shm is being used, append the prefix (including // the leading '/') that would be used by a process with the given // pid to the given string and return true. If not, return false. // (This is public so that the Linux sandboxing code can use it.) static bool AppendPosixShmPrefix(std::string* str, pid_t pid); // Similar, but simply returns whether POSIX shm is in use. static bool UsingPosixShm(); #endif private: bool CreateInternal(size_t size, bool freezeable); // Unmapping shared memory requires the mapped size on Unix but not // Windows; this encapsulates that difference. struct MappingDeleter { #ifdef OS_POSIX // A default-constructed deleter must be used only with nullptr // (to allow default-constructing UniqueMapping). A deleter with // a size must be used at most once. size_t mapped_size_ = 0; explicit MappingDeleter(size_t size) : mapped_size_(size) {} #endif MappingDeleter() = default; void operator()(void* ptr); }; using UniqueMapping = mozilla::UniquePtr; UniqueMapping memory_; size_t max_size_ = 0; mozilla::UniqueFileHandle mapped_file_; #if defined(OS_WIN) // If true indicates this came from an external source so needs extra checks // before being mapped. bool external_section_ = false; #elif defined(OS_POSIX) && !defined(ANDROID) mozilla::UniqueFileHandle frozen_file_; bool is_memfd_ = false; #endif bool read_only_ = false; bool freezeable_ = false; DISALLOW_EVIL_CONSTRUCTORS(SharedMemory); }; } // namespace base #endif // BASE_SHARED_MEMORY_H_