From bc72d9813e1003dd488dafaaa0c66feee26eebf6 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 22 Mar 2019 00:11:51 +0000 Subject: [PATCH] Bug 1515551 - Add functionality to SharedMemoryBasic to help map the shared memory at an arbitrary address. r=kmag This patch adds two things: 1. An optional fixed_address argument to SharedMemoryBasic::Map, which is the address to map the shared memory at. 2. A FindFreeAddressSpace function that callers can use to find a contiguous block of free address space, which can then be used to determine an address to pass in to Map that is likely to be free. Patches in bug 1474793 will use these to place the User Agent style sheets in a shared memory buffer in the parent process at an address that is also likely to be free in content processes. Differential Revision: https://phabricator.services.mozilla.com/D15057 --HG-- extra : moz-landing-system : lando --- ipc/chromium/src/base/shared_memory.h | 15 +++++++- ipc/chromium/src/base/shared_memory_posix.cc | 31 +++++++++++++--- ipc/chromium/src/base/shared_memory_win.cc | 16 +++++++-- ipc/glue/SharedMemory.h | 2 +- ipc/glue/SharedMemoryBasic_android.cpp | 26 ++++++++++++-- ipc/glue/SharedMemoryBasic_android.h | 4 ++- ipc/glue/SharedMemoryBasic_chromium.h | 8 +++-- ipc/glue/SharedMemoryBasic_mach.h | 4 ++- ipc/glue/SharedMemoryBasic_mach.mm | 37 ++++++++++++++++---- 9 files changed, 120 insertions(+), 23 deletions(-) diff --git a/ipc/chromium/src/base/shared_memory.h b/ipc/chromium/src/base/shared_memory.h index 25fb06a1f9fb..4d2ee92cf7ce 100644 --- a/ipc/chromium/src/base/shared_memory.h +++ b/ipc/chromium/src/base/shared_memory.h @@ -67,7 +67,11 @@ class SharedMemory { // 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. - bool Map(size_t bytes); + // + // 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. // Returns true if successful; returns false on error or if the @@ -94,6 +98,15 @@ class SharedMemory { // 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); + // Share the shared memory to another process. Attempts // to create a platform-specific new_handle which can be // used in a remote process to access the shared memory diff --git a/ipc/chromium/src/base/shared_memory_posix.cc b/ipc/chromium/src/base/shared_memory_posix.cc index 580e81d0054b..ef3788e23488 100644 --- a/ipc/chromium/src/base/shared_memory_posix.cc +++ b/ipc/chromium/src/base/shared_memory_posix.cc @@ -156,16 +156,30 @@ bool SharedMemory::Create(size_t size) { return true; } -bool SharedMemory::Map(size_t bytes) { +bool SharedMemory::Map(size_t bytes, void* fixed_address) { if (mapped_file_ == -1) return false; - memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), - MAP_SHARED, mapped_file_, 0); - - if (memory_) max_size_ = bytes; + // Don't use MAP_FIXED when a fixed_address was specified, since that can + // replace pages that are alread mapped at that address. + memory_ = + mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_, 0); bool mmap_succeeded = (memory_ != (void*)-1); + DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; + + if (mmap_succeeded) { + if (fixed_address && memory_ != fixed_address) { + bool munmap_succeeded = munmap(memory_, bytes) == 0; + DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno; + memory_ = NULL; + return false; + } + + max_size_ = bytes; + } + return mmap_succeeded; } @@ -178,6 +192,13 @@ bool SharedMemory::Unmap() { return true; } +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = + mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + munmap(memory, size); + return memory != MAP_FAILED ? memory : NULL; +} + bool SharedMemory::ShareToProcessCommon(ProcessId processId, SharedMemoryHandle* new_handle, bool close_self) { diff --git a/ipc/chromium/src/base/shared_memory_win.cc b/ipc/chromium/src/base/shared_memory_win.cc index 832049b33d3e..346fed329363 100644 --- a/ipc/chromium/src/base/shared_memory_win.cc +++ b/ipc/chromium/src/base/shared_memory_win.cc @@ -109,17 +109,19 @@ bool SharedMemory::Create(size_t size) { return true; } -bool SharedMemory::Map(size_t bytes) { +bool SharedMemory::Map(size_t bytes, void* fixed_address) { if (mapped_file_ == NULL) return false; if (external_section_ && !IsSectionSafeToMap(mapped_file_)) { return false; } - memory_ = MapViewOfFile( + memory_ = MapViewOfFileEx( mapped_file_, read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, - 0, 0, bytes); + 0, 0, bytes, fixed_address); if (memory_ != NULL) { + MOZ_ASSERT(!fixed_address || memory_ == fixed_address, + "MapViewOfFileEx returned an expected address"); return true; } return false; @@ -133,6 +135,14 @@ bool SharedMemory::Unmap() { return true; } +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); + if (memory) { + VirtualFree(memory, 0, MEM_RELEASE); + } + return memory; +} + bool SharedMemory::ShareToProcessCommon(ProcessId processId, SharedMemoryHandle* new_handle, bool close_self) { diff --git a/ipc/glue/SharedMemory.h b/ipc/glue/SharedMemory.h index e41c86fbedb1..0fa3e3912e38 100644 --- a/ipc/glue/SharedMemory.h +++ b/ipc/glue/SharedMemory.h @@ -50,7 +50,7 @@ class SharedMemory { virtual void* memory() const = 0; virtual bool Create(size_t size) = 0; - virtual bool Map(size_t nBytes) = 0; + virtual bool Map(size_t nBytes, void* fixed_address = nullptr) = 0; virtual void CloseHandle() = 0; diff --git a/ipc/glue/SharedMemoryBasic_android.cpp b/ipc/glue/SharedMemoryBasic_android.cpp index 4f87b3b4df67..2be87c07de39 100644 --- a/ipc/glue/SharedMemoryBasic_android.cpp +++ b/ipc/glue/SharedMemoryBasic_android.cpp @@ -68,7 +68,7 @@ bool SharedMemoryBasic::Create(size_t aNbytes) { return true; } -bool SharedMemoryBasic::Map(size_t nBytes) { +bool SharedMemoryBasic::Map(size_t nBytes, void* fixed_address) { MOZ_ASSERT(nullptr == mMemory, "Already Map()d"); int prot = PROT_READ; @@ -76,17 +76,37 @@ bool SharedMemoryBasic::Map(size_t nBytes) { prot |= PROT_WRITE; } - mMemory = mmap(nullptr, nBytes, prot, MAP_SHARED, mShmFd, 0); + // Don't use MAP_FIXED when a fixed_address was specified, since that can + // replace pages that are alread mapped at that address. + mMemory = mmap(fixed_address, nBytes, prot, MAP_SHARED, mShmFd, 0); + if (MAP_FAILED == mMemory) { - LogError("ShmemAndroid::Map()"); + if (!fixed_address) { + LogError("ShmemAndroid::Map()"); + } mMemory = nullptr; return false; } + if (fixed_address && mMemory != fixed_address) { + if (munmap(mMemory, nBytes)) { + LogError("ShmemAndroid::Map():unmap"); + mMemory = nullptr; + return false; + } + } + Mapped(nBytes); return true; } +void* SharedMemoryBasic::FindFreeAddressSpace(size_t size) { + void* memory = + mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + munmap(memory, size); + return memory != (void*)-1 ? memory : NULL; +} + bool SharedMemoryBasic::ShareToProcess(base::ProcessId /*unused*/, Handle* aNewHandle) { MOZ_ASSERT(mShmFd >= 0, "Should have been Create()d by now"); diff --git a/ipc/glue/SharedMemoryBasic_android.h b/ipc/glue/SharedMemoryBasic_android.h index 3380dbaca719..6e6eddeac1fa 100644 --- a/ipc/glue/SharedMemoryBasic_android.h +++ b/ipc/glue/SharedMemoryBasic_android.h @@ -32,7 +32,7 @@ class SharedMemoryBasic final virtual bool Create(size_t aNbytes) override; - virtual bool Map(size_t nBytes) override; + virtual bool Map(size_t nBytes, void* fixed_address = nullptr) override; virtual void CloseHandle() override; @@ -48,6 +48,8 @@ class SharedMemoryBasic final static Handle NULLHandle() { return Handle(); } + static void* FindFreeAddressSpace(size_t aSize); + virtual bool IsHandleValid(const Handle& aHandle) const override { return aHandle.fd >= 0; } diff --git a/ipc/glue/SharedMemoryBasic_chromium.h b/ipc/glue/SharedMemoryBasic_chromium.h index 497b55b23135..bb07d5208059 100644 --- a/ipc/glue/SharedMemoryBasic_chromium.h +++ b/ipc/glue/SharedMemoryBasic_chromium.h @@ -41,8 +41,8 @@ class SharedMemoryBasic final return ok; } - virtual bool Map(size_t nBytes) override { - bool ok = mSharedMemory.Map(nBytes); + virtual bool Map(size_t nBytes, void* fixed_address = nullptr) override { + bool ok = mSharedMemory.Map(nBytes, fixed_address); if (ok) { Mapped(nBytes); } @@ -76,6 +76,10 @@ class SharedMemoryBasic final return ret; } + static void* FindFreeAddressSpace(size_t size) { + return base::SharedMemory::FindFreeAddressSpace(size); + } + private: ~SharedMemoryBasic() {} diff --git a/ipc/glue/SharedMemoryBasic_mach.h b/ipc/glue/SharedMemoryBasic_mach.h index 24fcd3e8e963..ead0b301bcc7 100644 --- a/ipc/glue/SharedMemoryBasic_mach.h +++ b/ipc/glue/SharedMemoryBasic_mach.h @@ -70,7 +70,7 @@ class SharedMemoryBasic final : public SharedMemoryCommon { virtual bool Create(size_t aNbytes) override; - virtual bool Map(size_t nBytes) override; + virtual bool Map(size_t nBytes, void* fixed_address = nullptr) override; virtual void CloseHandle() override; @@ -86,6 +86,8 @@ class SharedMemoryBasic final : public SharedMemoryCommon { static Handle NULLHandle() { return Handle(); } + static void* FindFreeAddressSpace(size_t aSize); + virtual bool IsHandleValid(const Handle& aHandle) const override; virtual bool ShareToProcess(base::ProcessId aProcessId, diff --git a/ipc/glue/SharedMemoryBasic_mach.mm b/ipc/glue/SharedMemoryBasic_mach.mm index de26bef761b5..2a3e823882bf 100644 --- a/ipc/glue/SharedMemoryBasic_mach.mm +++ b/ipc/glue/SharedMemoryBasic_mach.mm @@ -512,7 +512,7 @@ bool SharedMemoryBasic::Create(size_t size) { return true; } -bool SharedMemoryBasic::Map(size_t size) { +bool SharedMemoryBasic::Map(size_t size, void* fixed_address) { MOZ_ASSERT(mMemory == nullptr); if (MACH_PORT_NULL == mPort) { @@ -520,18 +520,31 @@ bool SharedMemoryBasic::Map(size_t size) { } kern_return_t kr; - mach_vm_address_t address = 0; + mach_vm_address_t address = toVMAddress(fixed_address); vm_prot_t vmProtection = VM_PROT_READ; if (mOpenRights == RightsReadWrite) { vmProtection |= VM_PROT_WRITE; } - kr = mach_vm_map(mach_task_self(), &address, round_page(size), 0, VM_FLAGS_ANYWHERE, mPort, 0, - false, vmProtection, vmProtection, VM_INHERIT_NONE); + kr = mach_vm_map(mach_task_self(), &address, round_page(size), 0, + fixed_address ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE, + mPort, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE); if (kr != KERN_SUCCESS) { - LOG_ERROR("Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n", size, - mach_task_self(), mPort, mach_error_string(kr), kr); + if (!fixed_address) { + LOG_ERROR("Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n", + size, mach_task_self(), mPort, mach_error_string(kr), kr); + } + return false; + } + + if (fixed_address && fixed_address != toPointer(address)) { + kr = vm_deallocate(mach_task_self(), address, size); + if (kr != KERN_SUCCESS) { + LOG_ERROR("Failed to unmap shared memory at unsuitable address " + "(%zu bytes) from %x, port %x. %s (%x)\n", + size, mach_task_self(), mPort, mach_error_string(kr), kr); + } return false; } @@ -540,6 +553,18 @@ bool SharedMemoryBasic::Map(size_t size) { return true; } +void* SharedMemoryBasic::FindFreeAddressSpace(size_t size) { + mach_vm_address_t address = 0; + size = round_page(size); + if (mach_vm_map(mach_task_self(), &address, size, 0, + VM_FLAGS_ANYWHERE, MEMORY_OBJECT_NULL, 0, false, VM_PROT_NONE, + VM_PROT_NONE, VM_INHERIT_NONE) != KERN_SUCCESS || + vm_deallocate(mach_task_self(), address, size) != KERN_SUCCESS) { + return nullptr; + } + return toPointer(address); +} + bool SharedMemoryBasic::ShareToProcess(base::ProcessId pid, Handle* aNewHandle) { if (pid == getpid()) { *aNewHandle = mPort;