diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 3379283ad6e7..3f40b9bd9ae3 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2388,7 +2388,7 @@ mozilla::ipc::IPCResult ContentChild::RecvRegisterStringBundles( for (auto& descriptor : aDescriptors) { stringBundleService->RegisterContentBundle( - descriptor.bundleURL(), descriptor.mapHandle(), descriptor.mapSize()); + descriptor.bundleURL(), descriptor.mapFile(), descriptor.mapSize()); } return IPC_OK(); @@ -2401,7 +2401,7 @@ mozilla::ipc::IPCResult ContentChild::RecvUpdateL10nFileSources( } mozilla::ipc::IPCResult ContentChild::RecvUpdateSharedData( - SharedMemoryHandle&& aMapHandle, const uint32_t& aMapSize, + const FileDescriptor& aMapFile, const uint32_t& aMapSize, nsTArray&& aBlobs, nsTArray&& aChangedKeys) { nsTArray> blobImpls(aBlobs.Length()); for (auto& ipcBlob : aBlobs) { @@ -2409,12 +2409,12 @@ mozilla::ipc::IPCResult ContentChild::RecvUpdateSharedData( } if (mSharedData) { - mSharedData->Update(std::move(aMapHandle), aMapSize, std::move(blobImpls), + mSharedData->Update(aMapFile, aMapSize, std::move(blobImpls), std::move(aChangedKeys)); } else { mSharedData = new SharedMap(ContentProcessMessageManager::Get()->GetParentObject(), - std::move(aMapHandle), aMapSize, std::move(blobImpls)); + aMapFile, aMapSize, std::move(blobImpls)); } return IPC_OK(); @@ -2482,7 +2482,7 @@ mozilla::ipc::IPCResult ContentChild::RecvRebuildFontList( mozilla::ipc::IPCResult ContentChild::RecvFontListShmBlockAdded( const uint32_t& aGeneration, const uint32_t& aIndex, - SharedMemoryHandle&& aHandle) { + base::SharedMemoryHandle&& aHandle) { if (gfxPlatform::Initialized()) { gfxPlatformFontList::PlatformFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle)); diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 1fddd4a32a2a..97923483a016 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -120,7 +120,7 @@ class ContentChild final : public PContentChild, const mozilla::dom::ipc::StructuredCloneData& aInitialData, bool aIsReadyForBackgroundProcessing); - void InitSharedUASheets(Maybe&& aHandle, + void InitSharedUASheets(Maybe&& aHandle, uintptr_t aAddress); void InitGraphicsDeviceData(const ContentDeviceData& aData); @@ -314,7 +314,7 @@ class ContentChild final : public PContentChild, nsTArray&& aDescriptors); mozilla::ipc::IPCResult RecvUpdateSharedData( - SharedMemoryHandle&& aMapHandle, const uint32_t& aMapSize, + const FileDescriptor& aMapFile, const uint32_t& aMapSize, nsTArray&& aBlobs, nsTArray&& aChangedKeys); mozilla::ipc::IPCResult RecvFontListChanged(); @@ -334,7 +334,7 @@ class ContentChild final : public PContentChild, mozilla::ipc::IPCResult RecvRebuildFontList(const bool& aFullRebuild); mozilla::ipc::IPCResult RecvFontListShmBlockAdded( const uint32_t& aGeneration, const uint32_t& aIndex, - SharedMemoryHandle&& aHandle); + base::SharedMemoryHandle&& aHandle); mozilla::ipc::IPCResult RecvUpdateAppLocales( nsTArray&& aAppLocales); @@ -510,9 +510,9 @@ class ContentChild final : public PContentChild, mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes( XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData, FullLookAndFeel&& aLookAndFeelData, SystemFontList&& aFontList, - Maybe&& aSharedUASheetHandle, + Maybe&& aSharedUASheetHandle, const uintptr_t& aSharedUASheetAddress, - nsTArray&& aSharedFontListBlocks, + nsTArray&& aSharedFontListBlocks, const bool& aIsReadyForBackgroundProcessing); mozilla::ipc::IPCResult RecvProvideAnonymousTemporaryFile( @@ -542,7 +542,7 @@ class ContentChild final : public PContentChild, // for use during gfx initialization. SystemFontList& SystemFontList() { return mFontList; } - nsTArray& SharedFontListBlocks() { + nsTArray& SharedFontListBlocks() { return mSharedFontListBlocks; } @@ -831,7 +831,7 @@ class ContentChild final : public PContentChild, // Temporary storage for look and feel data. FullLookAndFeel mLookAndFeelData; // Temporary storage for list of shared-fontlist memory blocks. - nsTArray mSharedFontListBlocks; + nsTArray mSharedFontListBlocks; AppInfo mAppInfo; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 406743daabbd..88de9f70d968 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -8,6 +8,7 @@ #include "mozilla/DebugOnly.h" #include "base/basictypes.h" +#include "base/shared_memory.h" #include "ContentParent.h" #include "mozilla/ipc/ProcessUtils.h" @@ -150,7 +151,6 @@ #include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/IPCStreamUtils.h" -#include "mozilla/ipc/SharedMemory.h" #include "mozilla/ipc/TestShellParent.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ImageBridgeParent.h" @@ -1526,12 +1526,11 @@ void ContentParent::GetAllEvenIfDead(nsTArray& aArray) { void ContentParent::BroadcastStringBundle( const StringBundleDescriptor& aBundle) { + AutoTArray array; + array.AppendElement(aBundle); + for (auto* cp : AllProcesses(eLive)) { - AutoTArray array; - array.AppendElement(StringBundleDescriptor( - aBundle.bundleURL(), SharedMemory::CloneHandle(aBundle.mapHandle()), - aBundle.mapSize())); - Unused << cp->SendRegisterStringBundles(std::move(array)); + Unused << cp->SendRegisterStringBundles(array); } } @@ -1545,9 +1544,9 @@ void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration, uint32_t aIndex) { auto* pfl = gfxPlatformFontList::PlatformFontList(); for (auto* cp : AllProcesses(eLive)) { - SharedMemory::Handle handle = + base::SharedMemoryHandle handle = pfl->ShareShmBlockToProcess(aIndex, cp->Pid()); - if (handle == SharedMemory::NULLHandle()) { + if (handle == base::SharedMemory::NULLHandle()) { // If something went wrong here, we just skip it; the child will need to // request the block as needed, at some performance cost. continue; @@ -5681,7 +5680,7 @@ mozilla::ipc::IPCResult ContentParent::RecvShutdownPerfStats( mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock( const uint32_t& aGeneration, const uint32_t& aIndex, - SharedMemory::Handle* aOut) { + base::SharedMemoryHandle* aOut) { auto* fontList = gfxPlatformFontList::PlatformFontList(); MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?"); fontList->ShareFontListShmBlockToProcess(aGeneration, aIndex, Pid(), aOut); @@ -5733,7 +5732,7 @@ mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading( } mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict( - nsIURI* aURI, SharedMemory::Handle* aOutHandle, uint32_t* aOutSize) { + nsIURI* aURI, base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) { if (!aURI) { return IPC_FAIL(this, "aURI must not be null."); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 90e64e897cae..c1ec40ee7871 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -22,7 +22,6 @@ #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "mozilla/ipc/SharedMemory.h" #include "mozilla/Attributes.h" #include "mozilla/DataMutex.h" #include "mozilla/HalTypes.h" @@ -1128,7 +1127,7 @@ class ContentParent final : public PContentParent, mozilla::ipc::IPCResult RecvGetFontListShmBlock( const uint32_t& aGeneration, const uint32_t& aIndex, - mozilla::ipc::SharedMemory::Handle* aOut); + base::SharedMemoryHandle* aOut); mozilla::ipc::IPCResult RecvInitializeFamily(const uint32_t& aGeneration, const uint32_t& aFamilyIndex, @@ -1151,9 +1150,9 @@ class ContentParent final : public PContentParent, mozilla::ipc::IPCResult RecvStartCmapLoading(const uint32_t& aGeneration, const uint32_t& aStartIndex); - mozilla::ipc::IPCResult RecvGetHyphDict( - nsIURI* aURIParams, mozilla::ipc::SharedMemory::Handle* aOutHandle, - uint32_t* aOutSize); + mozilla::ipc::IPCResult RecvGetHyphDict(nsIURI* aURIParams, + base::SharedMemoryHandle* aOutHandle, + uint32_t* aOutSize); mozilla::ipc::IPCResult RecvNotifyBenchmarkResult(const nsAString& aCodecName, const uint32_t& aDecodeFPS); diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index 559fa7236f83..0c2d73ece68f 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -7,6 +7,7 @@ #include "mozilla/ipc/IOThreadChild.h" #include "ContentProcess.h" +#include "base/shared_memory.h" #include "mozilla/Preferences.h" #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) @@ -77,7 +78,7 @@ void ContentProcess::InfallibleInit(int aArgc, char* aArgv[]) { geckoargs::sParentBuildID.Get(aArgc, aArgv); // command line: -jsInitHandle handle -jsInitLen length - Maybe jsInitHandle = + Maybe jsInitHandle = geckoargs::sJsInitHandle.Get(aArgc, aArgv); Maybe jsInitLen = geckoargs::sJsInitLen.Get(aArgc, aArgv); diff --git a/dom/ipc/MemMapSnapshot.cpp b/dom/ipc/MemMapSnapshot.cpp index cc0bf83ed076..53accfdb1bec 100644 --- a/dom/ipc/MemMapSnapshot.cpp +++ b/dom/ipc/MemMapSnapshot.cpp @@ -6,40 +6,39 @@ #include "MemMapSnapshot.h" +#include "mozilla/AutoMemMap.h" #include "mozilla/ResultExtensions.h" +#include "mozilla/Try.h" +#include "mozilla/ipc/FileDescriptor.h" namespace mozilla::ipc { Result MemMapSnapshot::Init(size_t aSize) { - MOZ_ASSERT(!mMem); + MOZ_ASSERT(!mInitialized); - auto aMem = MakeRefPtr(); - if (NS_WARN_IF(!aMem->CreateFreezable(aSize))) { + if (NS_WARN_IF(!mMem.CreateFreezeable(aSize))) { return Err(NS_ERROR_FAILURE); } - if (NS_WARN_IF(!aMem->Map(aSize))) { + if (NS_WARN_IF(!mMem.Map(aSize))) { return Err(NS_ERROR_FAILURE); } - mMem = std::move(aMem); + mInitialized = true; return Ok(); } -Result MemMapSnapshot::Finalize(RefPtr& aMem) { - MOZ_ASSERT(mMem); +Result MemMapSnapshot::Finalize(loader::AutoMemMap& aMem) { + MOZ_ASSERT(mInitialized); - auto size = mMem->Size(); - if (NS_WARN_IF(!mMem->Freeze())) { - return Err(NS_ERROR_FAILURE); - } - - aMem = std::move(mMem); - - // We need to re-map the memory as `Freeze()` unmaps it. - if (NS_WARN_IF(!aMem->Map(size))) { + if (NS_WARN_IF(!mMem.Freeze())) { return Err(NS_ERROR_FAILURE); } + // TakeHandle resets mMem, so call max_size first. + size_t size = mMem.max_size(); + FileDescriptor memHandle(mMem.TakeHandle()); + MOZ_TRY(aMem.initWithHandle(memHandle, size)); + mInitialized = false; return Ok(); } diff --git a/dom/ipc/MemMapSnapshot.h b/dom/ipc/MemMapSnapshot.h index d04d0db24235..12b4d353ad4f 100644 --- a/dom/ipc/MemMapSnapshot.h +++ b/dom/ipc/MemMapSnapshot.h @@ -9,13 +9,17 @@ #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" -#include "mozilla/RefPtr.h" #include "mozilla/RangedPtr.h" #include "mozilla/Result.h" -#include "mozilla/ipc/SharedMemory.h" +#include "base/shared_memory.h" #include "ErrorList.h" -namespace mozilla::ipc { +namespace mozilla { +namespace loader { +class AutoMemMap; +} + +namespace ipc { /** * A helper class for creating a read-only snapshot of memory-mapped data. @@ -31,18 +35,20 @@ namespace mozilla::ipc { class MOZ_RAII MemMapSnapshot { public: Result Init(size_t aSize); - Result Finalize(RefPtr& aMem); + Result Finalize(loader::AutoMemMap& aMap); template RangedPtr Get() { - MOZ_ASSERT(mMem); - return {static_cast(mMem->Memory()), mMem->MaxSize() / sizeof(T)}; + MOZ_ASSERT(mInitialized); + return {static_cast(mMem.memory()), mMem.max_size() / sizeof(T)}; } private: - RefPtr mMem; + base::SharedMemory mMem; + bool mInitialized = false; }; -} // namespace mozilla::ipc +} // namespace ipc +} // namespace mozilla #endif // dom_ipc_MemMapSnapshot_h diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 0d7ac1fbd7f8..4c823e4f190a 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -137,7 +137,7 @@ using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext using mozilla::dom::PermitUnloadResult from "nsIDocumentViewer.h"; using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h"; using mozilla::dom::WindowContextTransaction from "mozilla/dom/WindowContext.h"; -[MoveOnly] using mozilla::ipc::SharedMemoryHandle from "mozilla/ipc/SharedMemory.h"; +[MoveOnly] using base::SharedMemoryHandle from "base/shared_memory.h"; using gfxSparseBitSet from "gfxFontUtils.h"; using FontVisibility from "gfxFontEntry.h"; using mozilla::dom::MediaControlAction from "mozilla/dom/MediaControlKeySource.h"; @@ -401,7 +401,7 @@ struct VisitedQueryResult struct StringBundleDescriptor { nsCString bundleURL; - SharedMemoryHandle mapHandle; + FileDescriptor mapFile; uint32_t mapSize; }; @@ -750,7 +750,7 @@ child: async RegisterStringBundles(StringBundleDescriptor[] stringBundles); - async UpdateSharedData(SharedMemoryHandle aMapHandle, uint32_t aSize, + async UpdateSharedData(FileDescriptor mapFile, uint32_t aSize, IPCBlob[] blobs, nsCString[] changedKeys); diff --git a/dom/ipc/SharedMap.cpp b/dom/ipc/SharedMap.cpp index f11cf52ecd13..dc6602d37707 100644 --- a/dom/ipc/SharedMap.cpp +++ b/dom/ipc/SharedMap.cpp @@ -10,7 +10,6 @@ #include "MemMapSnapshot.h" #include "ScriptPreloader-inl.h" -#include "mozilla/RefPtr.h" #include "mozilla/dom/AutoEntryScript.h" #include "mozilla/dom/BlobImpl.h" #include "mozilla/dom/ContentParent.h" @@ -42,12 +41,12 @@ static inline void AlignTo(size_t* aOffset, size_t aAlign) { SharedMap::SharedMap() = default; -SharedMap::SharedMap(nsIGlobalObject* aGlobal, SharedMemoryHandle&& aMapHandle, +SharedMap::SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor& aMapFile, size_t aMapSize, nsTArray>&& aBlobs) - : DOMEventTargetHelper(aGlobal), - mBlobImpls(std::move(aBlobs)), - mMapHandle(std::move(aMapHandle)), - mMapSize(aMapSize) {} + : DOMEventTargetHelper(aGlobal), mBlobImpls(std::move(aBlobs)) { + mMapFile.reset(new FileDescriptor(aMapFile)); + mMapSize = aMapSize; +} bool SharedMap::Has(const nsACString& aName) { Unused << MaybeRebuild(); @@ -97,20 +96,24 @@ void SharedMap::Entry::Read(JSContext* aCx, holder.Read(aCx, aRetVal, aRv); } -SharedMap::SharedMemoryHandle SharedMap::CloneHandle() const { - if (mMap->IsValid()) { - return mMap->CloneHandle(); +FileDescriptor SharedMap::CloneMapFile() const { + if (mMap.initialized()) { + return mMap.cloneHandle(); } - return SharedMemory::CloneHandle(mMapHandle); + return *mMapFile; } -void SharedMap::Update(SharedMemoryHandle&& aMapHandle, size_t aMapSize, +void SharedMap::Update(const FileDescriptor& aMapFile, size_t aMapSize, nsTArray>&& aBlobs, nsTArray&& aChangedKeys) { MOZ_DIAGNOSTIC_ASSERT(!mWritable); - mMap->TakeHandleAndUnmap(); - mMapHandle = std::move(aMapHandle); + mMap.reset(); + if (mMapFile) { + *mMapFile = aMapFile; + } else { + mMapFile.reset(new FileDescriptor(aMapFile)); + } mMapSize = aMapSize; mEntries.Clear(); mEntryArray.reset(); @@ -191,7 +194,7 @@ void SharedMap::Entry::ExtractData(char* aDestPtr, uint32_t aNewOffset, } Result SharedMap::MaybeRebuild() { - if (!SharedMemory::IsHandleValid(mMapHandle)) { + if (!mMapFile) { return Ok(); } @@ -203,19 +206,13 @@ Result SharedMap::MaybeRebuild() { // its shared memory region. When needed, that structured clone data is // retrieved directly as indexes into the SharedMap's shared memory region. - if (!mMap->SetHandle(SharedMemory::CloneHandle(mMapHandle), - SharedMemory::OpenRights::RightsReadOnly)) { - return Err(NS_ERROR_FAILURE); - } - mMapHandle = SharedMemory::NULLHandle(); - if (!mMap->Map(mMapSize)) { - return Err(NS_ERROR_FAILURE); - } + MOZ_TRY(mMap.initWithHandle(*mMapFile, mMapSize)); + mMapFile.reset(); // We should be able to pass this range as an initializer list or an immediate // param, but gcc currently chokes on that if optimization is enabled, and // initializes everything to 0. - Range range((uint8_t*)mMap->Memory(), mMap->Size()); + Range range(&mMap.get()[0], mMap.size()); InputBuffer buffer(range); uint32_t count; @@ -248,7 +245,7 @@ WritableSharedMap::WritableSharedMap() { // Serialize the initial empty contents of the map immediately so that we // always have a file descriptor to send to callers of CloneMapFile(). Unused << Serialize(); - MOZ_RELEASE_ASSERT(mMap->IsValid()); + MOZ_RELEASE_ASSERT(mMap.initialized()); } SharedMap* WritableSharedMap::GetReadOnly() { @@ -256,7 +253,7 @@ SharedMap* WritableSharedMap::GetReadOnly() { nsTArray> blobs(mBlobImpls.Clone()); mReadOnly = new SharedMap(ContentProcessMessageManager::Get()->GetParentObject(), - CloneHandle(), MapSize(), std::move(blobs)); + CloneMapFile(), MapSize(), std::move(blobs)); } return mReadOnly; } @@ -339,7 +336,7 @@ Result WritableSharedMap::Serialize() { memcpy(ptr.get(), header.Get(), header.cursor()); // We've already updated offsets at this point. We need this to succeed. - mMap->TakeHandleAndUnmap(); + mMap.reset(); MOZ_RELEASE_ASSERT(mem.Finalize(mMap).isOk()); return Ok(); @@ -355,7 +352,7 @@ void WritableSharedMap::SendTo(ContentParent* aParent) const { } } - Unused << aParent->SendUpdateSharedData(CloneHandle(), mMap->Size(), blobs, + Unused << aParent->SendUpdateSharedData(CloneMapFile(), mMap.size(), blobs, mChangedKeys); } @@ -376,7 +373,7 @@ void WritableSharedMap::BroadcastChanges() { if (mReadOnly) { nsTArray> blobImpls(mBlobImpls.Clone()); - mReadOnly->Update(CloneHandle(), mMap->Size(), std::move(blobImpls), + mReadOnly->Update(CloneMapFile(), mMap.size(), std::move(blobImpls), std::move(mChangedKeys)); } diff --git a/dom/ipc/SharedMap.h b/dom/ipc/SharedMap.h index cfbc1e10d905..0a5b686c627b 100644 --- a/dom/ipc/SharedMap.h +++ b/dom/ipc/SharedMap.h @@ -9,8 +9,8 @@ #include "mozilla/dom/MozSharedMapBinding.h" +#include "mozilla/AutoMemMap.h" #include "mozilla/dom/ipc/StructuredCloneData.h" -#include "mozilla/ipc/SharedMemory.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" @@ -53,13 +53,12 @@ namespace ipc { * WritableSharedMap instances. */ class SharedMap : public DOMEventTargetHelper { - using SharedMemory = mozilla::ipc::SharedMemory; - using SharedMemoryHandle = mozilla::ipc::SharedMemoryHandle; + using FileDescriptor = mozilla::ipc::FileDescriptor; public: SharedMap(); - SharedMap(nsIGlobalObject* aGlobal, SharedMemoryHandle&&, size_t, + SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor&, size_t, nsTArray>&& aBlobs); // Returns true if the map contains the given (UTF-8) key. @@ -97,7 +96,7 @@ class SharedMap : public DOMEventTargetHelper { * memory region for this map. The file descriptor may be passed between * processes, and used to update corresponding instances in child processes. */ - SharedMemoryHandle CloneHandle() const; + FileDescriptor CloneMapFile() const; /** * Returns the size of the memory mapped region that backs this map. Must be @@ -105,14 +104,14 @@ class SharedMap : public DOMEventTargetHelper { * descriptor returned by CloneMapFile() in order to initialize or update a * child SharedMap. */ - size_t MapSize() const { return mMap->Size(); } + size_t MapSize() const { return mMap.size(); } /** * Updates this instance to reflect the contents of the shared memory region - * in the given map handle, and broadcasts a change event for the given set of + * in the given map file, and broadcasts a change event for the given set of * changed (UTF-8-encoded) keys. */ - void Update(SharedMemoryHandle&& aMapHandle, size_t aMapSize, + void Update(const FileDescriptor& aMapFile, size_t aMapSize, nsTArray>&& aBlobs, nsTArray&& aChangedKeys); @@ -263,8 +262,12 @@ class SharedMap : public DOMEventTargetHelper { Result MaybeRebuild(); void MaybeRebuild() const; - SharedMemoryHandle mMapHandle; - // The size of the memory-mapped region backed by mMap, in bytes. + // Note: This header is included by WebIDL binding headers, and therefore + // can't include "windows.h". Since FileDescriptor.h does include "windows.h" + // on Windows, we can only forward declare FileDescriptor, and can't include + // it as an inline member. + UniquePtr mMapFile; + // The size of the memory-mapped region backed by mMapFile, in bytes. size_t mMapSize = 0; mutable nsClassHashtable mEntries; @@ -273,14 +276,14 @@ class SharedMap : public DOMEventTargetHelper { // Manages the memory mapping of the current snapshot. This is initialized // lazily after each SharedMap construction or updated, based on the values in // mMapFile and mMapSize. - RefPtr mMap = MakeRefPtr(); + loader::AutoMemMap mMap; bool mWritable = false; // Returns a pointer to the beginning of the memory mapped snapshot. Entry // offsets are relative to this pointer, and Entry objects access their // structured clone data by indexing this pointer. - char* Data() { return static_cast(mMap->Memory()); } + char* Data() { return mMap.get().get(); } }; class WritableSharedMap final : public SharedMap { diff --git a/dom/ipc/SharedStringMap.cpp b/dom/ipc/SharedStringMap.cpp index 648dc9da2272..8d50de686339 100644 --- a/dom/ipc/SharedStringMap.cpp +++ b/dom/ipc/SharedStringMap.cpp @@ -11,6 +11,7 @@ #include "mozilla/BinarySearch.h" #include "mozilla/Try.h" +#include "mozilla/ipc/FileDescriptor.h" using namespace mozilla::loader; @@ -27,42 +28,27 @@ static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) { return mod ? aAlign - mod : 0; } -SharedStringMap::SharedStringMap(const SharedMemoryHandle& aMapHandle, +SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile, size_t aMapSize) { - auto map = MakeRefPtr(); - { - auto result = map->SetHandle(SharedMemory::CloneHandle(aMapHandle), - SharedMemory::OpenRights::RightsReadOnly); - MOZ_RELEASE_ASSERT(result); - } - { - auto result = map->Map(aMapSize); - MOZ_RELEASE_ASSERT(result); - } - + auto result = mMap.initWithHandle(aMapFile, aMapSize); + MOZ_RELEASE_ASSERT(result.isOk()); + MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic); // We return literal nsStrings and nsCStrings pointing to the mapped data, // which means that we may still have references to the mapped data even // after this instance is destroyed. That means that we need to keep the // mapping alive until process shutdown, in order to be safe. - mMappedMemory = map->TakeMapping(); - mHandle = map->TakeHandle(); - - MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic); + mMap.setPersistent(); } SharedStringMap::SharedStringMap(SharedStringMapBuilder&& aBuilder) { - RefPtr map; - auto result = aBuilder.Finalize(map); - MOZ_RELEASE_ASSERT(result.isOk() && map); - - mMappedMemory = map->TakeMapping(); - mHandle = map->TakeHandle(); - + auto result = aBuilder.Finalize(mMap); + MOZ_RELEASE_ASSERT(result.isOk()); MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic); + mMap.setPersistent(); } -mozilla::ipc::SharedMemoryHandle SharedStringMap::CloneHandle() const { - return SharedMemory::CloneHandle(mHandle); +mozilla::ipc::FileDescriptor SharedStringMap::CloneFileDescriptor() const { + return mMap.cloneHandle(); } bool SharedStringMap::Has(const nsCString& aKey) { @@ -98,7 +84,7 @@ void SharedStringMapBuilder::Add(const nsCString& aKey, } Result SharedStringMapBuilder::Finalize( - RefPtr& aMap) { + loader::AutoMemMap& aMap) { using Header = SharedStringMap::Header; MOZ_ASSERT(mEntries.Count() == mKeyTable.Count()); diff --git a/dom/ipc/SharedStringMap.h b/dom/ipc/SharedStringMap.h index ca90cf1f33f6..cb14e09dbc1a 100644 --- a/dom/ipc/SharedStringMap.h +++ b/dom/ipc/SharedStringMap.h @@ -7,9 +7,9 @@ #ifndef dom_ipc_SharedStringMap_h #define dom_ipc_SharedStringMap_h +#include "mozilla/AutoMemMap.h" #include "mozilla/Result.h" #include "mozilla/dom/ipc/StringTable.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsTHashMap.h" namespace mozilla::dom::ipc { @@ -30,6 +30,8 @@ class SharedStringMapBuilder; * freed before process shutdown. Do not use it for short-lived mappings. */ class SharedStringMap { + using FileDescriptor = mozilla::ipc::FileDescriptor; + public: /** * The header at the beginning of the shared memory region describing its @@ -88,7 +90,7 @@ class SharedStringMap { // Note: These constructors are infallible on the premise that this class // is used primarily in cases where it is critical to platform // functionality. - explicit SharedStringMap(const mozilla::ipc::SharedMemoryHandle&, size_t); + explicit SharedStringMap(const FileDescriptor&, size_t); explicit SharedStringMap(SharedStringMapBuilder&&); /** @@ -146,23 +148,21 @@ class SharedStringMap { } /** - * Returns a copy of the read-only shared memory handle which backs the shared - * memory region for this map. The handle may be passed between processes, and - * used to construct new instances of SharedStringMap with the same data as - * this instance. + * Returns a copy of the read-only file descriptor which backs the shared + * memory region for this map. The file descriptor may be passed between + * processes, and used to construct new instances of SharedStringMap with + * the same data as this instance. */ - mozilla::ipc::SharedMemoryHandle CloneHandle() const; + FileDescriptor CloneFileDescriptor() const; - size_t MapSize() const { return mMappedMemory.size(); } + size_t MapSize() const { return mMap.size(); } protected: ~SharedStringMap() = default; private: // Type-safe getters for values in the shared memory region: - const Header& GetHeader() const { - return *reinterpret_cast(mMappedMemory.data()); - } + const Header& GetHeader() const { return mMap.get
()[0]; } RangedPtr Entries() const { return {reinterpret_cast(&GetHeader() + 1), EntryCount()}; @@ -171,22 +171,18 @@ class SharedStringMap { uint32_t EntryCount() const { return GetHeader().mEntryCount; } StringTable KeyTable() const { - const auto& header = GetHeader(); - return {{&mMappedMemory.data()[header.mKeyStringsOffset], + auto& header = GetHeader(); + return {{&mMap.get()[header.mKeyStringsOffset], header.mKeyStringsSize}}; } StringTable ValueTable() const { - const auto& header = GetHeader(); - return {{&mMappedMemory.data()[header.mValueStringsOffset], + auto& header = GetHeader(); + return {{&mMap.get()[header.mValueStringsOffset], header.mValueStringsSize}}; } - mozilla::ipc::SharedMemoryHandle mHandle; - // This is a leaked shared memory mapping (see the constructor definition for - // an explanation). It replaces AutoMemMap::setPersistent behavior as part of - // bug 1454816. - Span mMappedMemory; + loader::AutoMemMap mMap; }; /** @@ -205,10 +201,10 @@ class MOZ_RAII SharedStringMapBuilder { /** * Finalizes the binary representation of the map, writes it to a shared - * memory region, and then initializes the given SharedMemory with a reference + * memory region, and then initializes the given AutoMemMap with a reference * to the read-only copy of it. */ - Result Finalize(RefPtr& aMap); + Result Finalize(loader::AutoMemMap& aMap); private: using Entry = SharedStringMap::Entry; diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index a1fc6184a95f..39f874da07e2 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -21,7 +21,7 @@ #include "mozilla/ipc/GeckoChildProcessHost.h" #if defined(XP_LINUX) && defined(MOZ_SANDBOX) # include "mozilla/SandboxInfo.h" -# include "mozilla/ipc/SharedMemory.h" +# include "base/shared_memory.h" #endif #include "mozilla/Services.h" #include "mozilla/SSE.h" @@ -302,7 +302,7 @@ class NotifyGMPProcessLoadedTask : public Runnable { #if defined(XP_LINUX) && defined(MOZ_SANDBOX) if (SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia) && - ipc::SharedMemory::UsingPosixShm()) { + base::SharedMemory::UsingPosixShm()) { canProfile = false; } #endif diff --git a/gfx/thebes/SharedFontList-impl.h b/gfx/thebes/SharedFontList-impl.h index 4fd57df4925a..0603edac162b 100644 --- a/gfx/thebes/SharedFontList-impl.h +++ b/gfx/thebes/SharedFontList-impl.h @@ -7,14 +7,13 @@ #include "SharedFontList.h" -#include "base/process.h" +#include "base/shared_memory.h" + #include "gfxFontUtils.h" #include "nsClassHashtable.h" #include "nsTHashMap.h" #include "nsXULAppAPI.h" -#include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" -#include "mozilla/ipc/SharedMemory.h" // This is split out from SharedFontList.h because that header is included // quite widely (via gfxPlatformFontList.h, gfxTextRun.h, etc), and other code @@ -243,11 +242,11 @@ class FontList { * list has changed/grown since the child was first initialized). */ void ShareShmBlockToProcess(uint32_t aIndex, base::ProcessId aPid, - ipc::SharedMemory::Handle* aOut) { + base::SharedMemoryHandle* aOut) { MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); if (aIndex >= mReadOnlyShmems.Length()) { // Block index out of range - *aOut = ipc::SharedMemory::NULLHandle(); + *aOut = base::SharedMemory::NULLHandle(); return; } *aOut = mReadOnlyShmems[aIndex]->CloneHandle(); @@ -261,14 +260,14 @@ class FontList { * shared to the given process. This is used at child process startup * to pass the complete list at once. */ - void ShareBlocksToProcess(nsTArray* aBlocks, + void ShareBlocksToProcess(nsTArray* aBlocks, base::ProcessId aPid); - ipc::SharedMemory::Handle ShareBlockToProcess(uint32_t aIndex, - base::ProcessId aPid); + base::SharedMemoryHandle ShareBlockToProcess(uint32_t aIndex, + base::ProcessId aPid); void ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, - ipc::SharedMemory::Handle aHandle); + base::SharedMemoryHandle aHandle); /** * Support for memory reporter. */ @@ -300,11 +299,11 @@ class FontList { struct ShmBlock { // Takes ownership of aShmem. Note that in a child process, aShmem will be // mapped as read-only. - explicit ShmBlock(RefPtr&& aShmem) + explicit ShmBlock(mozilla::UniquePtr&& aShmem) : mShmem(std::move(aShmem)) {} // Get pointer to the mapped memory. - void* Memory() const { return mShmem->Memory(); } + void* Memory() const { return mShmem->memory(); } // Only the parent process does allocation, so only it will update this // field. Content processes read the value when checking Pointer validity. @@ -326,7 +325,7 @@ class FontList { return static_cast(Memory())->mBlockSize; } - RefPtr mShmem; + mozilla::UniquePtr mShmem; }; Header& GetHeader() const; @@ -372,7 +371,7 @@ class FontList { * Auxiliary array, used only in the parent process; holds read-only copies * of the shmem blocks; these are what will be shared to child processes. */ - nsTArray> mReadOnlyShmems; + nsTArray> mReadOnlyShmems; #ifdef XP_WIN // Bool array to track whether we have read face names from the name table. diff --git a/gfx/thebes/SharedFontList.cpp b/gfx/thebes/SharedFontList.cpp index a4f0b4030abb..459eb3a65e55 100644 --- a/gfx/thebes/SharedFontList.cpp +++ b/gfx/thebes/SharedFontList.cpp @@ -6,7 +6,6 @@ #include "gfxPlatformFontList.h" #include "gfxFontUtils.h" #include "gfxFont.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsReadableUtils.h" #include "prerror.h" #include "mozilla/dom/ContentChild.h" @@ -739,23 +738,22 @@ FontList::FontList(uint32_t aGeneration) { // SetXPCOMProcessAttributes. auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks(); for (auto& handle : blocks) { - auto newShm = MakeRefPtr(); + auto newShm = MakeUnique(); if (!newShm->IsHandleValid(handle)) { // Bail out and let UpdateShmBlocks try to do its thing below. break; } - if (!newShm->SetHandle(std::move(handle), - ipc::SharedMemory::OpenRights::RightsReadOnly)) { + if (!newShm->SetHandle(std::move(handle), true)) { MOZ_CRASH("failed to set shm handle"); } - if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) { + if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } - uint32_t size = static_cast(newShm->Memory())->mBlockSize; + uint32_t size = static_cast(newShm->memory())->mBlockSize; MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) { newShm->Unmap(); - if (!newShm->Map(size) || !newShm->Memory()) { + if (!newShm->Map(size) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } } @@ -802,16 +800,16 @@ FontList::Header& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS { bool FontList::AppendShmBlock(uint32_t aSizeNeeded) { MOZ_ASSERT(XRE_IsParentProcess()); uint32_t size = std::max(aSizeNeeded, SHM_BLOCK_SIZE); - auto newShm = MakeRefPtr(); - if (!newShm->CreateFreezable(size)) { + auto newShm = MakeUnique(); + if (!newShm->CreateFreezeable(size)) { MOZ_CRASH("failed to create shared memory"); return false; } - if (!newShm->Map(size) || !newShm->Memory()) { + if (!newShm->Map(size) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); return false; } - auto readOnly = MakeRefPtr(); + auto readOnly = MakeUnique(); if (!newShm->ReadOnlyCopy(readOnly.get())) { MOZ_CRASH("failed to create read-only copy"); return false; @@ -846,16 +844,15 @@ bool FontList::AppendShmBlock(uint32_t aSizeNeeded) { } void FontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, - ipc::SharedMemory::Handle aHandle) { + base::SharedMemoryHandle aHandle) { MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(mBlocks.Length() > 0); - auto newShm = MakeRefPtr(); + auto newShm = MakeUnique(); if (!newShm->IsHandleValid(aHandle)) { return; } - if (!newShm->SetHandle(std::move(aHandle), - ipc::SharedMemory::RightsReadOnly)) { + if (!newShm->SetHandle(std::move(aHandle), true)) { MOZ_CRASH("failed to set shm handle"); } @@ -866,15 +863,15 @@ void FontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, return; } - if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) { + if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } - uint32_t size = static_cast(newShm->Memory())->mBlockSize; + uint32_t size = static_cast(newShm->memory())->mBlockSize; MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) { newShm->Unmap(); - if (!newShm->Map(size) || !newShm->Memory()) { + if (!newShm->Map(size) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } } @@ -895,27 +892,26 @@ FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) { // If we have no existing blocks, we don't want a generation check yet; // the header in the first block will define the generation of this list uint32_t generation = aIndex == 0 ? 0 : GetGeneration(); - ipc::SharedMemory::Handle handle = ipc::SharedMemory::NULLHandle(); + base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock( generation, aIndex, &handle)) { return nullptr; } - auto newShm = MakeRefPtr(); + auto newShm = MakeUnique(); if (!newShm->IsHandleValid(handle)) { return nullptr; } - if (!newShm->SetHandle(std::move(handle), - ipc::SharedMemory::RightsReadOnly)) { + if (!newShm->SetHandle(std::move(handle), true)) { MOZ_CRASH("failed to set shm handle"); } - if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) { + if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } - uint32_t size = static_cast(newShm->Memory())->mBlockSize; + uint32_t size = static_cast(newShm->memory())->mBlockSize; MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) { newShm->Unmap(); - if (!newShm->Map(size) || !newShm->Memory()) { + if (!newShm->Map(size) || !newShm->memory()) { MOZ_CRASH("failed to map shared memory"); } } @@ -944,8 +940,8 @@ bool FontList::UpdateShmBlocks(bool aMustLock) MOZ_NO_THREAD_SAFETY_ANALYSIS { return result; } -void FontList::ShareBlocksToProcess( - nsTArray* aBlocks, base::ProcessId aPid) { +void FontList::ShareBlocksToProcess(nsTArray* aBlocks, + base::ProcessId aPid) { MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); for (auto& shmem : mReadOnlyShmems) { auto handle = shmem->CloneHandle(); @@ -961,8 +957,8 @@ void FontList::ShareBlocksToProcess( } } -ipc::SharedMemory::Handle FontList::ShareBlockToProcess(uint32_t aIndex, - base::ProcessId aPid) { +base::SharedMemoryHandle FontList::ShareBlockToProcess(uint32_t aIndex, + base::ProcessId aPid) { MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); MOZ_RELEASE_ASSERT(aIndex < mReadOnlyShmems.Length()); diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index e2161f92b5dc..d81a1a350a9c 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -3044,7 +3044,7 @@ void gfxPlatformFontList::CancelInitOtherFamilyNamesTask() { void gfxPlatformFontList::ShareFontListShmBlockToProcess( uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid, - mozilla::ipc::SharedMemory::Handle* aOut) { + base::SharedMemoryHandle* aOut) { auto list = SharedFontList(); if (!list) { return; @@ -3052,28 +3052,26 @@ void gfxPlatformFontList::ShareFontListShmBlockToProcess( if (!aGeneration || list->GetGeneration() == aGeneration) { list->ShareShmBlockToProcess(aIndex, aPid, aOut); } else { - *aOut = mozilla::ipc::SharedMemory::NULLHandle(); + *aOut = base::SharedMemory::NULLHandle(); } } void gfxPlatformFontList::ShareFontListToProcess( - nsTArray* aBlocks, - base::ProcessId aPid) { + nsTArray* aBlocks, base::ProcessId aPid) { auto list = SharedFontList(); if (list) { list->ShareBlocksToProcess(aBlocks, aPid); } } -mozilla::ipc::SharedMemory::Handle gfxPlatformFontList::ShareShmBlockToProcess( +base::SharedMemoryHandle gfxPlatformFontList::ShareShmBlockToProcess( uint32_t aIndex, base::ProcessId aPid) { MOZ_RELEASE_ASSERT(SharedFontList()); return SharedFontList()->ShareBlockToProcess(aIndex, aPid); } -void gfxPlatformFontList::ShmBlockAdded( - uint32_t aGeneration, uint32_t aIndex, - mozilla::ipc::SharedMemory::Handle aHandle) { +void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, + base::SharedMemoryHandle aHandle) { if (SharedFontList()) { AutoLock lock(mLock); SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle)); diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index efe7f669efc1..5a53d8b9ca06 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -19,7 +19,6 @@ #include "gfxPlatform.h" #include "SharedFontList.h" -#include "base/process.h" #include "nsIMemoryReporter.h" #include "mozilla/Attributes.h" #include "mozilla/EnumeratedArray.h" @@ -27,9 +26,10 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/RangedArray.h" #include "mozilla/RecursiveMutex.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsLanguageAtomService.h" +#include "base/shared_memory.h" + namespace mozilla { namespace fontlist { struct AliasData; @@ -355,19 +355,18 @@ class gfxPlatformFontList : public gfxFontInfoLoader { // be shared to the given processId. void ShareFontListShmBlockToProcess(uint32_t aGeneration, uint32_t aIndex, base::ProcessId aPid, - mozilla::ipc::SharedMemory::Handle* aOut); + base::SharedMemoryHandle* aOut); // Populate the array aBlocks with the complete list of shmem handles ready // to be shared to the given processId. - void ShareFontListToProcess( - nsTArray* aBlocks, - base::ProcessId aPid); + void ShareFontListToProcess(nsTArray* aBlocks, + base::ProcessId aPid); void ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, - mozilla::ipc::SharedMemory::Handle aHandle); + base::SharedMemoryHandle aHandle); - mozilla::ipc::SharedMemory::Handle ShareShmBlockToProcess( - uint32_t aIndex, base::ProcessId aPid); + base::SharedMemoryHandle ShareShmBlockToProcess(uint32_t aIndex, + base::ProcessId aPid); void SetCharacterMap(uint32_t aGeneration, uint32_t aFamilyIndex, bool aAlias, uint32_t aFaceIndex, const gfxSparseBitSet& aMap); diff --git a/intl/hyphenation/glue/nsHyphenationManager.cpp b/intl/hyphenation/glue/nsHyphenationManager.cpp index 01839373189a..90d40a733c48 100644 --- a/intl/hyphenation/glue/nsHyphenationManager.cpp +++ b/intl/hyphenation/glue/nsHyphenationManager.cpp @@ -330,11 +330,11 @@ void nsHyphenationManager::LoadAliases() { } void nsHyphenationManager::ShareHyphDictToProcess( - nsIURI* aURI, base::ProcessId aPid, - mozilla::ipc::SharedMemory::Handle* aOutHandle, uint32_t* aOutSize) { + nsIURI* aURI, base::ProcessId aPid, base::SharedMemoryHandle* aOutHandle, + uint32_t* aOutSize) { MOZ_ASSERT(XRE_IsParentProcess()); // aURI will be referring to an omnijar resource (otherwise just bail). - *aOutHandle = mozilla::ipc::SharedMemory::NULLHandle(); + *aOutHandle = base::SharedMemory::NULLHandle(); *aOutSize = 0; // Extract the locale code from the URI, and get the corresponding diff --git a/intl/hyphenation/glue/nsHyphenationManager.h b/intl/hyphenation/glue/nsHyphenationManager.h index 1565e2fd3e7d..5b1d0f9adbbb 100644 --- a/intl/hyphenation/glue/nsHyphenationManager.h +++ b/intl/hyphenation/glue/nsHyphenationManager.h @@ -6,9 +6,8 @@ #ifndef nsHyphenationManager_h__ #define nsHyphenationManager_h__ -#include "base/process.h" +#include "base/shared_memory.h" #include "mozilla/Omnijar.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsHashKeys.h" #include "nsAtomHashKeys.h" #include "nsInterfaceHashtable.h" @@ -28,7 +27,7 @@ class nsHyphenationManager : public nsIObserver { already_AddRefed GetHyphenator(nsAtom* aLocale); void ShareHyphDictToProcess(nsIURI* aURI, base::ProcessId aPid, - mozilla::ipc::SharedMemory::Handle* aOutHandle, + base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize); static nsHyphenationManager* Instance(); diff --git a/intl/hyphenation/glue/nsHyphenator.cpp b/intl/hyphenation/glue/nsHyphenator.cpp index e506a3a423d9..129f30f9d550 100644 --- a/intl/hyphenation/glue/nsHyphenator.cpp +++ b/intl/hyphenation/glue/nsHyphenator.cpp @@ -66,27 +66,27 @@ static const void* GetItemPtrFromJarURI(nsIJARURI* aJAR, uint32_t* aLength) { return nullptr; } -static RefPtr GetHyphDictFromParent(nsIURI* aURI, - uint32_t* aLength) { +static UniquePtr GetHyphDictFromParent(nsIURI* aURI, + uint32_t* aLength) { MOZ_ASSERT(!XRE_IsParentProcess()); - ipc::SharedMemory::Handle handle = ipc::SharedMemory::NULLHandle(); + base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); uint32_t size; MOZ_ASSERT(aURI); if (!dom::ContentChild::GetSingleton()->SendGetHyphDict(aURI, &handle, &size)) { return nullptr; } - RefPtr shm = MakeRefPtr(); + UniquePtr shm = MakeUnique(); if (!shm->IsHandleValid(handle)) { return nullptr; } - if (!shm->SetHandle(std::move(handle), ipc::SharedMemory::RightsReadOnly)) { + if (!shm->SetHandle(std::move(handle), true)) { return nullptr; } if (!shm->Map(size)) { return nullptr; } - char* addr = static_cast(shm->Memory()); + char* addr = static_cast(shm->memory()); if (!addr) { return nullptr; } @@ -94,21 +94,21 @@ static RefPtr GetHyphDictFromParent(nsIURI* aURI, return shm; } -static RefPtr CopyToShmem(const CompiledData* aData) { +static UniquePtr CopyToShmem(const CompiledData* aData) { MOZ_ASSERT(XRE_IsParentProcess()); // The shm-related calls here are not expected to fail, but if they do, // we'll just return null (as if the resource was unavailable) and proceed // without hyphenation. uint32_t size = mapped_hyph_compiled_data_size(aData); - RefPtr shm = MakeRefPtr(); - if (!shm->CreateFreezable(size)) { + UniquePtr shm = MakeUnique(); + if (!shm->CreateFreezeable(size)) { return nullptr; } if (!shm->Map(size)) { return nullptr; } - char* buffer = static_cast(shm->Memory()); + char* buffer = static_cast(shm->memory()); if (!buffer) { return nullptr; } @@ -121,8 +121,9 @@ static RefPtr CopyToShmem(const CompiledData* aData) { return shm; } -static RefPtr LoadFromURI(nsIURI* aURI, uint32_t* aLength, - bool aPrecompiled) { +static UniquePtr LoadFromURI(nsIURI* aURI, + uint32_t* aLength, + bool aPrecompiled) { MOZ_ASSERT(XRE_IsParentProcess()); nsCOMPtr channel; if (NS_FAILED(NS_NewChannel( @@ -145,14 +146,14 @@ static RefPtr LoadFromURI(nsIURI* aURI, uint32_t* aLength, } if (aPrecompiled) { - RefPtr shm = MakeRefPtr(); - if (!shm->CreateFreezable(available)) { + UniquePtr shm = MakeUnique(); + if (!shm->CreateFreezeable(available)) { return nullptr; } if (!shm->Map(available)) { return nullptr; } - char* buffer = static_cast(shm->Memory()); + char* buffer = static_cast(shm->memory()); if (!buffer) { return nullptr; } @@ -211,7 +212,7 @@ nsHyphenator::nsHyphenator(nsIURI* aURI, bool aHyphenateCapitalized) // compilation once per language per session. if (!precompiled && !XRE_IsParentProcess()) { uint32_t length; - RefPtr shm = GetHyphDictFromParent(aURI, &length); + UniquePtr shm = GetHyphDictFromParent(aURI, &length); if (shm) { // We don't need to validate mDict because the parent process // will have done so. @@ -245,7 +246,7 @@ nsHyphenator::nsHyphenator(nsIURI* aURI, bool aHyphenateCapitalized) UniquePtr data(mapped_hyph_compile_buffer( static_cast(ptr), length, false)); if (data) { - RefPtr shm = CopyToShmem(data.get()); + UniquePtr shm = CopyToShmem(data.get()); if (shm) { mDictSize = mapped_hyph_compiled_data_size(data.get()); mDict = AsVariant(std::move(shm)); @@ -259,14 +260,16 @@ nsHyphenator::nsHyphenator(nsIURI* aURI, bool aHyphenateCapitalized) // buffer; if we're a child, send a request to the parent for the // shared-memory copy (which it will load if not already available). if (XRE_IsParentProcess()) { - RefPtr shm = LoadFromURI(aURI, &length, precompiled); + UniquePtr shm = + LoadFromURI(aURI, &length, precompiled); if (shm) { mDictSize = length; mDict = AsVariant(std::move(shm)); return; } } else { - RefPtr shm = GetHyphDictFromParent(aURI, &length); + UniquePtr shm = + GetHyphDictFromParent(aURI, &length); if (shm) { // We don't need to validate mDict because the parent process // will have done so. @@ -312,7 +315,7 @@ nsHyphenator::nsHyphenator(nsIURI* aURI, bool aHyphenateCapitalized) UniquePtr data( mapped_hyph_compile_file(path.get(), false)); if (data) { - RefPtr shm = CopyToShmem(data.get()); + UniquePtr shm = CopyToShmem(data.get()); if (shm) { mDictSize = mapped_hyph_compiled_data_size(data.get()); mDict = AsVariant(std::move(shm)); @@ -334,7 +337,7 @@ nsHyphenator::nsHyphenator(nsIURI* aURI, bool aHyphenateCapitalized) bool nsHyphenator::IsValid() { return mDict.match( [](const void*& ptr) { return ptr != nullptr; }, - [](RefPtr& shm) { return shm != nullptr; }, + [](UniquePtr& shm) { return shm != nullptr; }, [](UniquePtr& hyph) { return hyph != nullptr; }); } @@ -446,9 +449,9 @@ void nsHyphenator::HyphenateWord(const nsAString& aString, uint32_t aStart, static_cast(ptr), mDictSize, utf8.BeginReading(), utf8.Length(), hyphenValues.Elements(), hyphenValues.Length()); }, - [&](RefPtr& shm) { + [&](UniquePtr& shm) { return mapped_hyph_find_hyphen_values_raw( - static_cast(shm->Memory()), mDictSize, + static_cast(shm->memory()), mDictSize, utf8.BeginReading(), utf8.Length(), hyphenValues.Elements(), hyphenValues.Length()); }, @@ -486,14 +489,14 @@ void nsHyphenator::HyphenateWord(const nsAString& aString, uint32_t aStart, } } -void nsHyphenator::CloneHandle(ipc::SharedMemory::Handle* aOutHandle, +void nsHyphenator::CloneHandle(base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) { // If the resource is invalid, or if we fail to share it to the child // process, we'll just bail out and continue without hyphenation; no need // for this to be a fatal error. - if (!mDict.is>()) { + if (!mDict.is>()) { return; } - *aOutHandle = mDict.as>()->CloneHandle(); + *aOutHandle = mDict.as>()->CloneHandle(); *aOutSize = mDictSize; } diff --git a/intl/hyphenation/glue/nsHyphenator.h b/intl/hyphenation/glue/nsHyphenator.h index 25473b8c1e47..7574d57fdbbe 100644 --- a/intl/hyphenation/glue/nsHyphenator.h +++ b/intl/hyphenation/glue/nsHyphenator.h @@ -6,8 +6,7 @@ #ifndef nsHyphenator_h__ #define nsHyphenator_h__ -#include "mozilla/ipc/SharedMemory.h" -#include "mozilla/RefPtr.h" +#include "base/shared_memory.h" #include "mozilla/UniquePtr.h" #include "mozilla/Variant.h" #include "nsCOMPtr.h" @@ -42,8 +41,7 @@ class nsHyphenator { nsresult Hyphenate(const nsAString& aText, nsTArray& aHyphens); - void CloneHandle(mozilla::ipc::SharedMemory::Handle* aOutHandle, - uint32_t* aOutSize); + void CloneHandle(base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize); private: ~nsHyphenator() = default; @@ -52,8 +50,8 @@ class nsHyphenator { nsTArray& aHyphens); mozilla::Variant, // shmem block - mozilla::UniquePtr // loaded by mapped_hyph + mozilla::UniquePtr, // shmem block + mozilla::UniquePtr // loaded by mapped_hyph > mDict; uint32_t mDictSize; // size of mDict data (not used if type is HyphDic) diff --git a/intl/strres/nsIStringBundle.idl b/intl/strres/nsIStringBundle.idl index fef8859c2b6a..a548517703a0 100644 --- a/intl/strres/nsIStringBundle.idl +++ b/intl/strres/nsIStringBundle.idl @@ -8,12 +8,14 @@ %{C++ #include "mozilla/MemoryReporting.h" -#include "mozilla/ipc/SharedMemory.h" namespace mozilla { namespace dom { class ContentParent; } +namespace ipc { +class FileDescriptor; +} } // Define Contractid and CID @@ -27,7 +29,7 @@ class ContentParent; %} [ptr] native ContentParent(mozilla::dom::ContentParent); -[ref] native SharedMemoryHandle(mozilla::ipc::SharedMemoryHandle); +[ref] native FileDescriptor(mozilla::ipc::FileDescriptor); native MallocSizeOf(mozilla::MallocSizeOf); [scriptable, builtinclass, uuid(D85A17C2-AA7C-11d2-9B8C-00805F8A16D9)] @@ -104,6 +106,6 @@ interface nsIStringBundleService : nsISupports [notxpcom, nostdcall] void sendContentBundles(in ContentParent aContentParent); [notxpcom, nostdcall] void registerContentBundle(in ACString aBundleURL, - [const] in SharedMemoryHandle aMapHandle, + [const] in FileDescriptor aMapFile, in size_t aMapSize); }; diff --git a/intl/strres/nsStringBundle.cpp b/intl/strres/nsStringBundle.cpp index 969a65ec8b55..31ecbc3d0019 100644 --- a/intl/strres/nsStringBundle.cpp +++ b/intl/strres/nsStringBundle.cpp @@ -209,8 +209,7 @@ class SharedStringBundle final : public nsStringBundleBase { * called in child processes, for bundles initially created in the parent * process. */ - void SetMapFile(const mozilla::ipc::SharedMemoryHandle& aHandle, - size_t aSize); + void SetMapFile(const FileDescriptor& aFile, size_t aSize); NS_DECL_ISUPPORTS_INHERITED NS_DECLARE_STATIC_IID_ACCESSOR(SHAREDSTRINGBUNDLE_IID) @@ -223,16 +222,16 @@ class SharedStringBundle final : public nsStringBundleBase { * parent process, and may be used to send shared string bundles to child * processes. */ - mozilla::ipc::SharedMemoryHandle CloneHandle() const { + FileDescriptor CloneFileDescriptor() const { MOZ_ASSERT(XRE_IsParentProcess()); - if (mMapHandle.isSome()) { - return mozilla::ipc::SharedMemory::CloneHandle(mMapHandle.ref()); + if (mMapFile.isSome()) { + return mMapFile.ref(); } - return mStringMap->CloneHandle(); + return mStringMap->CloneFileDescriptor(); } size_t MapSize() const { - if (mMapHandle.isSome()) { + if (mMapFile.isSome()) { return mMapSize; } if (mStringMap) { @@ -241,14 +240,14 @@ class SharedStringBundle final : public nsStringBundleBase { return 0; } - bool Initialized() const { return mStringMap || mMapHandle.isSome(); } + bool Initialized() const { return mStringMap || mMapFile.isSome(); } StringBundleDescriptor GetDescriptor() const { MOZ_ASSERT(Initialized()); StringBundleDescriptor descriptor; descriptor.bundleURL() = BundleURL(); - descriptor.mapHandle() = CloneHandle(); + descriptor.mapFile() = CloneFileDescriptor(); descriptor.mapSize() = MapSize(); return descriptor; } @@ -274,7 +273,7 @@ class SharedStringBundle final : public nsStringBundleBase { private: RefPtr mStringMap; - Maybe mMapHandle; + Maybe mMapFile; size_t mMapSize; }; @@ -522,9 +521,9 @@ nsresult nsStringBundle::LoadProperties() { nsresult SharedStringBundle::LoadProperties() { if (mStringMap) return NS_OK; - if (mMapHandle.isSome()) { - mStringMap = new SharedStringMap(mMapHandle.ref(), mMapSize); - mMapHandle.reset(); + if (mMapFile.isSome()) { + mStringMap = new SharedStringMap(mMapFile.ref(), mMapSize); + mMapFile.reset(); return NS_OK; } @@ -577,11 +576,10 @@ nsresult SharedStringBundle::LoadProperties() { return NS_OK; } -void SharedStringBundle::SetMapFile( - const mozilla::ipc::SharedMemoryHandle& aHandle, size_t aSize) { +void SharedStringBundle::SetMapFile(const FileDescriptor& aFile, size_t aSize) { MOZ_ASSERT(XRE_IsContentProcess()); mStringMap = nullptr; - mMapHandle.emplace(mozilla::ipc::SharedMemory::CloneHandle(aHandle)); + mMapFile.emplace(aFile); mMapSize = aSize; } @@ -829,8 +827,8 @@ void nsStringBundleService::SendContentBundles(ContentParent* aContentParent) { } void nsStringBundleService::RegisterContentBundle( - const nsACString& aBundleURL, - const mozilla::ipc::SharedMemoryHandle& aMapHandle, size_t aMapSize) { + const nsACString& aBundleURL, const FileDescriptor& aMapFile, + size_t aMapSize) { RefPtr proxy; bundleCacheEntry_t* cacheEntry = mBundleMap.Get(aBundleURL); @@ -848,7 +846,7 @@ void nsStringBundleService::RegisterContentBundle( auto bundle = MakeBundleRefPtr( PromiseFlatCString(aBundleURL).get()); - bundle->SetMapFile(aMapHandle, aMapSize); + bundle->SetMapFile(aMapFile, aMapSize); if (proxy) { proxy->Retarget(bundle); diff --git a/ipc/chromium/moz.build b/ipc/chromium/moz.build index d5485d4ee771..429c068d343d 100644 --- a/ipc/chromium/moz.build +++ b/ipc/chromium/moz.build @@ -47,6 +47,7 @@ if CONFIG["TARGET_KERNEL"] == "WINNT": "src/base/object_watcher.cc", "src/base/platform_thread_win.cc", "src/base/process_util_win.cc", + "src/base/shared_memory_win.cc", "src/base/sys_string_conversions_win.cc", "src/base/thread_local_win.cc", "src/base/time_win.cc", @@ -66,6 +67,7 @@ if CONFIG["TARGET_KERNEL"] != "WINNT": "src/base/message_pump_libevent.cc", "src/base/platform_thread_posix.cc", "src/base/process_util_posix.cc", + "src/base/shared_memory_posix.cc", "src/base/string16.cc", "src/base/thread_local_posix.cc", "src/base/waitable_event_posix.cc", diff --git a/ipc/chromium/src/base/shared_memory.h b/ipc/chromium/src/base/shared_memory.h new file mode 100644 index 000000000000..773557733496 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory.h @@ -0,0 +1,197 @@ +/* -*- 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_ + +#if defined(XP_UNIX) +# 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. + // If `unmap_view` is true, this unmaps the memory as a side-effect (and + // cleans up any OS-specific resources). + mozilla::UniqueFileHandle TakeHandle(bool unmap_view = true) { + mozilla::UniqueFileHandle handle = std::move(mapped_file_); + Close(unmap_view); + 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 XP_UNIX + // 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 XP_UNIX + // 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(XP_WIN) + // If true indicates this came from an external source so needs extra checks + // before being mapped. + bool external_section_ = false; +#elif !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_ diff --git a/ipc/chromium/src/base/shared_memory_posix.cc b/ipc/chromium/src/base/shared_memory_posix.cc new file mode 100644 index 000000000000..bfe20e343ac1 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_posix.cc @@ -0,0 +1,563 @@ +/* -*- 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. + +#include "base/shared_memory.h" + +#include +#include +#include +#include +#include + +#ifdef ANDROID +# include "mozilla/Ashmem.h" +#endif + +#ifdef XP_LINUX +# include "linux_memfd_defs.h" +#endif +#ifdef MOZ_WIDGET_GTK +# include "mozilla/WidgetUtilsGtk.h" +#endif + +#ifdef __FreeBSD__ +# include +#endif + +#ifdef MOZ_VALGRIND +# include +#endif + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "mozilla/Atomics.h" +#include "mozilla/Maybe.h" +#include "mozilla/ProfilerThreadSleep.h" +#include "mozilla/UniquePtrExtensions.h" +#include "prenv.h" + +namespace base { + +void SharedMemory::MappingDeleter::operator()(void* ptr) { + // Check that this isn't a default-constructed deleter. (`munmap` + // is specified to fail with `EINVAL` if the length is 0, so this + // assertion isn't load-bearing.) + DCHECK(mapped_size_ != 0); + munmap(ptr, mapped_size_); + // Guard against multiple calls of the same deleter, which shouldn't + // happen (but could, if `UniquePtr::reset` were used). Calling + // `munmap` with an incorrect non-zero length would be bad. + mapped_size_ = 0; +} + +SharedMemory::~SharedMemory() { + // This is almost equal to the default destructor, except for the + // warning message about unfrozen freezable memory. + Close(); +} + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(!mapped_file_); +#ifndef ANDROID + DCHECK(!frozen_file_); +#endif + + freezeable_ = false; + mapped_file_ = std::move(handle); + read_only_ = read_only; + // is_memfd_ only matters for freezing, which isn't possible + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != nullptr; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; } + +// static +bool SharedMemory::UsingPosixShm() { + // Undocumented feature of AppendPosixShmPrefix to reduce code + // duplication: if the string pointer is null, it's ignored. + return AppendPosixShmPrefix(nullptr, 0); +} + +#ifdef ANDROID + +// Android has its own shared memory API, ashmem. It doesn't support +// POSIX shm_open, and the memfd support (see below) also doesn't work +// because its SELinux policy prevents the procfs operations we'd use +// (see bug 1670277 for more details). + +// static +bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { + return false; +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + read_only_ = false; + + DCHECK(size > 0); + DCHECK(!mapped_file_); + + int fd = mozilla::android::ashmem_create(nullptr, size); + if (fd < 0) { + CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); + return false; + } + + mapped_file_.reset(fd); + max_size_ = size; + freezeable_ = freezeable; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(mapped_file_); + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + + if (mozilla::android::ashmem_setProt(mapped_file_.get(), PROT_READ) != 0) { + CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: " + << strerror(errno); + return false; + } + + mozilla::UniqueFileHandle ro_file = std::move(mapped_file_); + + freezeable_ = false; + ro_out->Close(); + ro_out->mapped_file_ = std::move(ro_file); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + + return true; +} + +#else // not Android + +// memfd_create is a nonstandard interface for creating anonymous +// shared memory accessible as a file descriptor but not tied to any +// filesystem. It first appeared in Linux 3.17, and was adopted by +// FreeBSD in version 13. + +# if !defined(HAVE_MEMFD_CREATE) && defined(XP_LINUX) && \ + defined(SYS_memfd_create) + +// Older libc versions (e.g., glibc before 2.27) don't have the +// wrapper, but we can supply our own; see `linux_memfd_defs.h`. + +static int memfd_create(const char* name, unsigned int flags) { + return syscall(SYS_memfd_create, name, flags); +} + +# define HAVE_MEMFD_CREATE 1 +# endif + +// memfd supports having "seals" applied to the file, to prevent +// various types of changes (which apply to all fds referencing the +// file). Unfortunately, we can't rely on F_SEAL_WRITE to implement +// Freeze(); see the comments in ReadOnlyCopy() below. +// +// Instead, to prevent a child process from regaining write access to +// a read-only copy, the OS must also provide a way to remove write +// permissions at the file descriptor level. This next section +// attempts to accomplish that. + +# ifdef HAVE_MEMFD_CREATE +# ifdef XP_LINUX +# define USE_MEMFD_CREATE 1 + +// To create a read-only duplicate of an fd, we can use procfs; the +// same operation could restore write access, but sandboxing prevents +// child processes from accessing /proc. +// +// (Note: if this ever changes to not use /proc, also reconsider how +// and if HaveMemfd should check whether this works.) + +static int DupReadOnly(int fd) { + std::string path = StringPrintf("/proc/self/fd/%d", fd); + // procfs opens probably won't EINTR, but checking for it can't hurt + return HANDLE_EINTR(open(path.c_str(), O_RDONLY | O_CLOEXEC)); +} + +# elif defined(__FreeBSD__) +# define USE_MEMFD_CREATE 1 + +// FreeBSD's Capsicum framework allows irrevocably restricting the +// operations permitted on a file descriptor. + +static int DupReadOnly(int fd) { + int rofd = dup(fd); + if (rofd < 0) { + return -1; + } + + cap_rights_t rights; + cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); + if (cap_rights_limit(rofd, &rights) < 0) { + int err = errno; + close(rofd); + errno = err; + return -1; + } + + return rofd; +} + +# else // unhandled OS +# warning "OS has memfd_create but no DupReadOnly implementation" +# endif // OS selection +# endif // HAVE_MEMFD_CREATE + +// Runtime detection for memfd support. +static bool HaveMemfd() { +# ifdef USE_MEMFD_CREATE + static const bool kHave = [] { + mozilla::UniqueFileHandle fd( + memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING)); + if (!fd) { + DCHECK_EQ(errno, ENOSYS); + return false; + } + + // Verify that DupReadOnly works; on Linux it's known to fail if: + // + // * SELinux assigns the memfd a type for which this process's + // domain doesn't have "open" permission; this is always the + // case on Android but could occur on desktop as well + // + // * /proc (used by the DupReadOnly implementation) isn't mounted, + // which is a configuration that the Tor Browser project is + // interested in as a way to reduce fingerprinting risk + // + // Sandboxed processes on Linux also can't use it if sandboxing + // has already been started, but that's expected. It should be + // safe for sandboxed child processes to use memfd even if an + // unsandboxed process couldn't freeze them, because freezing + // isn't allowed (or meaningful) for memory created by another + // process. + + if (!PR_GetEnv("MOZ_SANDBOXED")) { + mozilla::UniqueFileHandle rofd(DupReadOnly(fd.get())); + if (!rofd) { + CHROMIUM_LOG(WARNING) << "read-only dup failed (" << strerror(errno) + << "); not using memfd"; + return false; + } + } + return true; + }(); + return kHave; +# else + return false; +# endif // USE_MEMFD_CREATE +} + +// static +bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { + if (HaveMemfd()) { + return false; + } + // See also UsingPosixShm(). + if (!str) { + return true; + } + *str += '/'; +# ifdef MOZ_WIDGET_GTK + // The Snap package environment doesn't provide a private /dev/shm + // (it's used for communication with services like PulseAudio); + // instead AppArmor is used to restrict access to it. Anything with + // this prefix is allowed: + if (const char* snap = mozilla::widget::GetSnapInstanceName()) { + StringAppendF(str, "snap.%s.", snap); + } +# endif // XP_LINUX + // Hopefully the "implementation defined" name length limit is long + // enough for this. + StringAppendF(str, "org.mozilla.ipc.%d.", static_cast(pid)); + return true; +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + read_only_ = false; + + DCHECK(size > 0); + DCHECK(!mapped_file_); + DCHECK(!frozen_file_); + + mozilla::UniqueFileHandle fd; + mozilla::UniqueFileHandle frozen_fd; + bool is_memfd = false; + +# ifdef USE_MEMFD_CREATE + if (HaveMemfd()) { + const unsigned flags = MFD_CLOEXEC | (freezeable ? MFD_ALLOW_SEALING : 0); + fd.reset(memfd_create("mozilla-ipc", flags)); + if (!fd) { + // In general it's too late to fall back here -- in a sandboxed + // child process, shm_open is already blocked. And it shouldn't + // be necessary. + CHROMIUM_LOG(WARNING) << "failed to create memfd: " << strerror(errno); + return false; + } + is_memfd = true; + if (freezeable) { + frozen_fd.reset(DupReadOnly(fd.get())); + if (!frozen_fd) { + CHROMIUM_LOG(WARNING) + << "failed to create read-only memfd: " << strerror(errno); + return false; + } + } + } +# endif + + if (!fd) { + // Generic Unix: shm_open + shm_unlink + do { + // The names don't need to be unique, but it saves time if they + // usually are. + static mozilla::Atomic sNameCounter; + std::string name; + CHECK(AppendPosixShmPrefix(&name, getpid())); + StringAppendF(&name, "%zu", sNameCounter++); + // O_EXCL means the names being predictable shouldn't be a problem. + fd.reset(HANDLE_EINTR( + shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600))); + if (fd) { + if (freezeable) { + frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400))); + if (!frozen_fd) { + int open_err = errno; + shm_unlink(name.c_str()); + DLOG(FATAL) << "failed to re-open freezeable shm: " + << strerror(open_err); + return false; + } + } + if (shm_unlink(name.c_str()) != 0) { + // This shouldn't happen, but if it does: assume the file is + // in fact leaked, and bail out now while it's still 0-length. + DLOG(FATAL) << "failed to unlink shm: " << strerror(errno); + return false; + } + } + } while (!fd && errno == EEXIST); + } + + if (!fd) { + CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); + return false; + } + + mozilla::Maybe fallocateError; +# if defined(HAVE_POSIX_FALLOCATE) + // Using posix_fallocate will ensure that there's actually space for this + // file. Otherwise we end up with a sparse file that can give SIGBUS if we + // run out of space while writing to it. (This doesn't apply to memfd.) + if (!is_memfd) { + int rv; + // Avoid repeated interruptions of posix_fallocate by the profiler's + // SIGPROF sampling signal. Indicating "thread sleep" here means we'll + // get up to one interruption but not more. See bug 1658847 for more. + // This has to be scoped outside the HANDLE_RV_EINTR retry loop. + { + AUTO_PROFILER_THREAD_SLEEP; + + rv = HANDLE_RV_EINTR( + posix_fallocate(fd.get(), 0, static_cast(size))); + } + + // Some filesystems have trouble with posix_fallocate. For now, we must + // fallback ftruncate and accept the allocation failures like we do + // without posix_fallocate. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914 + if (rv != 0 && rv != EOPNOTSUPP && rv != EINVAL && rv != ENODEV) { + CHROMIUM_LOG(WARNING) + << "fallocate failed to set shm size: " << strerror(rv); + return false; + } + fallocateError = mozilla::Some(rv); + } +# endif + + // If posix_fallocate isn't supported / relevant for this type of + // file (either failed with an expected error, or wasn't attempted), + // then set the size with ftruncate: + if (fallocateError != mozilla::Some(0)) { + int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast(size))); + if (rv != 0) { + int ftruncate_errno = errno; + if (fallocateError) { + CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: " + << strerror(*fallocateError); + } + CHROMIUM_LOG(WARNING) + << "ftruncate failed to set shm size: " << strerror(ftruncate_errno); + return false; + } + } + + mapped_file_ = std::move(fd); + frozen_file_ = std::move(frozen_fd); + max_size_ = size; + freezeable_ = freezeable; + is_memfd_ = is_memfd; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(mapped_file_); + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + +# ifdef USE_MEMFD_CREATE +# ifdef MOZ_VALGRIND + // Valgrind allows memfd_create but doesn't understand F_ADD_SEALS. + static const bool haveSeals = RUNNING_ON_VALGRIND == 0; +# else + static const bool haveSeals = true; +# endif + static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS"); + if (is_memfd_ && haveSeals && useSeals) { + // Seals are added to the file as defense-in-depth. The primary + // method of access control is creating a read-only fd (using + // procfs in this case) and requiring that sandboxes processes not + // have access to /proc/self/fd to regain write permission; this + // is the same as with shm_open. + // + // Unfortunately, F_SEAL_WRITE is unreliable: if the process + // forked while there was a writeable mapping, it will inherit a + // copy of the mapping, which causes the seal to fail. + // + // (Also, in the future we may want to split this into separate + // classes for mappings and shared memory handles, which would + // complicate identifying the case where `F_SEAL_WRITE` would be + // possible even in the absence of races with fork.) + // + // However, Linux 5.1 added F_SEAL_FUTURE_WRITE, which prevents + // write operations afterwards, but existing writeable mappings + // are unaffected (similar to ashmem protection semantics). + + const int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; + int sealError = EINVAL; + +# ifdef F_SEAL_FUTURE_WRITE + sealError = + fcntl(mapped_file_.get(), F_ADD_SEALS, seals | F_SEAL_FUTURE_WRITE) == 0 + ? 0 + : errno; +# endif // F_SEAL_FUTURE_WRITE + if (sealError == EINVAL) { + sealError = + fcntl(mapped_file_.get(), F_ADD_SEALS, seals) == 0 ? 0 : errno; + } + if (sealError != 0) { + CHROMIUM_LOG(WARNING) << "failed to seal memfd: " << strerror(errno); + return false; + } + } +# else // !USE_MEMFD_CREATE + DCHECK(!is_memfd_); +# endif + + DCHECK(frozen_file_); + DCHECK(mapped_file_); + mapped_file_ = nullptr; + mozilla::UniqueFileHandle ro_file = std::move(frozen_file_); + + DCHECK(ro_file); + freezeable_ = false; + ro_out->Close(); + ro_out->mapped_file_ = std::move(ro_file); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + + return true; +} + +#endif // not Android + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +bool SharedMemory::Map(size_t bytes, void* fixed_address) { + if (!mapped_file_) { + return false; + } + DCHECK(!memory_); + + // Don't use MAP_FIXED when a fixed_address was specified, since that can + // replace pages that are alread mapped at that address. + void* mem = + mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_.get(), 0); + + if (mem == MAP_FAILED) { + CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno); + return false; + } + + if (fixed_address && mem != fixed_address) { + bool munmap_succeeded = munmap(mem, bytes) == 0; + DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno; + return false; + } + + memory_ = UniqueMapping(mem, MappingDeleter(bytes)); + return true; +} + +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = mmap(nullptr, size, PROT_NONE, + MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); + if (memory == MAP_FAILED) { + return nullptr; + } + munmap(memory, size); + return memory; +} + +SharedMemoryHandle SharedMemory::CloneHandle() { + freezeable_ = false; + const int new_fd = dup(mapped_file_.get()); + if (new_fd < 0) { + CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: " + << strerror(errno); + return nullptr; + } + return mozilla::UniqueFileHandle(new_fd); +} +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + mapped_file_ = nullptr; +#ifndef ANDROID + if (frozen_file_) { + CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen"; + frozen_file_ = nullptr; + } +#endif +} + +} // namespace base diff --git a/ipc/chromium/src/base/shared_memory_win.cc b/ipc/chromium/src/base/shared_memory_win.cc new file mode 100644 index 000000000000..8269912156a5 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_win.cc @@ -0,0 +1,266 @@ +/* -*- 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. + +#include "base/shared_memory.h" + +#include "base/logging.h" +#include "base/win_util.h" +#include "base/string_util.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/RandomNum.h" +#include "nsDebug.h" +#include "nsString.h" +#ifdef MOZ_MEMORY +# include "mozmemory_utils.h" +#endif + +namespace { +// NtQuerySection is an internal (but believed to be stable) API and the +// structures it uses are defined in nt_internals.h. +// So we have to define them ourselves. +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation, +} SECTION_INFORMATION_CLASS; + +typedef struct _SECTION_BASIC_INFORMATION { + PVOID BaseAddress; + ULONG Attributes; + LARGE_INTEGER Size; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef ULONG(__stdcall* NtQuerySectionType)( + HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass, + PVOID SectionInformation, ULONG SectionInformationLength, + PULONG ResultLength); + +// Checks if the section object is safe to map. At the moment this just means +// it's not an image section. +bool IsSectionSafeToMap(HANDLE handle) { + static NtQuerySectionType nt_query_section_func = + reinterpret_cast( + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection")); + DCHECK(nt_query_section_func); + + // The handle must have SECTION_QUERY access for this to succeed. + SECTION_BASIC_INFORMATION basic_information = {}; + ULONG status = + nt_query_section_func(handle, SectionBasicInformation, &basic_information, + sizeof(basic_information), nullptr); + if (status) { + return false; + } + + return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; +} + +} // namespace + +namespace base { + +void SharedMemory::MappingDeleter::operator()(void* ptr) { + UnmapViewOfFile(ptr); +} + +SharedMemory::~SharedMemory() = default; + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(!mapped_file_); + + external_section_ = true; + freezeable_ = false; // just in case + mapped_file_ = std::move(handle); + read_only_ = read_only; + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != nullptr; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; } + +// Wrapper around CreateFileMappingW for pagefile-backed regions. When out of +// memory, may attempt to stall and retry rather than returning immediately, in +// hopes that the page file is about to be expanded by Windows. (bug 1822383, +// bug 1716727) +// +// This method is largely a copy of the MozVirtualAlloc method from +// mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls, +// except re-purposed to handle CreateFileMapping. +static HANDLE MozCreateFileMappingW( + LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) { +#ifdef MOZ_MEMORY + constexpr auto IsOOMError = [] { + return ::GetLastError() == ERROR_COMMITMENT_LIMIT; + }; + + { + HANDLE handle = ::CreateFileMappingW( + INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, + dwMaximumSizeHigh, dwMaximumSizeLow, lpName); + if (MOZ_LIKELY(handle)) { + MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, + "::CreateFileMapping should return NULL, not " + "INVALID_HANDLE_VALUE, on failure"); + return handle; + } + + // We can't do anything for errors other than OOM. + if (!IsOOMError()) { + return nullptr; + } + } + + // Retry as many times as desired (possibly zero). + const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs(); + + const auto ret = + stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional { + HANDLE handle = ::CreateFileMappingW( + INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, + dwMaximumSizeHigh, dwMaximumSizeLow, lpName); + + if (handle) { + MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, + "::CreateFileMapping should return NULL, not " + "INVALID_HANDLE_VALUE, on failure"); + return handle; + } + + // Failure for some reason other than OOM. + if (!IsOOMError()) { + return nullptr; + } + + return std::nullopt; + }); + + return ret.value_or(nullptr); +#else + return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes, + flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, + lpName); +#endif +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + DCHECK(!mapped_file_); + read_only_ = false; + + // If the shared memory object has no DACL, any process can + // duplicate its handles with any access rights; e.g., re-add write + // access to a read-only handle. To prevent that, we give it an + // empty DACL, so that no process can do that. + SECURITY_ATTRIBUTES sa, *psa = nullptr; + SECURITY_DESCRIPTOR sd; + ACL dacl; + + if (freezeable) { + psa = &sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) || + NS_WARN_IF( + !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) || + NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) { + return false; + } + } + + mapped_file_.reset(MozCreateFileMappingW(psa, PAGE_READWRITE, 0, + static_cast(size), nullptr)); + if (!mapped_file_) return false; + + max_size_ = size; + freezeable_ = freezeable; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + + HANDLE ro_handle; + if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_.release(), + GetCurrentProcess(), &ro_handle, + GENERIC_READ | FILE_MAP_READ, false, + DUPLICATE_CLOSE_SOURCE)) { + // DUPLICATE_CLOSE_SOURCE applies even if there is an error. + return false; + } + + freezeable_ = false; + + ro_out->Close(); + ro_out->mapped_file_.reset(ro_handle); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + ro_out->external_section_ = external_section_; + + return true; +} + +bool SharedMemory::Map(size_t bytes, void* fixed_address) { + if (!mapped_file_) { + return false; + } + + if (external_section_ && !IsSectionSafeToMap(mapped_file_.get())) { + return false; + } + + void* mem = MapViewOfFileEx( + mapped_file_.get(), + read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, bytes, + fixed_address); + if (mem) { + MOZ_ASSERT(!fixed_address || mem == fixed_address, + "MapViewOfFileEx returned an expected address"); + memory_.reset(mem); + return true; + } + return false; +} + +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); + if (memory) { + VirtualFree(memory, 0, MEM_RELEASE); + } + return memory; +} + +SharedMemoryHandle SharedMemory::CloneHandle() { + freezeable_ = false; + HANDLE handle = INVALID_HANDLE_VALUE; + if (DuplicateHandle(GetCurrentProcess(), mapped_file_.get(), + GetCurrentProcess(), &handle, 0, false, + DUPLICATE_SAME_ACCESS)) { + return SharedMemoryHandle(handle); + } + NS_WARNING("DuplicateHandle Failed!"); + return nullptr; +} + +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + mapped_file_ = nullptr; +} + +} // namespace base diff --git a/ipc/glue/BigBuffer.cpp b/ipc/glue/BigBuffer.cpp index 2dbde130dba6..2045c84539b0 100644 --- a/ipc/glue/BigBuffer.cpp +++ b/ipc/glue/BigBuffer.cpp @@ -6,7 +6,6 @@ #include "mozilla/ipc/BigBuffer.h" -#include "chrome/common/ipc_message_utils.h" #include "mozilla/ipc/SharedMemory.h" #include "nsDebug.h" diff --git a/ipc/glue/IdleSchedulerChild.cpp b/ipc/glue/IdleSchedulerChild.cpp index 9a094c395f3a..31cfb5457dfb 100644 --- a/ipc/glue/IdleSchedulerChild.cpp +++ b/ipc/glue/IdleSchedulerChild.cpp @@ -32,9 +32,8 @@ void IdleSchedulerChild::Init(IdlePeriodState* aIdlePeriodState) { auto resolve = [&](std::tuple, uint32_t>&& aResult) { if (std::get<0>(aResult)) { - mActiveCounter->SetHandle(std::move(*std::get<0>(aResult)), - SharedMemory::RightsReadWrite); - mActiveCounter->Map(sizeof(int32_t)); + mActiveCounter.SetHandle(std::move(*std::get<0>(aResult)), false); + mActiveCounter.Map(sizeof(int32_t)); mChildId = std::get<1>(aResult); if (mChildId && mIdlePeriodState && mIdlePeriodState->IsActive()) { SetActive(); @@ -54,23 +53,23 @@ IPCResult IdleSchedulerChild::RecvIdleTime(uint64_t aId, TimeDuration aBudget) { } void IdleSchedulerChild::SetActive() { - if (mChildId && CanSend() && mActiveCounter->Memory()) { + if (mChildId && CanSend() && mActiveCounter.memory()) { ++(static_cast*>( - mActiveCounter->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]); - ++(static_cast*>(mActiveCounter->Memory())[mChildId]); + mActiveCounter.memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]); + ++(static_cast*>(mActiveCounter.memory())[mChildId]); } } bool IdleSchedulerChild::SetPaused() { - if (mChildId && CanSend() && mActiveCounter->Memory()) { - --(static_cast*>(mActiveCounter->Memory())[mChildId]); + if (mChildId && CanSend() && mActiveCounter.memory()) { + --(static_cast*>(mActiveCounter.memory())[mChildId]); // The following expression reduces the global activity count and checks if // it drops below the cpu counter limit. - return (static_cast*>(mActiveCounter->Memory()) - [NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER])-- == - static_cast*>( + return (static_cast*>( mActiveCounter - ->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER]; + .memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER])-- == + static_cast*>( + mActiveCounter.memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER]; } return false; diff --git a/ipc/glue/IdleSchedulerChild.h b/ipc/glue/IdleSchedulerChild.h index ce06eec6176f..cf93084bfc69 100644 --- a/ipc/glue/IdleSchedulerChild.h +++ b/ipc/glue/IdleSchedulerChild.h @@ -9,9 +9,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -#include "mozilla/RefPtr.h" #include "mozilla/ipc/PIdleSchedulerChild.h" -#include "mozilla/ipc/SharedMemory.h" class nsIIdlePeriod; @@ -58,7 +56,7 @@ class IdleSchedulerChild final : public PIdleSchedulerChild { friend class BackgroundChildImpl; // See IdleScheduleParent::sActiveChildCounter - RefPtr mActiveCounter = MakeRefPtr(); + base::SharedMemory mActiveCounter; IdlePeriodState* mIdlePeriodState = nullptr; diff --git a/ipc/glue/IdleSchedulerParent.cpp b/ipc/glue/IdleSchedulerParent.cpp index 27a3116f3854..7f91e9e754b1 100644 --- a/ipc/glue/IdleSchedulerParent.cpp +++ b/ipc/glue/IdleSchedulerParent.cpp @@ -17,7 +17,7 @@ namespace mozilla::ipc { -RefPtr IdleSchedulerParent::sActiveChildCounter = nullptr; +base::SharedMemory* IdleSchedulerParent::sActiveChildCounter = nullptr; std::bitset IdleSchedulerParent::sInUseChildCounters; LinkedList IdleSchedulerParent::sIdleAndGCRequests; @@ -107,9 +107,9 @@ void IdleSchedulerParent::CalculateNumIdleTasks() { std::min(std::max(sNumCPUs / sPrefConcurrentGCsCPUDivisor, 1u), sPrefConcurrentGCsMax); - if (sActiveChildCounter && sActiveChildCounter->Memory()) { + if (sActiveChildCounter && sActiveChildCounter->memory()) { static_cast*>( - sActiveChildCounter->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] = + sActiveChildCounter->memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] = static_cast(sMaxConcurrentIdleTasksInChildProcesses); } IdleSchedulerParent::Schedule(nullptr); @@ -120,13 +120,13 @@ IdleSchedulerParent::~IdleSchedulerParent() { // that is the case. if (mChildId) { sInUseChildCounters[mChildId] = false; - if (sActiveChildCounter && sActiveChildCounter->Memory() && + if (sActiveChildCounter && sActiveChildCounter->memory() && static_cast*>( - sActiveChildCounter->Memory())[mChildId]) { + sActiveChildCounter->memory())[mChildId]) { --static_cast*>( sActiveChildCounter - ->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]; - static_cast*>(sActiveChildCounter->Memory())[mChildId] = + ->memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]; + static_cast*>(sActiveChildCounter->memory())[mChildId] = 0; } } @@ -154,6 +154,7 @@ IdleSchedulerParent::~IdleSchedulerParent() { sChildProcessesAlive--; if (sChildProcessesAlive == 0) { MOZ_ASSERT(sIdleAndGCRequests.isEmpty()); + delete sActiveChildCounter; sActiveChildCounter = nullptr; if (sStarvationPreventer) { @@ -177,23 +178,24 @@ IPCResult IdleSchedulerParent::RecvInitForIdleUse( // Create a shared memory object which is shared across all the relevant // processes. if (!sActiveChildCounter) { - sActiveChildCounter = MakeRefPtr(); + sActiveChildCounter = new base::SharedMemory(); size_t shmemSize = NS_IDLE_SCHEDULER_COUNTER_ARRAY_LENGHT * sizeof(int32_t); if (sActiveChildCounter->Create(shmemSize) && sActiveChildCounter->Map(shmemSize)) { - memset(sActiveChildCounter->Memory(), 0, shmemSize); + memset(sActiveChildCounter->memory(), 0, shmemSize); sInUseChildCounters[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER] = true; sInUseChildCounters[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] = true; static_cast*>( sActiveChildCounter - ->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] = + ->memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] = static_cast(sMaxConcurrentIdleTasksInChildProcesses); } else { + delete sActiveChildCounter; sActiveChildCounter = nullptr; } } - Maybe activeCounter; - if (SharedMemory::Handle handle = + Maybe activeCounter; + if (SharedMemoryHandle handle = sActiveChildCounter ? sActiveChildCounter->CloneHandle() : nullptr) { activeCounter.emplace(std::move(handle)); } @@ -210,7 +212,7 @@ IPCResult IdleSchedulerParent::RecvInitForIdleUse( // If there wasn't an empty item, we'll fallback to 0. mChildId = unusedId; - aResolve(std::tuple&&, const uint32_t&>( + aResolve(std::tuple&&, const uint32_t&>( std::move(activeCounter), mChildId)); return IPC_OK(); } @@ -320,7 +322,7 @@ int32_t IdleSchedulerParent::ActiveCount() { if (sActiveChildCounter) { return (static_cast*>( sActiveChildCounter - ->Memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]); + ->memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]); } return 0; } diff --git a/ipc/glue/IdleSchedulerParent.h b/ipc/glue/IdleSchedulerParent.h index 5f57487e588c..a4ae130e28e9 100644 --- a/ipc/glue/IdleSchedulerParent.h +++ b/ipc/glue/IdleSchedulerParent.h @@ -11,9 +11,8 @@ #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" -#include "mozilla/RefPtr.h" #include "mozilla/ipc/PIdleSchedulerParent.h" -#include "mozilla/ipc/SharedMemory.h" +#include "base/shared_memory.h" #include #define NS_IDLE_SCHEDULER_COUNTER_ARRAY_LENGHT 1024 @@ -89,7 +88,7 @@ class IdleSchedulerParent final // This way the global activity can be checked in a fast way by just looking // at [0] value. // [1] is used for cpu count for child processes. - static RefPtr sActiveChildCounter; + static base::SharedMemory* sActiveChildCounter; // A bit is set if there is a child with child Id as the offset. // The bit is used to check per child specific activity counters in // sActiveChildCounter. diff --git a/ipc/glue/PIdleScheduler.ipdl b/ipc/glue/PIdleScheduler.ipdl index d0b94abaa2ca..1857c12eaf0a 100644 --- a/ipc/glue/PIdleScheduler.ipdl +++ b/ipc/glue/PIdleScheduler.ipdl @@ -1,11 +1,11 @@ -/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBackground; using mozilla::TimeDuration from "mozilla/TimeStamp.h"; -[MoveOnly] using mozilla::ipc::SharedMemoryHandle from "mozilla/ipc/SharedMemory.h"; +[MoveOnly] using base::SharedMemoryHandle from "base/shared_memory.h"; namespace mozilla { namespace ipc { @@ -32,7 +32,8 @@ namespace ipc { * process, child process informs the scheduler and the process is moved back * to the default queue. */ -async protocol PIdleScheduler { +async protocol PIdleScheduler +{ manager PBackground; child: @@ -43,8 +44,8 @@ parent: async RequestIdleTime(uint64_t id, TimeDuration budget); async IdleTimeUsed(uint64_t id); - // Child can send explicit Schedule message to parent if it thinks parent - // process might be able to let some other process to use idle time. + // Child can send explicit Schedule message to parent if it thinks parent process + // might be able to let some other process to use idle time. async Schedule(); // Note, these two messages can be sent even before InitForIdleUse. @@ -52,7 +53,7 @@ parent: async PrioritizedOperationDone(); // Ask if now would be a good time to GC - async RequestGC() returns(bool may_gc); + async RequestGC() returns (bool may_gc); // Let the parent know when we start a GC without asking first. async StartedGC(); @@ -60,10 +61,10 @@ parent: // Called for ending any kind of GC. async DoneGC(); - // This message is never sent. Each PIdleScheduler actor will stay alive as - // long as its PBackground manager. + // This message is never sent. Each PIdleScheduler actor will stay alive as long as + // its PBackground manager. async __delete__(); }; -} // namespace ipc -} // namespace mozilla +} // namespace ipc +} // namespace mozilla diff --git a/ipc/glue/ProcessChild.cpp b/ipc/glue/ProcessChild.cpp index d8159e2deaf8..44c0b26684d0 100644 --- a/ipc/glue/ProcessChild.cpp +++ b/ipc/glue/ProcessChild.cpp @@ -55,9 +55,9 @@ void ProcessChild::AddPlatformBuildID(geckoargs::ChildProcessArgs& aExtraArgs) { /* static */ bool ProcessChild::InitPrefs(int aArgc, char* aArgv[]) { - Maybe prefsHandle = + Maybe prefsHandle = geckoargs::sPrefsHandle.Get(aArgc, aArgv); - Maybe prefMapHandle = + Maybe prefMapHandle = geckoargs::sPrefMapHandle.Get(aArgc, aArgv); Maybe prefsLen = geckoargs::sPrefsLen.Get(aArgc, aArgv); Maybe prefMapSize = geckoargs::sPrefMapSize.Get(aArgc, aArgv); diff --git a/ipc/glue/ProcessUtils.h b/ipc/glue/ProcessUtils.h index 7049eae49054..4e92a11a51c2 100644 --- a/ipc/glue/ProcessUtils.h +++ b/ipc/glue/ProcessUtils.h @@ -12,10 +12,9 @@ #include "mozilla/GeckoArgs.h" #include "mozilla/ipc/FileDescriptor.h" -#include "mozilla/ipc/SharedMemory.h" +#include "base/shared_memory.h" #include "mozilla/Maybe.h" #include "mozilla/Preferences.h" -#include "mozilla/RefPtr.h" #include "nsXULAppAPI.h" namespace mozilla { @@ -39,9 +38,9 @@ class SharedPreferenceSerializer final { size_t GetPrefMapSize() const { return mPrefMapSize; } size_t GetPrefsLength() const { return mPrefsLength; } - const SharedMemoryHandle& GetPrefsHandle() const { return mPrefsHandle; } + const UniqueFileHandle& GetPrefsHandle() const { return mPrefsHandle; } - const SharedMemoryHandle& GetPrefMapHandle() const { return mPrefMapHandle; } + const UniqueFileHandle& GetPrefMapHandle() const { return mPrefMapHandle; } void AddSharedPrefCmdLineArgs(GeckoChildProcessHost& procHost, geckoargs::ChildProcessArgs& aExtraOpts) const; @@ -50,8 +49,8 @@ class SharedPreferenceSerializer final { DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer); size_t mPrefMapSize; size_t mPrefsLength; - SharedMemoryHandle mPrefMapHandle; - SharedMemoryHandle mPrefsHandle; + UniqueFileHandle mPrefMapHandle; + UniqueFileHandle mPrefsHandle; }; class SharedPreferenceDeserializer final { @@ -59,18 +58,18 @@ class SharedPreferenceDeserializer final { SharedPreferenceDeserializer(); ~SharedPreferenceDeserializer(); - bool DeserializeFromSharedMemory(SharedMemoryHandle aPrefsHandle, - SharedMemoryHandle aPrefMapHandle, + bool DeserializeFromSharedMemory(UniqueFileHandle aPrefsHandle, + UniqueFileHandle aPrefMapHandle, uint64_t aPrefsLen, uint64_t aPrefMapSize); - const SharedMemoryHandle& GetPrefMapHandle() const; + const FileDescriptor& GetPrefMapHandle() const; private: DISALLOW_COPY_AND_ASSIGN(SharedPreferenceDeserializer); - Maybe mPrefMapHandle; + Maybe mPrefMapHandle; Maybe mPrefsLen; Maybe mPrefMapSize; - RefPtr mShmem = MakeRefPtr(); + base::SharedMemory mShmem; }; // Generate command line argument to spawn a child process. If the shared memory @@ -80,7 +79,7 @@ void ExportSharedJSInit(GeckoChildProcessHost& procHost, // Initialize the content used by the JS engine during the initialization of a // JS::Runtime. -bool ImportSharedJSInit(SharedMemoryHandle aJsInitHandle, uint64_t aJsInitLen); +bool ImportSharedJSInit(UniqueFileHandle aJsInitHandle, uint64_t aJsInitLen); } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/ProcessUtils_common.cpp b/ipc/glue/ProcessUtils_common.cpp index a7abf9ac3c02..3c13be156e35 100644 --- a/ipc/glue/ProcessUtils_common.cpp +++ b/ipc/glue/ProcessUtils_common.cpp @@ -8,7 +8,6 @@ #include "mozilla/Preferences.h" #include "mozilla/GeckoArgs.h" -#include "mozilla/RefPtr.h" #include "mozilla/dom/RemoteType.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/UniquePtrExtensions.h" @@ -40,7 +39,8 @@ SharedPreferenceSerializer::SharedPreferenceSerializer( bool SharedPreferenceSerializer::SerializeToSharedMemory( const GeckoProcessType aDestinationProcessType, const nsACString& aDestinationRemoteType) { - mPrefMapHandle = Preferences::EnsureSnapshot(&mPrefMapSize); + mPrefMapHandle = + Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle(); bool destIsWebContent = aDestinationProcessType == GeckoProcessType_Content && @@ -52,31 +52,30 @@ bool SharedPreferenceSerializer::SerializeToSharedMemory( Preferences::SerializePreferences(prefs, destIsWebContent); mPrefsLength = prefs.Length(); - RefPtr shm = MakeRefPtr(); + base::SharedMemory shm; // Set up the shared memory. - if (!shm->Create(prefs.Length())) { + if (!shm.Create(prefs.Length())) { NS_ERROR("failed to create shared memory in the parent"); return false; } - if (!shm->Map(prefs.Length())) { + if (!shm.Map(prefs.Length())) { NS_ERROR("failed to map shared memory in the parent"); return false; } // Copy the serialized prefs into the shared memory. - memcpy(static_cast(shm->Memory()), prefs.get(), mPrefsLength); + memcpy(static_cast(shm.memory()), prefs.get(), mPrefsLength); - mPrefsHandle = shm->TakeHandleAndUnmap(); + mPrefsHandle = shm.TakeHandle(); return true; } void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs( mozilla::ipc::GeckoChildProcessHost& procHost, geckoargs::ChildProcessArgs& aExtraOpts) const { - SharedMemoryHandle prefsHandle = SharedMemory::CloneHandle(GetPrefsHandle()); + UniqueFileHandle prefsHandle = DuplicateFileHandle(GetPrefsHandle()); MOZ_RELEASE_ASSERT(prefsHandle, "failed to duplicate prefs handle"); - SharedMemoryHandle prefMapHandle = - SharedMemory::CloneHandle(GetPrefMapHandle()); + UniqueFileHandle prefMapHandle = DuplicateFileHandle(GetPrefMapHandle()); MOZ_RELEASE_ASSERT(prefMapHandle, "failed to duplicate pref map handle"); // Pass the handles and lengths via command line flags. @@ -95,7 +94,7 @@ SharedPreferenceDeserializer::~SharedPreferenceDeserializer() { } bool SharedPreferenceDeserializer::DeserializeFromSharedMemory( - SharedMemoryHandle aPrefsHandle, SharedMemoryHandle aPrefMapHandle, + UniqueFileHandle aPrefsHandle, UniqueFileHandle aPrefMapHandle, uint64_t aPrefsLen, uint64_t aPrefMapSize) { if (!aPrefsHandle || !aPrefMapHandle || !aPrefsLen || !aPrefMapSize) { return false; @@ -112,23 +111,21 @@ bool SharedPreferenceDeserializer::DeserializeFromSharedMemory( Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize); // Set up early prefs from the shared memory. - if (!mShmem->SetHandle(std::move(aPrefsHandle), - SharedMemory::RightsReadOnly)) { + if (!mShmem.SetHandle(std::move(aPrefsHandle), /* read_only */ true)) { NS_ERROR("failed to open shared memory in the child"); return false; } - if (!mShmem->Map(*mPrefsLen)) { + if (!mShmem.Map(*mPrefsLen)) { NS_ERROR("failed to map shared memory in the child"); return false; } - Preferences::DeserializePreferences(static_cast(mShmem->Memory()), + Preferences::DeserializePreferences(static_cast(mShmem.memory()), *mPrefsLen); return true; } -const SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefMapHandle() - const { +const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const { MOZ_ASSERT(mPrefMapHandle.isSome()); return mPrefMapHandle.ref(); @@ -141,12 +138,12 @@ void ExportSharedJSInit(mozilla::ipc::GeckoChildProcessHost& procHost, return; #else auto& shmem = xpc::SelfHostedShmem::GetSingleton(); - SharedMemoryHandle handle = SharedMemory::CloneHandle(shmem.Handle()); + UniqueFileHandle handle = DuplicateFileHandle(shmem.Handle()); size_t len = shmem.Content().Length(); // If the file is not found or the content is empty, then we would start the // content process without this optimization. - if (!SharedMemory::IsHandleValid(handle) || !len) { + if (!handle || !len) { return; } @@ -156,7 +153,7 @@ void ExportSharedJSInit(mozilla::ipc::GeckoChildProcessHost& procHost, #endif } -bool ImportSharedJSInit(SharedMemoryHandle aJsInitHandle, uint64_t aJsInitLen) { +bool ImportSharedJSInit(UniqueFileHandle aJsInitHandle, uint64_t aJsInitLen) { // This is an optimization, and as such we can safely recover if the command // line argument are not provided. if (!aJsInitLen || !aJsInitHandle) { diff --git a/ipc/glue/RawShmem.h b/ipc/glue/RawShmem.h index 212cc3b7612f..e23a109e554c 100644 --- a/ipc/glue/RawShmem.h +++ b/ipc/glue/RawShmem.h @@ -6,7 +6,6 @@ #ifndef MOZILLA_IPC_RAWSHMEM_H_ #define MOZILLA_IPC_RAWSHMEM_H_ -#include "chrome/common/ipc_message_utils.h" #include "mozilla/ipc/SharedMemory.h" #include "mozilla/Span.h" #include diff --git a/ipc/glue/SharedMemory.cpp b/ipc/glue/SharedMemory.cpp index 689fffff5901..94a81aea0d38 100644 --- a/ipc/glue/SharedMemory.cpp +++ b/ipc/glue/SharedMemory.cpp @@ -4,14 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* This source code was derived from Chromium code, and as such is also subject - * to the [Chromium license](ipc/chromium/src/LICENSE). */ - #include "mozilla/ipc/SharedMemory.h" -#include - -#include "chrome/common/ipc_message_utils.h" #include "mozilla/Atomics.h" #include "nsIMemoryReporter.h" @@ -59,7 +53,6 @@ SharedMemory::SharedMemory() : mAllocSize(0), mMappedSize(0) { SharedMemory::~SharedMemory() { Unmap(); CloseHandle(); - ResetImpl(); MOZ_ASSERT(gShmemAllocated >= mAllocSize, "Can't destroy more than allocated"); @@ -67,108 +60,8 @@ SharedMemory::~SharedMemory() { mAllocSize = 0; } -bool SharedMemory::Create(size_t aNBytes, bool freezable) { - MOZ_ASSERT(!IsValid(), "already initialized"); - bool ok = CreateImpl(aNBytes, freezable); - if (ok) { - mAllocSize = aNBytes; - mFreezable = freezable; - mReadOnly = false; - mExternalHandle = false; - gShmemAllocated += mAllocSize; - } - return ok; -} - -bool SharedMemory::Map(size_t aNBytes, void* fixedAddress) { - if (!mHandle) { - return false; - } - MOZ_ASSERT(!mMemory, "can't map memory when a mapping already exists"); - auto address = MapImpl(aNBytes, fixedAddress); - if (address) { - mMappedSize = aNBytes; - mMemory = UniqueMapping(*address, MappingDeleter(mMappedSize)); - gShmemMapped += mMappedSize; - return true; - } - return false; -} - -void SharedMemory::Unmap() { - if (!mMemory) { - return; - } - MOZ_ASSERT(gShmemMapped >= mMappedSize, "Can't unmap more than mapped"); - mMemory = nullptr; - gShmemMapped -= std::exchange(mMappedSize, 0); -} - -void* SharedMemory::Memory() const { -#ifdef FUZZING - return SharedMemoryFuzzer::MutateSharedMemory(mMemory.get(), mAllocSize); -#else - return mMemory.get(); -#endif -} - -Span SharedMemory::TakeMapping() { - // NOTE: this doesn't reduce gShmemMapped since it _is_ still mapped memory - // (and will be until the process terminates). - return {static_cast(mMemory.release()), - std::exchange(mMappedSize, 0)}; -} - -SharedMemory::Handle SharedMemory::TakeHandle() { - gShmemAllocated -= mAllocSize; - mAllocSize = 0; - return std::move(mHandle); -} - -bool SharedMemory::SetHandle(Handle aHandle, OpenRights aRights) { - MOZ_ASSERT(!IsValid(), - "SetHandle cannot be called when a valid handle is already held"); - ResetImpl(); - mHandle = std::move(aHandle); - mAllocSize = 0; - mMappedSize = 0; - mFreezable = false; - mReadOnly = aRights == OpenRights::RightsReadOnly; - mExternalHandle = true; - return true; -} - -bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { - MOZ_ASSERT(mHandle); - MOZ_ASSERT(!mReadOnly); - MOZ_ASSERT(mFreezable); - if (ro_out == this) { - MOZ_ASSERT( - !mMemory, - "Memory cannot be mapped when creating a read-only copy of this."); - } - auto handle = ReadOnlyCopyImpl(); - auto allocSize = mAllocSize; - TakeHandle(); - if (!handle) { - return false; - } - mFreezable = false; - - // Reset ro_out (unmapping, etc). - ro_out->~SharedMemory(); - - ro_out->mHandle = std::move(*handle); - ro_out->mAllocSize = allocSize; - gShmemAllocated += ro_out->mAllocSize; - ro_out->mReadOnly = true; - ro_out->mFreezable = false; - ro_out->mExternalHandle = mExternalHandle; - return true; -} - bool SharedMemory::WriteHandle(IPC::MessageWriter* aWriter) { - Handle handle = CloneHandle(mHandle); + Handle handle = CloneHandle(); if (!handle) { return false; } @@ -183,9 +76,6 @@ bool SharedMemory::ReadHandle(IPC::MessageReader* aReader) { } void SharedMemory::Protect(char* aAddr, size_t aSize, int aRights) { - // Don't allow altering of rights on freezable shared memory handles. - MOZ_ASSERT(!mFreezable); - char* memStart = reinterpret_cast(Memory()); if (!memStart) MOZ_CRASH("SharedMemory region points at NULL!"); char* memEnd = memStart + Size(); @@ -208,4 +98,37 @@ size_t SharedMemory::PageAlignedSize(size_t aSize) { return pageSize * nPagesNeeded; } +bool SharedMemory::Create(size_t aNBytes) { + bool ok = CreateImpl(aNBytes); + if (ok) { + mAllocSize = aNBytes; + gShmemAllocated += mAllocSize; + } + return ok; +} + +bool SharedMemory::Map(size_t aNBytes, void* fixedAddress) { + bool ok = MapImpl(aNBytes, fixedAddress); + if (ok) { + mMappedSize = aNBytes; + gShmemMapped += mMappedSize; + } + return ok; +} + +void SharedMemory::Unmap() { + MOZ_ASSERT(gShmemMapped >= mMappedSize, "Can't unmap more than mapped"); + UnmapImpl(mMappedSize); + gShmemMapped -= mMappedSize; + mMappedSize = 0; +} + +void* SharedMemory::Memory() const { +#ifdef FUZZING + return SharedMemoryFuzzer::MutateSharedMemory(MemoryImpl(), mAllocSize); +#else + return MemoryImpl(); +#endif +} + } // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemory.h b/ipc/glue/SharedMemory.h index 8a99fa0c4013..f6fc0d8bebed 100644 --- a/ipc/glue/SharedMemory.h +++ b/ipc/glue/SharedMemory.h @@ -4,56 +4,27 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* This source code was derived from Chromium code, and as such is also subject - * to the [Chromium license](ipc/chromium/src/LICENSE). */ - #ifndef mozilla_ipc_SharedMemory_h #define mozilla_ipc_SharedMemory_h #include -#include "mozilla/Maybe.h" -#include "mozilla/UniquePtrExtensions.h" +#include "chrome/common/ipc_message_utils.h" +#include "mozilla/Assertions.h" #include "nsISupportsImpl.h" // NS_INLINE_DECL_REFCOUNTING -#if !(defined(XP_DARWIN) || defined(XP_WIN)) -# include +#ifdef XP_DARWIN +# include "mozilla/ipc/SharedMemoryImpl_mach.h" +#else +# include "mozilla/ipc/SharedMemoryImpl_chromium.h" #endif -namespace IPC { -class MessageWriter; -class MessageReader; -} // namespace IPC - -namespace { -enum Rights { RightsNone = 0, RightsRead = 1 << 0, RightsWrite = 1 << 1 }; -} // namespace - namespace mozilla::ipc { -// Rust Bindgen code doesn't actually use these types, but `UniqueFileHandle` -// and `UniqueMachSendRight` aren't defined and some headers need to type check, -// so we define a dummy type. -#if defined(RUST_BINDGEN) -using SharedMemoryHandle = void*; -#elif defined(XP_DARWIN) -using SharedMemoryHandle = mozilla::UniqueMachSendRight; -#else -using SharedMemoryHandle = mozilla::UniqueFileHandle; -#endif - -class SharedMemory { +class SharedMemory : public SharedMemoryImpl { ~SharedMemory(); - /// # Provided methods public: - using Handle = SharedMemoryHandle; - - enum OpenRights { - RightsReadOnly = RightsRead, - RightsReadWrite = RightsRead | RightsWrite, - }; - SharedMemory(); // bug 1168843, compositor thread may create shared memory instances that are @@ -62,123 +33,32 @@ class SharedMemory { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedMemory) size_t Size() const { return mMappedSize; } - size_t MaxSize() const { return mAllocSize; } - - bool Create(size_t nBytes, bool freezable = false); - bool Map(size_t nBytes, void* fixedAddress = nullptr); - void Unmap(); - void* Memory() const; - /// Take the mapping memory. - /// - /// This prevents unmapping the memory. - Span TakeMapping(); - - Handle TakeHandleAndUnmap() { - auto handle = TakeHandle(); - Unmap(); - return handle; - } - Handle TakeHandle(); - Handle CloneHandle() { - mFreezable = false; - return SharedMemory::CloneHandle(mHandle); - } void CloseHandle() { TakeHandle(); } - bool SetHandle(Handle aHandle, OpenRights aRights); - bool IsValid() const { return IsHandleValid(mHandle); } - static bool IsHandleValid(const Handle& aHandle) { - // `operator!=` had ambiguous overload resolution with the windows Handle - // (mozilla::UniqueFileHandle), so invert `operator==` instead. - return !(aHandle == NULLHandle()); - } - static Handle NULLHandle() { return nullptr; } - - bool CreateFreezable(size_t nBytes) { return Create(nBytes, true); } - - [[nodiscard]] bool Freeze() { - Unmap(); - return ReadOnlyCopy(this); - } bool WriteHandle(IPC::MessageWriter* aWriter); bool ReadHandle(IPC::MessageReader* aReader); void Protect(char* aAddr, size_t aSize, int aRights); - static size_t PageAlignedSize(size_t aSize); - - /// Public methods which should be defined as part of each implementation. - public: - [[nodiscard]] bool ReadOnlyCopy(SharedMemory* ro_out); - static void SystemProtect(char* aAddr, size_t aSize, int aRights); [[nodiscard]] static bool SystemProtectFallible(char* aAddr, size_t aSize, int aRights); - static Handle CloneHandle(const Handle& aHandle); static size_t SystemPageSize(); - static void* FindFreeAddressSpace(size_t size); + static size_t PageAlignedSize(size_t aSize); + + bool Create(size_t nBytes); + bool Map(size_t nBytes, void* fixedAddress = nullptr); + void Unmap(); + void* Memory() const; - /// Private methods which should be defined as part of each implementation. private: - bool CreateImpl(size_t size, bool freezable); - Maybe MapImpl(size_t nBytes, void* fixedAddress); - static void UnmapImpl(size_t nBytes, void* address); - Maybe ReadOnlyCopyImpl(); - void ResetImpl(); - - /// Common members - private: - struct MappingDeleter { - size_t mMappedSize = 0; - explicit MappingDeleter(size_t size) : mMappedSize(size) {} - MappingDeleter() = default; - void operator()(void* ptr) { - MOZ_ASSERT(mMappedSize != 0); - UnmapImpl(mMappedSize, ptr); - // Guard against multiple calls of the same deleter, which shouldn't - // happen (but could, if `UniquePtr::reset` were used). Calling - // `munmap` with an incorrect non-zero length would be bad. - mMappedSize = 0; - } - }; - using UniqueMapping = mozilla::UniquePtr; - - // The held handle, if any. - Handle mHandle = NULLHandle(); // The size of the shmem region requested in Create(), if // successful. SharedMemory instances that are opened from a // foreign handle have an alloc size of 0, even though they have // access to the alloc-size information. size_t mAllocSize; - // The memory mapping, if any. - UniqueMapping mMemory; // The size of the region mapped in Map(), if successful. All // SharedMemorys that are mapped have a non-zero mapped size. size_t mMappedSize; - // Whether the handle held is freezable. - bool mFreezable = false; - // Whether the handle held is read-only. - bool mReadOnly = false; - // Whether the handle held is external (set with `SetHandle`). - bool mExternalHandle = false; - -#if !defined(XP_DARWIN) && !defined(XP_WIN) - /// # Unix/POSIX-specific methods and members. - public: - // 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(); - -# if !defined(ANDROID) && !defined(RUST_BINDGEN) - private: - mozilla::UniqueFileHandle mFrozenFile; - bool mIsMemfd = false; -# endif -#endif }; } // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemoryImpl_chromium.cpp b/ipc/glue/SharedMemoryImpl_chromium.cpp new file mode 100644 index 000000000000..77270d103f22 --- /dev/null +++ b/ipc/glue/SharedMemoryImpl_chromium.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ipc/SharedMemoryImpl_chromium.h" + +namespace mozilla::ipc { + +SharedMemoryImpl::Handle SharedMemoryImpl::CloneHandle() { + return mSharedMemory.CloneHandle(); +} + +SharedMemoryImpl::Handle SharedMemoryImpl::TakeHandle() { + return mSharedMemory.TakeHandle(false); +} + +bool SharedMemoryImpl::IsHandleValid(const Handle& aHandle) const { + return base::SharedMemory::IsHandleValid(aHandle); +} + +bool SharedMemoryImpl::SetHandle(Handle aHandle, OpenRights aRights) { + return mSharedMemory.SetHandle(std::move(aHandle), aRights == RightsReadOnly); +} + +SharedMemoryImpl::Handle SharedMemoryImpl::NULLHandle() { + return base::SharedMemory::NULLHandle(); +} + +void* SharedMemoryImpl::FindFreeAddressSpace(size_t size) { + return base::SharedMemory::FindFreeAddressSpace(size); +} + +bool SharedMemoryImpl::CreateImpl(size_t size) { + return mSharedMemory.Create(size); +} + +bool SharedMemoryImpl::MapImpl(size_t nBytes, void* fixedAddress) { + return mSharedMemory.Map(nBytes, fixedAddress); +} + +void SharedMemoryImpl::UnmapImpl(size_t mappedSize) { mSharedMemory.Unmap(); } + +void* SharedMemoryImpl::MemoryImpl() const { return mSharedMemory.memory(); } + +} // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemoryImpl_chromium.h b/ipc/glue/SharedMemoryImpl_chromium.h new file mode 100644 index 000000000000..c5027c98042e --- /dev/null +++ b/ipc/glue/SharedMemoryImpl_chromium.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_SharedMemoryImpl_posix_h +#define mozilla_ipc_SharedMemoryImpl_posix_h + +#include "base/shared_memory.h" + +namespace { +enum Rights { RightsNone = 0, RightsRead = 1 << 0, RightsWrite = 1 << 1 }; +} // namespace + +namespace mozilla::ipc { + +class SharedMemoryImpl { + public: + using Handle = base::SharedMemoryHandle; + + enum OpenRights { + RightsReadOnly = RightsRead, + RightsReadWrite = RightsRead | RightsWrite, + }; + + Handle CloneHandle(); + Handle TakeHandle(); + + bool IsHandleValid(const Handle& aHandle) const; + bool SetHandle(Handle aHandle, OpenRights aRights); + + static Handle NULLHandle(); + static void* FindFreeAddressSpace(size_t size); + + protected: + SharedMemoryImpl() = default; + ~SharedMemoryImpl() {} + + bool CreateImpl(size_t size); + bool MapImpl(size_t nBytes, void* fixedAddress); + void UnmapImpl(size_t mappedSize); + void* MemoryImpl() const; + + private: + base::SharedMemory mSharedMemory; +}; + +} // namespace mozilla::ipc + +#endif diff --git a/ipc/glue/SharedMemoryImpl_mach.cpp b/ipc/glue/SharedMemoryImpl_mach.cpp new file mode 100644 index 000000000000..20f0de9b9b2f --- /dev/null +++ b/ipc/glue/SharedMemoryImpl_mach.cpp @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ipc/SharedMemoryImpl_mach.h" + +#include + +#include +#include +#if defined(XP_IOS) +# include +# define mach_vm_address_t vm_address_t +# define mach_vm_map vm_map +# define mach_vm_read vm_read +# define mach_vm_region_recurse vm_region_recurse_64 +# define mach_vm_size_t vm_size_t +#else +# include +#endif +#include +#include + +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Printf.h" +#include "mozilla/StaticMutex.h" + +#ifdef DEBUG +# define LOG_ERROR(str, args...) \ + PR_BEGIN_MACRO \ + mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \ + NS_WARNING(msg.get()); \ + PR_END_MACRO +#else +# define LOG_ERROR(str, args...) \ + do { /* nothing */ \ + } while (0) +#endif + +namespace mozilla::ipc { + +SharedMemoryImpl::SharedMemoryImpl() + : mPort(MACH_PORT_NULL), mMemory(nullptr), mOpenRights(RightsReadWrite) {} + +bool SharedMemoryImpl::SetHandle(Handle aHandle, OpenRights aRights) { + MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized"); + + mPort = std::move(aHandle); + mOpenRights = aRights; + return true; +} + +static inline void* toPointer(mach_vm_address_t address) { + return reinterpret_cast(static_cast(address)); +} + +static inline mach_vm_address_t toVMAddress(void* pointer) { + return static_cast(reinterpret_cast(pointer)); +} + +bool SharedMemoryImpl::CreateImpl(size_t size) { + MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized"); + + memory_object_size_t memoryObjectSize = round_page(size); + + kern_return_t kr = + mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, 0, + MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT, + getter_Transfers(mPort), MACH_PORT_NULL); + if (kr != KERN_SUCCESS || memoryObjectSize < round_page(size)) { + LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", size, + mach_error_string(kr), kr); + TakeHandle(); + return false; + } + return true; +} + +bool SharedMemoryImpl::MapImpl(size_t size, void* fixedAddress) { + MOZ_ASSERT(mMemory == nullptr); + + if (MACH_PORT_NULL == mPort) { + return false; + } + + kern_return_t kr; + mach_vm_address_t address = toVMAddress(fixedAddress); + + 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, + fixedAddress ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE, + mPort.get(), 0, false, vmProtection, vmProtection, + VM_INHERIT_NONE); + if (kr != KERN_SUCCESS) { + if (!fixedAddress) { + LOG_ERROR( + "Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n", + size, mach_task_self(), mach_port_t(mPort.get()), + mach_error_string(kr), kr); + } + return false; + } + + if (fixedAddress && fixedAddress != 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(), mach_port_t(mPort.get()), + mach_error_string(kr), kr); + } + return false; + } + + mMemory = toPointer(address); + return true; +} + +void* SharedMemoryImpl::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); +} + +auto SharedMemoryImpl::CloneHandle() -> Handle { + return mozilla::RetainMachSendRight(mPort.get()); +} + +auto SharedMemoryImpl::TakeHandle() -> Handle { + mOpenRights = RightsReadWrite; + return std::move(mPort); +} + +void SharedMemoryImpl::UnmapImpl(size_t mappedSize) { + if (!mMemory) { + return; + } + vm_address_t address = toVMAddress(mMemory); + kern_return_t kr = + vm_deallocate(mach_task_self(), address, round_page(mappedSize)); + if (kr != KERN_SUCCESS) { + LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", + mach_error_string(kr), kr); + return; + } + mMemory = nullptr; +} + +void* SharedMemoryImpl::MemoryImpl() const { return mMemory; } + +bool SharedMemoryImpl::IsHandleValid(const Handle& aHandle) const { + return aHandle != nullptr; +} + +auto SharedMemoryImpl::NULLHandle() -> Handle { return Handle(); } + +} // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemoryImpl_mach.h b/ipc/glue/SharedMemoryImpl_mach.h new file mode 100644 index 000000000000..c45c39a3a298 --- /dev/null +++ b/ipc/glue/SharedMemoryImpl_mach.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_SharedMemoryImpl_mach_h +#define mozilla_ipc_SharedMemoryImpl_mach_h + +#include "mozilla/UniquePtrExtensions.h" +#include + +namespace { +enum Rights { RightsNone = 0, RightsRead = 1 << 0, RightsWrite = 1 << 1 }; +} // namespace + +namespace mozilla::ipc { + +class SharedMemoryImpl { + public: + using Handle = mozilla::UniqueMachSendRight; + + enum OpenRights { + RightsReadOnly = RightsRead, + RightsReadWrite = RightsRead | RightsWrite, + }; + + Handle CloneHandle(); + Handle TakeHandle(); + + bool IsHandleValid(const Handle& aHandle) const; + bool SetHandle(Handle aHandle, OpenRights aRights); + + static Handle NULLHandle(); + static void* FindFreeAddressSpace(size_t size); + + protected: + SharedMemoryImpl(); + ~SharedMemoryImpl() {} + + bool CreateImpl(size_t size); + bool MapImpl(size_t nBytes, void* fixedAddress); + void UnmapImpl(size_t mappedSize); + void* MemoryImpl() const; + + private: + mozilla::UniqueMachSendRight mPort; + // Pointer to mapped region, null if unmapped. + void* mMemory; + // Access rights to map an existing region with. + OpenRights mOpenRights; +}; + +} // namespace mozilla::ipc + +#endif diff --git a/ipc/glue/SharedMemory_android.cpp b/ipc/glue/SharedMemory_android.cpp deleted file mode 100644 index 04f24167af59..000000000000 --- a/ipc/glue/SharedMemory_android.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* This source code was derived from Chromium code, and as such is also subject - * to the [Chromium license](ipc/chromium/src/LICENSE). */ - -#include "mozilla/ipc/SharedMemory.h" - -#include -#include -#include -#include -#include - -#include "mozilla/Ashmem.h" - -#ifdef MOZ_VALGRIND -# include -#endif - -#include "base/eintr_wrapper.h" -#include "base/logging.h" -#include "base/string_util.h" -#include "mozilla/Atomics.h" -#include "mozilla/Maybe.h" -#include "mozilla/ProfilerThreadSleep.h" -#include "mozilla/UniquePtrExtensions.h" -#include "prenv.h" - -namespace mozilla::ipc { - -void SharedMemory::ResetImpl() {}; - -SharedMemory::Handle SharedMemory::CloneHandle(const Handle& aHandle) { - const int new_fd = dup(aHandle.get()); - if (new_fd < 0) { - CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: " - << strerror(errno); - return nullptr; - } - return mozilla::UniqueFileHandle(new_fd); -} - -void* SharedMemory::FindFreeAddressSpace(size_t size) { - void* memory = mmap(nullptr, size, PROT_NONE, - MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); - if (memory == MAP_FAILED) { - return nullptr; - } - munmap(memory, size); - return memory; -} - -Maybe SharedMemory::MapImpl(size_t nBytes, void* fixedAddress) { - // Don't use MAP_FIXED when a fixed_address was specified, since that can - // replace pages that are alread mapped at that address. - void* mem = - mmap(fixedAddress, nBytes, PROT_READ | (mReadOnly ? 0 : PROT_WRITE), - MAP_SHARED, mHandle.get(), 0); - - if (mem == MAP_FAILED) { - CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno); - return Nothing(); - } - - if (fixedAddress && mem != fixedAddress) { - bool munmap_succeeded = munmap(mem, nBytes) == 0; - DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno; - return Nothing(); - } - - return Some(mem); -} - -void SharedMemory::UnmapImpl(size_t nBytes, void* address) { - munmap(address, nBytes); -} - -// Android has its own shared memory API, ashmem. It doesn't support POSIX -// shm_open, and the memfd support (see posix impl) also doesn't work because -// its SELinux policy prevents the procfs operations we'd use (see bug 1670277 -// for more details). - -bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { - return false; -} - -bool SharedMemory::UsingPosixShm() { return false; } - -bool SharedMemory::CreateImpl(size_t size, bool freezable) { - DCHECK(size > 0); - DCHECK(!mHandle); - - int fd = mozilla::android::ashmem_create(nullptr, size); - if (fd < 0) { - CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); - return false; - } - - mHandle.reset(fd); - return true; -} - -Maybe SharedMemory::ReadOnlyCopyImpl() { - if (mozilla::android::ashmem_setProt(mHandle.get(), PROT_READ) != 0) { - CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: " - << strerror(errno); - return Nothing(); - } - - mozilla::UniqueFileHandle ro_file = std::move(mHandle); - - return Some(std::move(ro_file)); -} - -void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) { - if (!SystemProtectFallible(aAddr, aSize, aRights)) { - MOZ_CRASH("can't mprotect()"); - } -} - -bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize, - int aRights) { - int flags = 0; - if (aRights & RightsRead) flags |= PROT_READ; - if (aRights & RightsWrite) flags |= PROT_WRITE; - if (RightsNone == aRights) flags = PROT_NONE; - - return 0 == mprotect(aAddr, aSize, flags); -} - -size_t SharedMemory::SystemPageSize() { return sysconf(_SC_PAGESIZE); } - -} // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemory_mach.cpp b/ipc/glue/SharedMemory_mach.cpp deleted file mode 100644 index 610b7318cb05..000000000000 --- a/ipc/glue/SharedMemory_mach.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/ipc/SharedMemory.h" - -#include - -#include -#include -#if defined(XP_IOS) -# include -# define mach_vm_address_t vm_address_t -# define mach_vm_map vm_map -# define mach_vm_read vm_read -# define mach_vm_region_recurse vm_region_recurse_64 -# define mach_vm_size_t vm_size_t -#else -# include -#endif -#include -#include // mprotect -#include - -#if defined(XP_MACOSX) && defined(__x86_64__) -# include "prenv.h" -#endif - -#include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/Printf.h" -#include "mozilla/StaticMutex.h" - -#ifdef DEBUG -# define LOG_ERROR(str, args...) \ - PR_BEGIN_MACRO \ - mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \ - NS_WARNING(msg.get()); \ - PR_END_MACRO -#else -# define LOG_ERROR(str, args...) \ - do { /* nothing */ \ - } while (0) -#endif - -namespace mozilla::ipc { - -static inline void* toPointer(mach_vm_address_t address) { - return reinterpret_cast(static_cast(address)); -} - -static inline mach_vm_address_t toVMAddress(void* pointer) { - return static_cast(reinterpret_cast(pointer)); -} - -void SharedMemory::ResetImpl() {}; - -bool SharedMemory::CreateImpl(size_t size, bool freezable) { - memory_object_size_t memoryObjectSize = round_page(size); - - kern_return_t kr = - mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, 0, - MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT, - getter_Transfers(mHandle), MACH_PORT_NULL); - if (kr != KERN_SUCCESS || memoryObjectSize < round_page(size)) { - LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", size, - mach_error_string(kr), kr); - TakeHandle(); - return false; - } - return true; -} - -Maybe MapMemory(size_t size, void* fixedAddress, - const mozilla::UniqueMachSendRight& port, - bool readOnly) { - kern_return_t kr; - mach_vm_address_t address = toVMAddress(fixedAddress); - - vm_prot_t vmProtection = VM_PROT_READ; - if (!readOnly) { - vmProtection |= VM_PROT_WRITE; - } - - kr = - mach_vm_map(mach_task_self(), &address, round_page(size), 0, - fixedAddress ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE, port.get(), - 0, false, vmProtection, vmProtection, VM_INHERIT_NONE); - if (kr != KERN_SUCCESS) { - if (!fixedAddress) { - LOG_ERROR( - "Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n", - size, mach_task_self(), mach_port_t(port.get()), - mach_error_string(kr), kr); - } - return Nothing(); - } - - if (fixedAddress && fixedAddress != 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(), mach_port_t(port.get()), - mach_error_string(kr), kr); - } - return Nothing(); - } - - return Some(toPointer(address)); -} - -Maybe SharedMemory::MapImpl(size_t size, void* fixedAddress) { - return MapMemory(size, fixedAddress, mHandle, mReadOnly); -} - -void* SharedMemory::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); -} - -auto SharedMemory::CloneHandle(const Handle& aHandle) -> Handle { - return mozilla::RetainMachSendRight(aHandle.get()); -} - -void SharedMemory::UnmapImpl(size_t nBytes, void* address) { - vm_address_t vm_address = toVMAddress(address); - kern_return_t kr = - vm_deallocate(mach_task_self(), vm_address, round_page(nBytes)); - if (kr != KERN_SUCCESS) { - LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", - mach_error_string(kr), kr); - } -} - -Maybe SharedMemory::ReadOnlyCopyImpl() { - memory_object_size_t memoryObjectSize = round_page(mAllocSize); - - mozilla::UniqueMachSendRight port; - - void* address = mMemory.get(); - bool unmap = false; - - if (!address) { - // Temporarily map memory (as readonly) to get an address. - if (auto memory = MapMemory(memoryObjectSize, nullptr, mHandle, true)) { - address = *memory; - unmap = true; - } else { - return Nothing(); - } - } - - kern_return_t kr = mach_make_memory_entry_64( - mach_task_self(), &memoryObjectSize, - static_cast(reinterpret_cast(address)), - VM_PROT_READ, getter_Transfers(port), MACH_PORT_NULL); - - if (unmap) { - kern_return_t kr = - vm_deallocate(mach_task_self(), toVMAddress(address), memoryObjectSize); - if (kr != KERN_SUCCESS) { - LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", - mach_error_string(kr), kr); - } - } - - if (kr != KERN_SUCCESS || memoryObjectSize < round_page(mAllocSize)) { - LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", mAllocSize, - mach_error_string(kr), kr); - return Nothing(); - } - - return Some(std::move(port)); -} - -void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) { - if (!SystemProtectFallible(aAddr, aSize, aRights)) { - MOZ_CRASH("can't mprotect()"); - } -} - -bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize, - int aRights) { - int flags = 0; - if (aRights & RightsRead) flags |= PROT_READ; - if (aRights & RightsWrite) flags |= PROT_WRITE; - if (RightsNone == aRights) flags = PROT_NONE; - - return 0 == mprotect(aAddr, aSize, flags); -} - -#if defined(XP_MACOSX) && defined(__x86_64__) -std::atomic sPageSizeOverride = 0; -#endif - -size_t SharedMemory::SystemPageSize() { -#if defined(XP_MACOSX) && defined(__x86_64__) - if (sPageSizeOverride == 0) { - if (PR_GetEnv("MOZ_SHMEM_PAGESIZE_16K")) { - sPageSizeOverride = 16 * 1024; - } else { - sPageSizeOverride = sysconf(_SC_PAGESIZE); - } - } - return sPageSizeOverride; -#else - return sysconf(_SC_PAGESIZE); -#endif -} - -} // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemory_posix.cpp b/ipc/glue/SharedMemory_posix.cpp index f0f93ca85fca..798cdf312df0 100644 --- a/ipc/glue/SharedMemory_posix.cpp +++ b/ipc/glue/SharedMemory_posix.cpp @@ -4,418 +4,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* This source code was derived from Chromium code, and as such is also subject - * to the [Chromium license](ipc/chromium/src/LICENSE). */ +#include // mprotect +#include // sysconf #include "mozilla/ipc/SharedMemory.h" -#include -#include -#include -#include -#include - -#ifdef XP_LINUX -# include "base/linux_memfd_defs.h" +#if defined(XP_MACOSX) && defined(__x86_64__) +# include "prenv.h" #endif -#ifdef MOZ_WIDGET_GTK -# include "mozilla/WidgetUtilsGtk.h" -#endif - -#ifdef __FreeBSD__ -# include -#endif - -#ifdef MOZ_VALGRIND -# include -#endif - -#include "base/eintr_wrapper.h" -#include "base/logging.h" -#include "base/string_util.h" -#include "mozilla/Atomics.h" -#include "mozilla/Maybe.h" -#include "mozilla/ProfilerThreadSleep.h" -#include "mozilla/UniquePtrExtensions.h" -#include "prenv.h" namespace mozilla::ipc { -void SharedMemory::ResetImpl() { - if (mFrozenFile) { - CHROMIUM_LOG(WARNING) << "freezable shared memory was never frozen"; - mFrozenFile = nullptr; - } - mIsMemfd = false; -}; - -SharedMemory::Handle SharedMemory::CloneHandle(const Handle& aHandle) { - const int new_fd = dup(aHandle.get()); - if (new_fd < 0) { - CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: " - << strerror(errno); - return nullptr; - } - return mozilla::UniqueFileHandle(new_fd); -} - -void* SharedMemory::FindFreeAddressSpace(size_t size) { - void* memory = mmap(nullptr, size, PROT_NONE, - MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); - if (memory == MAP_FAILED) { - return nullptr; - } - munmap(memory, size); - return memory; -} - -Maybe SharedMemory::MapImpl(size_t nBytes, void* fixedAddress) { - // Don't use MAP_FIXED when a fixed_address was specified, since that can - // replace pages that are alread mapped at that address. - void* mem = - mmap(fixedAddress, nBytes, PROT_READ | (mReadOnly ? 0 : PROT_WRITE), - MAP_SHARED, mHandle.get(), 0); - - if (mem == MAP_FAILED) { - CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno); - return Nothing(); - } - - if (fixedAddress && mem != fixedAddress) { - bool munmap_succeeded = munmap(mem, nBytes) == 0; - DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno; - return Nothing(); - } - - return Some(mem); -} - -void SharedMemory::UnmapImpl(size_t nBytes, void* address) { - munmap(address, nBytes); -} - -// memfd_create is a nonstandard interface for creating anonymous -// shared memory accessible as a file descriptor but not tied to any -// filesystem. It first appeared in Linux 3.17, and was adopted by -// FreeBSD in version 13. - -#if !defined(HAVE_MEMFD_CREATE) && defined(XP_LINUX) && \ - defined(SYS_memfd_create) - -// Older libc versions (e.g., glibc before 2.27) don't have the -// wrapper, but we can supply our own; see `linux_memfd_defs.h`. - -static int memfd_create(const char* name, unsigned int flags) { - return syscall(SYS_memfd_create, name, flags); -} - -# define HAVE_MEMFD_CREATE 1 +#if defined(XP_MACOSX) && defined(__x86_64__) +std::atomic sPageSizeOverride = 0; #endif -// memfd supports having "seals" applied to the file, to prevent -// various types of changes (which apply to all fds referencing the -// file). Unfortunately, we can't rely on F_SEAL_WRITE to implement -// Freeze(); see the comments in ReadOnlyCopy() below. -// -// Instead, to prevent a child process from regaining write access to -// a read-only copy, the OS must also provide a way to remove write -// permissions at the file descriptor level. This next section -// attempts to accomplish that. - -#ifdef HAVE_MEMFD_CREATE -# ifdef XP_LINUX -# define USE_MEMFD_CREATE 1 - -// To create a read-only duplicate of an fd, we can use procfs; the -// same operation could restore write access, but sandboxing prevents -// child processes from accessing /proc. -// -// (Note: if this ever changes to not use /proc, also reconsider how -// and if HaveMemfd should check whether this works.) - -static int DupReadOnly(int fd) { - std::string path = StringPrintf("/proc/self/fd/%d", fd); - // procfs opens probably won't EINTR, but checking for it can't hurt - return HANDLE_EINTR(open(path.c_str(), O_RDONLY | O_CLOEXEC)); -} - -# elif defined(__FreeBSD__) -# define USE_MEMFD_CREATE 1 - -// FreeBSD's Capsicum framework allows irrevocably restricting the -// operations permitted on a file descriptor. - -static int DupReadOnly(int fd) { - int rofd = dup(fd); - if (rofd < 0) { - return -1; - } - - cap_rights_t rights; - cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); - if (cap_rights_limit(rofd, &rights) < 0) { - int err = errno; - close(rofd); - errno = err; - return -1; - } - - return rofd; -} - -# else // unhandled OS -# warning "OS has memfd_create but no DupReadOnly implementation" -# endif // OS selection -#endif // HAVE_MEMFD_CREATE - -// Runtime detection for memfd support. -static bool HaveMemfd() { -#ifdef USE_MEMFD_CREATE - static const bool kHave = [] { - mozilla::UniqueFileHandle fd( - memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING)); - if (!fd) { - DCHECK_EQ(errno, ENOSYS); - return false; - } - - // Verify that DupReadOnly works; on Linux it's known to fail if: - // - // * SELinux assigns the memfd a type for which this process's - // domain doesn't have "open" permission; this is always the - // case on Android but could occur on desktop as well - // - // * /proc (used by the DupReadOnly implementation) isn't mounted, - // which is a configuration that the Tor Browser project is - // interested in as a way to reduce fingerprinting risk - // - // Sandboxed processes on Linux also can't use it if sandboxing - // has already been started, but that's expected. It should be - // safe for sandboxed child processes to use memfd even if an - // unsandboxed process couldn't freeze them, because freezing - // isn't allowed (or meaningful) for memory created by another - // process. - - if (!PR_GetEnv("MOZ_SANDBOXED")) { - mozilla::UniqueFileHandle rofd(DupReadOnly(fd.get())); - if (!rofd) { - CHROMIUM_LOG(WARNING) << "read-only dup failed (" << strerror(errno) - << "); not using memfd"; - return false; - } - } - return true; - }(); - return kHave; -#else - return false; -#endif // USE_MEMFD_CREATE -} - -// static -bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { - if (HaveMemfd()) { - return false; - } - *str += '/'; -#ifdef MOZ_WIDGET_GTK - // The Snap package environment doesn't provide a private /dev/shm - // (it's used for communication with services like PulseAudio); - // instead AppArmor is used to restrict access to it. Anything with - // this prefix is allowed: - if (const char* snap = mozilla::widget::GetSnapInstanceName()) { - StringAppendF(str, "snap.%s.", snap); - } -#endif // XP_LINUX - // Hopefully the "implementation defined" name length limit is long - // enough for this. - StringAppendF(str, "org.mozilla.ipc.%d.", static_cast(pid)); - return true; -} - -bool SharedMemory::CreateImpl(size_t size, bool freezable) { - DCHECK(size > 0); - DCHECK(!mHandle); - DCHECK(!mFrozenFile); - - mozilla::UniqueFileHandle fd; - mozilla::UniqueFileHandle frozen_fd; - bool is_memfd = false; - -#ifdef USE_MEMFD_CREATE - if (HaveMemfd()) { - const unsigned flags = MFD_CLOEXEC | (freezable ? MFD_ALLOW_SEALING : 0); - fd.reset(memfd_create("mozilla-ipc", flags)); - if (!fd) { - // In general it's too late to fall back here -- in a sandboxed - // child process, shm_open is already blocked. And it shouldn't - // be necessary. - CHROMIUM_LOG(WARNING) << "failed to create memfd: " << strerror(errno); - return false; - } - is_memfd = true; - if (freezable) { - frozen_fd.reset(DupReadOnly(fd.get())); - if (!frozen_fd) { - CHROMIUM_LOG(WARNING) - << "failed to create read-only memfd: " << strerror(errno); - return false; - } - } - } -#endif - - if (!fd) { - // Generic Unix: shm_open + shm_unlink - do { - // The names don't need to be unique, but it saves time if they - // usually are. - static mozilla::Atomic sNameCounter; - std::string name; - CHECK(AppendPosixShmPrefix(&name, getpid())); - StringAppendF(&name, "%zu", sNameCounter++); - // O_EXCL means the names being predictable shouldn't be a problem. - fd.reset(HANDLE_EINTR( - shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600))); - if (fd) { - if (freezable) { - frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400))); - if (!frozen_fd) { - int open_err = errno; - shm_unlink(name.c_str()); - DLOG(FATAL) << "failed to re-open freezable shm: " - << strerror(open_err); - return false; - } - } - if (shm_unlink(name.c_str()) != 0) { - // This shouldn't happen, but if it does: assume the file is - // in fact leaked, and bail out now while it's still 0-length. - DLOG(FATAL) << "failed to unlink shm: " << strerror(errno); - return false; - } - } - } while (!fd && errno == EEXIST); - } - - if (!fd) { - CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); - return false; - } - - mozilla::Maybe fallocateError; -#if defined(HAVE_POSIX_FALLOCATE) - // Using posix_fallocate will ensure that there's actually space for this - // file. Otherwise we end up with a sparse file that can give SIGBUS if we - // run out of space while writing to it. (This doesn't apply to memfd.) - if (!is_memfd) { - int rv; - // Avoid repeated interruptions of posix_fallocate by the profiler's - // SIGPROF sampling signal. Indicating "thread sleep" here means we'll - // get up to one interruption but not more. See bug 1658847 for more. - // This has to be scoped outside the HANDLE_RV_EINTR retry loop. - { - AUTO_PROFILER_THREAD_SLEEP; - - rv = HANDLE_RV_EINTR( - posix_fallocate(fd.get(), 0, static_cast(size))); - } - - // Some filesystems have trouble with posix_fallocate. For now, we must - // fallback ftruncate and accept the allocation failures like we do - // without posix_fallocate. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914 - if (rv != 0 && rv != EOPNOTSUPP && rv != EINVAL && rv != ENODEV) { - CHROMIUM_LOG(WARNING) - << "fallocate failed to set shm size: " << strerror(rv); - return false; - } - fallocateError = mozilla::Some(rv); - } -#endif - - // If posix_fallocate isn't supported / relevant for this type of - // file (either failed with an expected error, or wasn't attempted), - // then set the size with ftruncate: - if (fallocateError != mozilla::Some(0)) { - int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast(size))); - if (rv != 0) { - int ftruncate_errno = errno; - if (fallocateError) { - CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: " - << strerror(*fallocateError); - } - CHROMIUM_LOG(WARNING) - << "ftruncate failed to set shm size: " << strerror(ftruncate_errno); - return false; - } - } - - mHandle = std::move(fd); - mFrozenFile = std::move(frozen_fd); - mIsMemfd = is_memfd; - return true; -} - -Maybe SharedMemory::ReadOnlyCopyImpl() { -#ifdef USE_MEMFD_CREATE -# ifdef MOZ_VALGRIND - // Valgrind allows memfd_create but doesn't understand F_ADD_SEALS. - static const bool haveSeals = RUNNING_ON_VALGRIND == 0; -# else - static const bool haveSeals = true; -# endif - static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS"); - if (mIsMemfd && haveSeals && useSeals) { - // Seals are added to the file as defense-in-depth. The primary - // method of access control is creating a read-only fd (using - // procfs in this case) and requiring that sandboxes processes not - // have access to /proc/self/fd to regain write permission; this - // is the same as with shm_open. - // - // Unfortunately, F_SEAL_WRITE is unreliable: if the process - // forked while there was a writeable mapping, it will inherit a - // copy of the mapping, which causes the seal to fail. - // - // (Also, in the future we may want to split this into separate - // classes for mappings and shared memory handles, which would - // complicate identifying the case where `F_SEAL_WRITE` would be - // possible even in the absence of races with fork.) - // - // However, Linux 5.1 added F_SEAL_FUTURE_WRITE, which prevents - // write operations afterwards, but existing writeable mappings - // are unaffected (similar to ashmem protection semantics). - - const int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; - int sealError = EINVAL; - -# ifdef F_SEAL_FUTURE_WRITE - sealError = - fcntl(mHandle.get(), F_ADD_SEALS, seals | F_SEAL_FUTURE_WRITE) == 0 - ? 0 - : errno; -# endif // F_SEAL_FUTURE_WRITE - if (sealError == EINVAL) { - sealError = fcntl(mHandle.get(), F_ADD_SEALS, seals) == 0 ? 0 : errno; - } - if (sealError != 0) { - CHROMIUM_LOG(WARNING) << "failed to seal memfd: " << strerror(errno); - return Nothing(); - } - } -#else // !USE_MEMFD_CREATE - DCHECK(!mIsMemfd); -#endif - - DCHECK(mFrozenFile); - DCHECK(mHandle); - mozilla::UniqueFileHandle ro_file = std::move(mFrozenFile); - - DCHECK(ro_file); - - return Some(std::move(ro_file)); -} - void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) { if (!SystemProtectFallible(aAddr, aSize, aRights)) { MOZ_CRASH("can't mprotect()"); @@ -432,8 +35,19 @@ bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize, return 0 == mprotect(aAddr, aSize, flags); } -size_t SharedMemory::SystemPageSize() { return sysconf(_SC_PAGESIZE); } - -bool SharedMemory::UsingPosixShm() { return !HaveMemfd(); } +size_t SharedMemory::SystemPageSize() { +#if defined(XP_MACOSX) && defined(__x86_64__) + if (sPageSizeOverride == 0) { + if (PR_GetEnv("MOZ_SHMEM_PAGESIZE_16K")) { + sPageSizeOverride = 16 * 1024; + } else { + sPageSizeOverride = sysconf(_SC_PAGESIZE); + } + } + return sPageSizeOverride; +#else + return sysconf(_SC_PAGESIZE); +#endif +} } // namespace mozilla::ipc diff --git a/ipc/glue/SharedMemory_windows.cpp b/ipc/glue/SharedMemory_windows.cpp index 3684e2b3ac54..7aa812e16dad 100644 --- a/ipc/glue/SharedMemory_windows.cpp +++ b/ipc/glue/SharedMemory_windows.cpp @@ -4,211 +4,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* This source code was derived from Chromium code, and as such is also subject - * to the [Chromium license](ipc/chromium/src/LICENSE). */ - #include "mozilla/ipc/SharedMemory.h" #include -#include "base/logging.h" -#include "base/win_util.h" -#include "base/string_util.h" -#include "mozilla/ipc/ProtocolUtils.h" -#include "mozilla/RandomNum.h" -#include "nsDebug.h" -#include "nsString.h" -#ifdef MOZ_MEMORY -# include "mozmemory_utils.h" -#endif - -namespace { -// NtQuerySection is an internal (but believed to be stable) API and the -// structures it uses are defined in nt_internals.h. -// So we have to define them ourselves. -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, -} SECTION_INFORMATION_CLASS; - -typedef struct _SECTION_BASIC_INFORMATION { - PVOID BaseAddress; - ULONG Attributes; - LARGE_INTEGER Size; -} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; - -typedef ULONG(__stdcall* NtQuerySectionType)( - HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass, - PVOID SectionInformation, ULONG SectionInformationLength, - PULONG ResultLength); - -// Checks if the section object is safe to map. At the moment this just means -// it's not an image section. -bool IsSectionSafeToMap(HANDLE handle) { - static NtQuerySectionType nt_query_section_func = - reinterpret_cast( - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection")); - DCHECK(nt_query_section_func); - - // The handle must have SECTION_QUERY access for this to succeed. - SECTION_BASIC_INFORMATION basic_information = {}; - ULONG status = - nt_query_section_func(handle, SectionBasicInformation, &basic_information, - sizeof(basic_information), nullptr); - if (status) { - return false; - } - - return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; -} - -} // namespace - namespace mozilla::ipc { -void SharedMemory::ResetImpl() {}; - -SharedMemory::Handle SharedMemory::CloneHandle(const Handle& aHandle) { - HANDLE handle = INVALID_HANDLE_VALUE; - if (DuplicateHandle(GetCurrentProcess(), aHandle.get(), GetCurrentProcess(), - &handle, 0, false, DUPLICATE_SAME_ACCESS)) { - return SharedMemoryHandle(handle); - } - NS_WARNING("DuplicateHandle Failed!"); - return nullptr; -} - -void* SharedMemory::FindFreeAddressSpace(size_t size) { - void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); - if (memory) { - VirtualFree(memory, 0, MEM_RELEASE); - } - return memory; -} - -Maybe SharedMemory::MapImpl(size_t nBytes, void* fixedAddress) { - if (mExternalHandle && !IsSectionSafeToMap(mHandle.get())) { - return Nothing(); - } - - void* mem = MapViewOfFileEx( - mHandle.get(), mReadOnly ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, - 0, 0, nBytes, fixedAddress); - if (mem) { - MOZ_ASSERT(!fixedAddress || mem == fixedAddress, - "MapViewOfFileEx returned an expected address"); - return Some(mem); - } - return Nothing(); -} - -void SharedMemory::UnmapImpl(size_t nBytes, void* address) { - UnmapViewOfFile(address); -} - -// Wrapper around CreateFileMappingW for pagefile-backed regions. When out of -// memory, may attempt to stall and retry rather than returning immediately, in -// hopes that the page file is about to be expanded by Windows. (bug 1822383, -// bug 1716727) -// -// This method is largely a copy of the MozVirtualAlloc method from -// mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls, -// except re-purposed to handle CreateFileMapping. -static HANDLE MozCreateFileMappingW( - LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, - DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) { -#ifdef MOZ_MEMORY - constexpr auto IsOOMError = [] { - return ::GetLastError() == ERROR_COMMITMENT_LIMIT; - }; - - { - HANDLE handle = ::CreateFileMappingW( - INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, - dwMaximumSizeHigh, dwMaximumSizeLow, lpName); - if (MOZ_LIKELY(handle)) { - MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, - "::CreateFileMapping should return NULL, not " - "INVALID_HANDLE_VALUE, on failure"); - return handle; - } - - // We can't do anything for errors other than OOM. - if (!IsOOMError()) { - return nullptr; - } - } - - // Retry as many times as desired (possibly zero). - const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs(); - - const auto ret = - stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional { - HANDLE handle = ::CreateFileMappingW( - INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, - dwMaximumSizeHigh, dwMaximumSizeLow, lpName); - - if (handle) { - MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, - "::CreateFileMapping should return NULL, not " - "INVALID_HANDLE_VALUE, on failure"); - return handle; - } - - // Failure for some reason other than OOM. - if (!IsOOMError()) { - return nullptr; - } - - return std::nullopt; - }); - - return ret.value_or(nullptr); -#else - return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes, - flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, - lpName); -#endif -} - -bool SharedMemory::CreateImpl(size_t size, bool freezable) { - // If the shared memory object has no DACL, any process can - // duplicate its handles with any access rights; e.g., re-add write - // access to a read-only handle. To prevent that, we give it an - // empty DACL, so that no process can do that. - SECURITY_ATTRIBUTES sa, *psa = nullptr; - SECURITY_DESCRIPTOR sd; - ACL dacl; - - if (freezable) { - psa = &sa; - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = &sd; - sa.bInheritHandle = FALSE; - - if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) || - NS_WARN_IF( - !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) || - NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) { - return false; - } - } - - mHandle.reset(MozCreateFileMappingW(psa, PAGE_READWRITE, 0, - static_cast(size), nullptr)); - return (bool)mHandle; -} - -Maybe SharedMemory::ReadOnlyCopyImpl() { - HANDLE ro_handle; - if (!::DuplicateHandle(GetCurrentProcess(), mHandle.get(), - GetCurrentProcess(), &ro_handle, - GENERIC_READ | FILE_MAP_READ, false, 0)) { - return Nothing(); - } - - return Some(ro_handle); -} - void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) { if (!SystemProtectFallible(aAddr, aSize, aRights)) { MOZ_CRASH("can't VirtualProtect()"); diff --git a/ipc/glue/Shmem.h b/ipc/glue/Shmem.h index ab95c8824f1e..f9b3e340111d 100644 --- a/ipc/glue/Shmem.h +++ b/ipc/glue/Shmem.h @@ -11,7 +11,6 @@ #include "base/basictypes.h" #include "base/process.h" -#include "chrome/common/ipc_message_utils.h" #include "nscore.h" #include "nsDebug.h" diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index ea4ebe266aac..5e18931b45e9 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -79,7 +79,14 @@ EXPORTS.mozilla.ipc += [ ] if CONFIG["OS_ARCH"] == "WINNT": - SOURCES += ["WindowsMessageLoop.cpp"] + SOURCES += [ + "SharedMemory_windows.cpp", + "WindowsMessageLoop.cpp", + ] +else: + UNIFIED_SOURCES += [ + "SharedMemory_posix.cpp", + ] if CONFIG["OS_ARCH"] == "WINNT": SOURCES += [ @@ -108,14 +115,11 @@ else: ] if CONFIG["OS_ARCH"] == "Darwin": - SOURCES += ["SharedMemory_mach.cpp"] -elif CONFIG["OS_ARCH"] == "WINNT": - SOURCES += ["SharedMemory_windows.cpp"] -elif CONFIG["OS_TARGET"] == "Android": - SOURCES += ["SharedMemory_android.cpp"] + EXPORTS.mozilla.ipc += ["SharedMemoryImpl_mach.h"] + SOURCES += ["SharedMemoryImpl_mach.cpp"] else: - SOURCES += ["SharedMemory_posix.cpp"] - + EXPORTS.mozilla.ipc += ["SharedMemoryImpl_chromium.h"] + SOURCES += ["SharedMemoryImpl_chromium.cpp"] if CONFIG["OS_ARCH"] == "Linux": UNIFIED_SOURCES += [ diff --git a/ipc/gtest/TestSharedMemory.cpp b/ipc/gtest/TestSharedMemory.cpp index e9395c731854..8e8380afc656 100644 --- a/ipc/gtest/TestSharedMemory.cpp +++ b/ipc/gtest/TestSharedMemory.cpp @@ -6,6 +6,8 @@ #include "gtest/gtest.h" +#include "base/shared_memory.h" + #include "mozilla/RefPtr.h" #include "mozilla/ipc/SharedMemory.h" @@ -28,29 +30,28 @@ namespace mozilla { // compromised and then receives a frozen handle. TEST(IPCSharedMemory, FreezeAndMapRW) { - auto shm = MakeRefPtr(); + base::SharedMemory shm; // Create and initialize - ASSERT_TRUE(shm->CreateFreezable(1)); - ASSERT_TRUE(shm->Map(1)); - auto* mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.CreateFreezeable(1)); + ASSERT_TRUE(shm.Map(1)); + auto mem = reinterpret_cast(shm.memory()); ASSERT_TRUE(mem); *mem = 'A'; // Freeze - ASSERT_TRUE(shm->Freeze()); - ASSERT_FALSE(shm->Memory()); + ASSERT_TRUE(shm.Freeze()); + ASSERT_FALSE(shm.memory()); // Re-create as writeable - auto handle = shm->TakeHandleAndUnmap(); - ASSERT_TRUE(shm->IsHandleValid(handle)); - ASSERT_FALSE(shm->IsValid()); - ASSERT_TRUE(shm->SetHandle(std::move(handle), - ipc::SharedMemory::OpenRights::RightsReadWrite)); - ASSERT_TRUE(shm->IsValid()); + auto handle = shm.TakeHandle(); + ASSERT_TRUE(shm.IsHandleValid(handle)); + ASSERT_FALSE(shm.IsValid()); + ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ false)); + ASSERT_TRUE(shm.IsValid()); // This should fail - EXPECT_FALSE(shm->Map(1)); + EXPECT_FALSE(shm.Map(1)); } // Try to restore write permissions to a frozen mapping. Threat @@ -59,22 +60,22 @@ TEST(IPCSharedMemory, FreezeAndMapRW) // proof-of-concept at https://crbug.com/project-zero/1671 ). TEST(IPCSharedMemory, FreezeAndReprotect) { - auto shm = MakeRefPtr(); + base::SharedMemory shm; // Create and initialize - ASSERT_TRUE(shm->CreateFreezable(1)); - ASSERT_TRUE(shm->Map(1)); - auto* mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.CreateFreezeable(1)); + ASSERT_TRUE(shm.Map(1)); + auto mem = reinterpret_cast(shm.memory()); ASSERT_TRUE(mem); *mem = 'A'; // Freeze - ASSERT_TRUE(shm->Freeze()); - ASSERT_FALSE(shm->Memory()); + ASSERT_TRUE(shm.Freeze()); + ASSERT_FALSE(shm.memory()); // Re-map - ASSERT_TRUE(shm->Map(1)); - mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.Map(1)); + mem = reinterpret_cast(shm.memory()); ASSERT_EQ(*mem, 'A'); // Try to alter protection; should fail @@ -82,39 +83,32 @@ TEST(IPCSharedMemory, FreezeAndReprotect) mem, 1, ipc::SharedMemory::RightsReadWrite)); } -#if !defined(XP_WIN) && !defined(XP_DARWIN) +#ifndef XP_WIN // This essentially tests whether FreezeAndReprotect would have failed -// without the freeze. -// -// It doesn't work on Windows: VirtualProtect can't exceed the permissions set -// in MapViewOfFile regardless of the security status of the original handle. -// -// It doesn't work on MacOS: we can set a higher max_protection for the memory -// when creating the handle, but we wouldn't want to do this for freezable -// handles (to prevent creating additional RW mappings that break the memory -// freezing invariants). +// without the freeze. It doesn't work on Windows: VirtualProtect +// can't exceed the permissions set in MapViewOfFile regardless of the +// security status of the original handle. TEST(IPCSharedMemory, Reprotect) { - auto shm = MakeRefPtr(); + base::SharedMemory shm; // Create and initialize - ASSERT_TRUE(shm->CreateFreezable(1)); - ASSERT_TRUE(shm->Map(1)); - auto* mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.CreateFreezeable(1)); + ASSERT_TRUE(shm.Map(1)); + auto mem = reinterpret_cast(shm.memory()); ASSERT_TRUE(mem); *mem = 'A'; // Re-create as read-only - auto handle = shm->TakeHandleAndUnmap(); - ASSERT_TRUE(shm->IsHandleValid(handle)); - ASSERT_FALSE(shm->IsValid()); - ASSERT_TRUE(shm->SetHandle(std::move(handle), - ipc::SharedMemory::OpenRights::RightsReadOnly)); - ASSERT_TRUE(shm->IsValid()); + auto handle = shm.TakeHandle(); + ASSERT_TRUE(shm.IsHandleValid(handle)); + ASSERT_FALSE(shm.IsValid()); + ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ true)); + ASSERT_TRUE(shm.IsValid()); // Re-map - ASSERT_TRUE(shm->Map(1)); - mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.Map(1)); + mem = reinterpret_cast(shm.memory()); ASSERT_EQ(*mem, 'A'); // Try to alter protection; should succeed, because not frozen @@ -129,23 +123,23 @@ TEST(IPCSharedMemory, Reprotect) // See also https://crbug.com/338538 TEST(IPCSharedMemory, WinUnfreeze) { - auto shm = MakeRefPtr(); + base::SharedMemory shm; // Create and initialize - ASSERT_TRUE(shm->CreateFreezable(1)); - ASSERT_TRUE(shm->Map(1)); - auto* mem = reinterpret_cast(shm->Memory()); + ASSERT_TRUE(shm.CreateFreezeable(1)); + ASSERT_TRUE(shm.Map(1)); + auto mem = reinterpret_cast(shm.memory()); ASSERT_TRUE(mem); *mem = 'A'; // Freeze - ASSERT_TRUE(shm->Freeze()); - ASSERT_FALSE(shm->Memory()); + ASSERT_TRUE(shm.Freeze()); + ASSERT_FALSE(shm.memory()); // Extract handle. - auto handle = shm->TakeHandleAndUnmap(); - ASSERT_TRUE(shm->IsHandleValid(handle)); - ASSERT_FALSE(shm->IsValid()); + auto handle = shm.TakeHandle(); + ASSERT_TRUE(shm.IsHandleValid(handle)); + ASSERT_FALSE(shm.IsValid()); // Unfreeze. HANDLE newHandle = INVALID_HANDLE_VALUE; @@ -160,25 +154,24 @@ TEST(IPCSharedMemory, WinUnfreeze) // mapping in the case that the page wasn't accessed before the copy. TEST(IPCSharedMemory, ROCopyAndWrite) { - auto shmRW = MakeRefPtr(); - auto shmRO = MakeRefPtr(); + base::SharedMemory shmRW, shmRO; // Create and initialize - ASSERT_TRUE(shmRW->CreateFreezable(1)); - ASSERT_TRUE(shmRW->Map(1)); - auto* memRW = reinterpret_cast(shmRW->Memory()); + ASSERT_TRUE(shmRW.CreateFreezeable(1)); + ASSERT_TRUE(shmRW.Map(1)); + auto memRW = reinterpret_cast(shmRW.memory()); ASSERT_TRUE(memRW); // Create read-only copy - ASSERT_TRUE(shmRW->ReadOnlyCopy(shmRO)); - EXPECT_FALSE(shmRW->IsValid()); - ASSERT_EQ(shmRW->Memory(), memRW); - ASSERT_EQ(shmRO->MaxSize(), size_t(1)); + ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO)); + EXPECT_FALSE(shmRW.IsValid()); + ASSERT_EQ(shmRW.memory(), memRW); + ASSERT_EQ(shmRO.max_size(), size_t(1)); // Map read-only - ASSERT_TRUE(shmRO->IsValid()); - ASSERT_TRUE(shmRO->Map(1)); - const auto* memRO = reinterpret_cast(shmRO->Memory()); + ASSERT_TRUE(shmRO.IsValid()); + ASSERT_TRUE(shmRO.Map(1)); + auto memRO = reinterpret_cast(shmRO.memory()); ASSERT_TRUE(memRO); ASSERT_NE(memRW, memRO); @@ -192,26 +185,25 @@ TEST(IPCSharedMemory, ROCopyAndWrite) // (and, before that, sees the state as of when the copy was made). TEST(IPCSharedMemory, ROCopyAndRewrite) { - auto shmRW = MakeRefPtr(); - auto shmRO = MakeRefPtr(); + base::SharedMemory shmRW, shmRO; // Create and initialize - ASSERT_TRUE(shmRW->CreateFreezable(1)); - ASSERT_TRUE(shmRW->Map(1)); - auto* memRW = reinterpret_cast(shmRW->Memory()); + ASSERT_TRUE(shmRW.CreateFreezeable(1)); + ASSERT_TRUE(shmRW.Map(1)); + auto memRW = reinterpret_cast(shmRW.memory()); ASSERT_TRUE(memRW); *memRW = 'A'; // Create read-only copy - ASSERT_TRUE(shmRW->ReadOnlyCopy(shmRO)); - EXPECT_FALSE(shmRW->IsValid()); - ASSERT_EQ(shmRW->Memory(), memRW); - ASSERT_EQ(shmRO->MaxSize(), size_t(1)); + ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO)); + EXPECT_FALSE(shmRW.IsValid()); + ASSERT_EQ(shmRW.memory(), memRW); + ASSERT_EQ(shmRO.max_size(), size_t(1)); // Map read-only - ASSERT_TRUE(shmRO->IsValid()); - ASSERT_TRUE(shmRO->Map(1)); - const auto* memRO = reinterpret_cast(shmRO->Memory()); + ASSERT_TRUE(shmRO.IsValid()); + ASSERT_TRUE(shmRO.Map(1)); + auto memRO = reinterpret_cast(shmRO.memory()); ASSERT_TRUE(memRO); ASSERT_NE(memRW, memRO); @@ -225,52 +217,49 @@ TEST(IPCSharedMemory, ROCopyAndRewrite) // See FreezeAndMapRW. TEST(IPCSharedMemory, ROCopyAndMapRW) { - auto shmRW = MakeRefPtr(); - auto shmRO = MakeRefPtr(); + base::SharedMemory shmRW, shmRO; // Create and initialize - ASSERT_TRUE(shmRW->CreateFreezable(1)); - ASSERT_TRUE(shmRW->Map(1)); - auto* memRW = reinterpret_cast(shmRW->Memory()); + ASSERT_TRUE(shmRW.CreateFreezeable(1)); + ASSERT_TRUE(shmRW.Map(1)); + auto memRW = reinterpret_cast(shmRW.memory()); ASSERT_TRUE(memRW); *memRW = 'A'; // Create read-only copy - ASSERT_TRUE(shmRW->ReadOnlyCopy(shmRO)); - ASSERT_TRUE(shmRO->IsValid()); + ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO)); + ASSERT_TRUE(shmRO.IsValid()); // Re-create as writeable - auto handle = shmRO->TakeHandleAndUnmap(); - ASSERT_TRUE(shmRO->IsHandleValid(handle)); - ASSERT_FALSE(shmRO->IsValid()); - ASSERT_TRUE(shmRO->SetHandle(std::move(handle), - ipc::SharedMemory::OpenRights::RightsReadWrite)); - ASSERT_TRUE(shmRO->IsValid()); + auto handle = shmRO.TakeHandle(); + ASSERT_TRUE(shmRO.IsHandleValid(handle)); + ASSERT_FALSE(shmRO.IsValid()); + ASSERT_TRUE(shmRO.SetHandle(std::move(handle), /* read-only */ false)); + ASSERT_TRUE(shmRO.IsValid()); // This should fail - EXPECT_FALSE(shmRO->Map(1)); + EXPECT_FALSE(shmRO.Map(1)); } // See FreezeAndReprotect TEST(IPCSharedMemory, ROCopyAndReprotect) { - auto shmRW = MakeRefPtr(); - auto shmRO = MakeRefPtr(); + base::SharedMemory shmRW, shmRO; // Create and initialize - ASSERT_TRUE(shmRW->CreateFreezable(1)); - ASSERT_TRUE(shmRW->Map(1)); - auto* memRW = reinterpret_cast(shmRW->Memory()); + ASSERT_TRUE(shmRW.CreateFreezeable(1)); + ASSERT_TRUE(shmRW.Map(1)); + auto memRW = reinterpret_cast(shmRW.memory()); ASSERT_TRUE(memRW); *memRW = 'A'; // Create read-only copy - ASSERT_TRUE(shmRW->ReadOnlyCopy(shmRO)); - ASSERT_TRUE(shmRO->IsValid()); + ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO)); + ASSERT_TRUE(shmRO.IsValid()); // Re-map - ASSERT_TRUE(shmRO->Map(1)); - auto* memRO = reinterpret_cast(shmRO->Memory()); + ASSERT_TRUE(shmRO.Map(1)); + auto memRO = reinterpret_cast(shmRO.memory()); ASSERT_EQ(*memRO, 'A'); // Try to alter protection; should fail @@ -278,6 +267,20 @@ TEST(IPCSharedMemory, ROCopyAndReprotect) memRO, 1, ipc::SharedMemory::RightsReadWrite)); } +TEST(IPCSharedMemory, IsZero) +{ + base::SharedMemory shm; + + static constexpr size_t kSize = 65536; + ASSERT_TRUE(shm.Create(kSize)); + ASSERT_TRUE(shm.Map(kSize)); + + auto* mem = reinterpret_cast(shm.memory()); + for (size_t i = 0; i < kSize; ++i) { + ASSERT_EQ(mem[i], 0) << "offset " << i; + } +} + #ifndef FUZZING TEST(IPCSharedMemory, BasicIsZero) { @@ -311,10 +314,9 @@ TEST(IPCSharedMemory, IsMemfd) ASSERT_EQ(sscanf(uts.release, "%d.%d", &major, &minor), 2); bool expectMemfd = major > kMajor || (major == kMajor && minor >= kMinor); - auto shm = MakeRefPtr(); - - ASSERT_TRUE(shm->Create(1)); - UniqueFileHandle fd = shm->TakeHandleAndUnmap(); + base::SharedMemory shm; + ASSERT_TRUE(shm.Create(1)); + UniqueFileHandle fd = shm.TakeHandle(); ASSERT_TRUE(fd); struct statfs fs; diff --git a/js/xpconnect/loader/AutoMemMap.cpp b/js/xpconnect/loader/AutoMemMap.cpp index f7b348242d59..7ce26a9a3373 100644 --- a/js/xpconnect/loader/AutoMemMap.cpp +++ b/js/xpconnect/loader/AutoMemMap.cpp @@ -92,8 +92,50 @@ Result AutoMemMap::initInternal(PRFileMapProtect prot, return Ok(); } +#ifdef XP_WIN + +Result AutoMemMap::initWithHandle(const FileDescriptor& file, + size_t size, + PRFileMapProtect prot) { + MOZ_ASSERT(!fd); + MOZ_ASSERT(!handle_); + if (!file.IsValid()) { + return Err(NS_ERROR_INVALID_ARG); + } + + handle_ = file.ClonePlatformHandle().release(); + + MOZ_ASSERT(!addr); + + size_ = size; + + addr = MapViewOfFile( + handle_, prot == PR_PROT_READONLY ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, + 0, 0, size); + if (!addr) { + return Err(NS_ERROR_FAILURE); + } + + return Ok(); +} + +FileDescriptor AutoMemMap::cloneHandle() const { + return FileDescriptor(handle_); +} + +#else + +Result AutoMemMap::initWithHandle(const FileDescriptor& file, + size_t size, + PRFileMapProtect prot) { + MOZ_DIAGNOSTIC_ASSERT(size > 0); + return init(file, prot, size); +} + FileDescriptor AutoMemMap::cloneHandle() const { return cloneFileDescriptor(); } +#endif + void AutoMemMap::reset() { if (addr && !persistent_) { Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS); @@ -103,6 +145,12 @@ void AutoMemMap::reset() { Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS); fileMap = nullptr; } +#ifdef XP_WIN + if (handle_) { + CloseHandle(handle_); + handle_ = nullptr; + } +#endif fd = nullptr; } diff --git a/js/xpconnect/loader/AutoMemMap.h b/js/xpconnect/loader/AutoMemMap.h index d0b449531e58..54180d09e3eb 100644 --- a/js/xpconnect/loader/AutoMemMap.h +++ b/js/xpconnect/loader/AutoMemMap.h @@ -37,6 +37,13 @@ class AutoMemMap { PRFileMapProtect prot = PR_PROT_READONLY, size_t maybeSize = 0); + // Initializes the mapped memory with a shared memory handle. On + // Unix-like systems, this is identical to the above init() method. On + // Windows, the FileDescriptor must be a handle for a file mapping, + // rather than a file descriptor. + Result initWithHandle(const FileDescriptor& file, size_t size, + PRFileMapProtect prot = PR_PROT_READONLY); + void reset(); bool initialized() const { return addr; } @@ -71,6 +78,13 @@ class AutoMemMap { AutoFDClose fd; PRFileMap* fileMap = nullptr; +#ifdef XP_WIN + // We can't include windows.h in this header, since it gets included + // by some binding headers (which are explicitly incompatible with + // windows.h). So we can't use the HANDLE type here. + void* handle_ = nullptr; +#endif + uint32_t size_ = 0; void* addr = nullptr; diff --git a/js/xpconnect/src/XPCSelfHostedShmem.cpp b/js/xpconnect/src/XPCSelfHostedShmem.cpp index abd7ca5b0ef7..e5c8578ccdec 100644 --- a/js/xpconnect/src/XPCSelfHostedShmem.cpp +++ b/js/xpconnect/src/XPCSelfHostedShmem.cpp @@ -43,8 +43,8 @@ void xpc::SelfHostedShmem::InitFromParent(ContentType aXdr) { MOZ_ASSERT(!mLen, "Shouldn't call this more than once"); size_t len = aXdr.Length(); - auto shm = mozilla::MakeRefPtr(); - if (NS_WARN_IF(!shm->CreateFreezable(len))) { + auto shm = mozilla::MakeUnique(); + if (NS_WARN_IF(!shm->CreateFreezeable(len))) { return; } @@ -52,29 +52,27 @@ void xpc::SelfHostedShmem::InitFromParent(ContentType aXdr) { return; } - void* address = shm->Memory(); + void* address = shm->memory(); memcpy(address, aXdr.Elements(), aXdr.LengthBytes()); - RefPtr roCopy = - mozilla::MakeRefPtr(); - if (NS_WARN_IF(!shm->ReadOnlyCopy(&*roCopy))) { + base::SharedMemory roCopy; + if (NS_WARN_IF(!shm->ReadOnlyCopy(&roCopy))) { return; } mMem = std::move(shm); - mHandle = roCopy->TakeHandleAndUnmap(); + mHandle = roCopy.TakeHandle(); mLen = len; } -bool xpc::SelfHostedShmem::InitFromChild( - mozilla::ipc::SharedMemoryHandle aHandle, size_t aLen) { +bool xpc::SelfHostedShmem::InitFromChild(::base::SharedMemoryHandle aHandle, + size_t aLen) { MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mLen, "Shouldn't call this more than once"); - auto shm = mozilla::MakeRefPtr(); - if (NS_WARN_IF(!shm->SetHandle(std::move(aHandle), - mozilla::ipc::SharedMemory::RightsReadOnly))) { + auto shm = mozilla::MakeUnique(); + if (NS_WARN_IF(!shm->SetHandle(std::move(aHandle), /* read_only */ true))) { return false; } @@ -94,10 +92,10 @@ xpc::SelfHostedShmem::ContentType xpc::SelfHostedShmem::Content() const { MOZ_ASSERT(mLen == 0); return ContentType(); } - return ContentType(reinterpret_cast(mMem->Memory()), mLen); + return ContentType(reinterpret_cast(mMem->memory()), mLen); } -const mozilla::ipc::SharedMemoryHandle& xpc::SelfHostedShmem::Handle() const { +const mozilla::UniqueFileHandle& xpc::SelfHostedShmem::Handle() const { return mHandle; } diff --git a/js/xpconnect/src/XPCSelfHostedShmem.h b/js/xpconnect/src/XPCSelfHostedShmem.h index 01359f2e6184..0fcb1ed05c01 100644 --- a/js/xpconnect/src/XPCSelfHostedShmem.h +++ b/js/xpconnect/src/XPCSelfHostedShmem.h @@ -7,12 +7,11 @@ #ifndef xpcselfhostedshmem_h___ #define xpcselfhostedshmem_h___ +#include "base/shared_memory.h" #include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" -#include "mozilla/RefPtr.h" #include "mozilla/Span.h" #include "mozilla/StaticPtr.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsIMemoryReporter.h" #include "nsIObserver.h" #include "nsIThread.h" @@ -48,14 +47,14 @@ class SelfHostedShmem final : public nsIMemoryReporter { // // This function is not thread-safe and should be call at most once and from // the main thread. - [[nodiscard]] bool InitFromChild(mozilla::ipc::SharedMemoryHandle aHandle, + [[nodiscard]] bool InitFromChild(base::SharedMemoryHandle aHandle, size_t aLen); // Return a span over the read-only XDR content of the self-hosted stencil. ContentType Content() const; // Return the file handle which is under which the content is mapped. - const mozilla::ipc::SharedMemoryHandle& Handle() const; + const mozilla::UniqueFileHandle& Handle() const; // Register this class to the memory reporter service. void InitMemoryReporter(); @@ -76,10 +75,10 @@ class SelfHostedShmem final : public nsIMemoryReporter { // read-only file Handle used to transfer from the parent process to content // processes. - mozilla::ipc::SharedMemoryHandle mHandle; + mozilla::UniqueFileHandle mHandle; // Shared memory used by JS runtime initialization. - RefPtr mMem; + mozilla::UniquePtr mMem; // Length of the content within the shared memory. size_t mLen = 0; diff --git a/layout/style/GlobalStyleSheetCache.cpp b/layout/style/GlobalStyleSheetCache.cpp index 4495675d5dd7..6a8507af7b7b 100644 --- a/layout/style/GlobalStyleSheetCache.cpp +++ b/layout/style/GlobalStyleSheetCache.cpp @@ -9,7 +9,6 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsExceptionHandler.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/NeverDestroyed.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/StyleSheet.h" @@ -105,11 +104,6 @@ namespace mozilla { using namespace mozilla; using namespace css; -mozilla::ipc::SharedMemoryHandle& sSharedMemoryHandle() { - static NeverDestroyed handle; - return *handle; -} - #define PREF_LEGACY_STYLESHEET_CUSTOMIZATION \ "toolkit.legacyUserProfileCustomizations.stylesheets" @@ -179,7 +173,7 @@ GlobalStyleSheetCache::CollectReports(nsIHandleReportCallback* aHandleReport, if (XRE_IsParentProcess()) { MOZ_COLLECT_REPORT( "explicit/layout/style-sheet-cache/shared", KIND_NONHEAP, UNITS_BYTES, - sSharedMemory.IsEmpty() ? 0 : sUsedSharedMemory, + sSharedMemory ? sUsedSharedMemory : 0, "Memory used for built-in style sheets that are shared to " "child processes."); } @@ -242,10 +236,10 @@ GlobalStyleSheetCache::GlobalStyleSheetCache() { if (XRE_IsParentProcess()) { // Load the style sheets and store them in a new shared memory buffer. InitSharedSheetsInParent(); - } else if (!sSharedMemory.IsEmpty()) { - // Use the shared memory that was given to us by a SetSharedMemory call - // under ContentChild::InitXPCOM. - MOZ_ASSERT(sSharedMemory.data(), + } else if (sSharedMemory) { + // Use the shared memory handle that was given to us by a SetSharedMemory + // call under ContentChild::InitXPCOM. + MOZ_ASSERT(sSharedMemory->memory(), "GlobalStyleSheetCache::SetSharedMemory should have mapped " "the shared memory"); } @@ -264,8 +258,8 @@ GlobalStyleSheetCache::GlobalStyleSheetCache() { // In the parent process, this means we'll just leave our eagerly loaded // non-shared sheets in the mFooSheet fields. In a content process, we'll // lazily load our own copies of the sheets later. - if (!sSharedMemory.IsEmpty()) { - if (auto* header = reinterpret_cast(sSharedMemory.data())) { + if (sSharedMemory) { + if (auto* header = static_cast(sSharedMemory->memory())) { MOZ_RELEASE_ASSERT(header->mMagic == Header::kMagic); #define STYLE_SHEET(identifier_, url_, shared_) \ @@ -305,10 +299,10 @@ void GlobalStyleSheetCache::LoadSheetFromSharedMemory( void GlobalStyleSheetCache::InitSharedSheetsInParent() { MOZ_ASSERT(XRE_IsParentProcess()); - MOZ_RELEASE_ASSERT(sSharedMemory.IsEmpty()); + MOZ_RELEASE_ASSERT(!sSharedMemory); - auto shm = MakeRefPtr(); - if (NS_WARN_IF(!shm->CreateFreezable(kSharedMemorySize))) { + auto shm = MakeUnique(); + if (NS_WARN_IF(!shm->CreateFreezeable(kSharedMemorySize))) { return; } @@ -340,7 +334,7 @@ void GlobalStyleSheetCache::InitSharedSheetsInParent() { #endif void* address = nullptr; - if (void* p = ipc::SharedMemory::FindFreeAddressSpace(2 * kOffset)) { + if (void* p = base::SharedMemory::FindFreeAddressSpace(2 * kOffset)) { address = reinterpret_cast(uintptr_t(p) + kOffset); } @@ -352,7 +346,7 @@ void GlobalStyleSheetCache::InitSharedSheetsInParent() { return; } } - address = shm->Memory(); + address = shm->memory(); auto* header = static_cast(address); header->mMagic = Header::kMagic; @@ -411,8 +405,7 @@ void GlobalStyleSheetCache::InitSharedSheetsInParent() { (Servo_SharedMemoryBuilder_GetLength(builder.get()) + pageSize - 1) & ~(pageSize - 1); - sSharedMemory = shm->TakeMapping(); - sSharedMemoryHandle() = shm->TakeHandle(); + sSharedMemory = shm.release(); } GlobalStyleSheetCache::~GlobalStyleSheetCache() { @@ -535,26 +528,25 @@ RefPtr GlobalStyleSheetCache::LoadSheet( } /* static */ void GlobalStyleSheetCache::SetSharedMemory( - ipc::SharedMemory::Handle aHandle, uintptr_t aAddress) { + base::SharedMemoryHandle aHandle, uintptr_t aAddress) { MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(!gStyleCache, "Too late, GlobalStyleSheetCache already created!"); - MOZ_ASSERT(sSharedMemory.IsEmpty(), "Shouldn't call this more than once"); + MOZ_ASSERT(!sSharedMemory, "Shouldn't call this more than once"); - auto shm = MakeRefPtr(); - if (!shm->SetHandle(std::move(aHandle), ipc::SharedMemory::RightsReadOnly)) { + auto shm = MakeUnique(); + if (!shm->SetHandle(std::move(aHandle), /* read_only */ true)) { return; } if (shm->Map(kSharedMemorySize, reinterpret_cast(aAddress))) { - sSharedMemory = shm->TakeMapping(); - sSharedMemoryHandle() = shm->TakeHandle(); + sSharedMemory = shm.release(); } } -ipc::SharedMemoryHandle GlobalStyleSheetCache::CloneHandle() { +base::SharedMemoryHandle GlobalStyleSheetCache::CloneHandle() { MOZ_ASSERT(XRE_IsParentProcess()); - if (ipc::SharedMemory::IsHandleValid(sSharedMemoryHandle())) { - return ipc::SharedMemory::CloneHandle(sSharedMemoryHandle()); + if (sSharedMemory) { + return sSharedMemory->CloneHandle(); } return nullptr; } @@ -563,7 +555,7 @@ StaticRefPtr GlobalStyleSheetCache::gStyleCache; StaticRefPtr GlobalStyleSheetCache::gCSSLoader; StaticRefPtr GlobalStyleSheetCache::gUserContentSheetURL; -Span GlobalStyleSheetCache::sSharedMemory; +StaticAutoPtr GlobalStyleSheetCache::sSharedMemory; size_t GlobalStyleSheetCache::sUsedSharedMemory; } // namespace mozilla diff --git a/layout/style/GlobalStyleSheetCache.h b/layout/style/GlobalStyleSheetCache.h index e4a75a8e8867..688729fede6b 100644 --- a/layout/style/GlobalStyleSheetCache.h +++ b/layout/style/GlobalStyleSheetCache.h @@ -9,6 +9,7 @@ #include "nsIMemoryReporter.h" #include "nsIObserver.h" +#include "base/shared_memory.h" #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PreferenceSheet.h" @@ -16,7 +17,6 @@ #include "mozilla/StaticPtr.h" #include "mozilla/UserAgentStyleSheetID.h" #include "mozilla/css/Loader.h" -#include "mozilla/ipc/SharedMemory.h" class nsIFile; class nsIURI; @@ -60,18 +60,18 @@ class GlobalStyleSheetCache final : public nsIObserver, // Called early on in a content process' life from // ContentChild::InitSharedUASheets, before the GlobalStyleSheetCache // singleton has been created. - static void SetSharedMemory(mozilla::ipc::SharedMemory::Handle aHandle, + static void SetSharedMemory(base::SharedMemoryHandle aHandle, uintptr_t aAddress); // Obtain a shared memory handle for the shared UA sheets to pass into a // content process. Called by ContentParent::InitInternal shortly after // a content process has been created. - mozilla::ipc::SharedMemoryHandle CloneHandle(); + base::SharedMemoryHandle CloneHandle(); // Returns the address of the shared memory segment that holds the shared UA // sheets. uintptr_t GetSharedMemoryAddress() { - return sSharedMemory.IsEmpty() ? 0 : uintptr_t(sSharedMemory.data()); + return sSharedMemory ? uintptr_t(sSharedMemory->memory()) : 0; } // Size of the shared memory buffer we'll create to store the shared UA @@ -120,7 +120,7 @@ class GlobalStyleSheetCache final : public nsIObserver, RefPtr mUserContentSheet; // Shared memory segment storing shared style sheets. - static Span sSharedMemory; + static StaticAutoPtr sSharedMemory; // How much of the shared memory buffer we ended up using. Used for memory // reporting in the parent process. diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 0f7b8e2f1fcf..b31af36d6746 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -3833,7 +3833,7 @@ void Preferences::DeserializePreferences(char* aStr, size_t aPrefsLen) { } /* static */ -mozilla::ipc::SharedMemoryHandle Preferences::EnsureSnapshot(size_t* aSize) { +FileDescriptor Preferences::EnsureSnapshot(size_t* aSize) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); @@ -3883,12 +3883,11 @@ mozilla::ipc::SharedMemoryHandle Preferences::EnsureSnapshot(size_t* aSize) { } *aSize = gSharedMap->MapSize(); - return gSharedMap->CloneHandle(); + return gSharedMap->CloneFileDescriptor(); } /* static */ -void Preferences::InitSnapshot(const mozilla::ipc::SharedMemoryHandle& aHandle, - size_t aSize) { +void Preferences::InitSnapshot(const FileDescriptor& aHandle, size_t aSize) { MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(!gSharedMap); diff --git a/modules/libpref/Preferences.h b/modules/libpref/Preferences.h index d789aecc9b5b..271871d6397a 100644 --- a/modules/libpref/Preferences.h +++ b/modules/libpref/Preferences.h @@ -17,7 +17,6 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/MozPromise.h" #include "mozilla/StaticPtr.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsIPrefBranch.h" @@ -410,11 +409,8 @@ class Preferences final : public nsIPrefService, bool aIsDestinationWebContentProcess); static void DeserializePreferences(char* aStr, size_t aPrefsLen); -#ifndef RUST_BINDGEN - static mozilla::ipc::SharedMemoryHandle EnsureSnapshot(size_t* aSize); - static void InitSnapshot(const mozilla::ipc::SharedMemoryHandle&, - size_t aSize); -#endif + static mozilla::ipc::FileDescriptor EnsureSnapshot(size_t* aSize); + static void InitSnapshot(const mozilla::ipc::FileDescriptor&, size_t aSize); // When a single pref is changed in the parent process, these methods are // used to pass the update to content processes. diff --git a/modules/libpref/SharedPrefMap.cpp b/modules/libpref/SharedPrefMap.cpp index 8119018efb93..7782ffdcbf56 100644 --- a/modules/libpref/SharedPrefMap.cpp +++ b/modules/libpref/SharedPrefMap.cpp @@ -13,6 +13,8 @@ #include "mozilla/Try.h" #include "mozilla/ipc/FileDescriptor.h" +using namespace mozilla::loader; + namespace mozilla { using namespace ipc; @@ -22,38 +24,24 @@ static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) { return mod ? aAlign - mod : 0; } -SharedPrefMap::SharedPrefMap(const SharedMemoryHandle& aMapHandle, - size_t aMapSize) { - auto map = MakeRefPtr(); - { - auto result = map->SetHandle(SharedMemory::CloneHandle(aMapHandle), - SharedMemory::OpenRights::RightsReadOnly); - MOZ_RELEASE_ASSERT(result); - } - { - auto result = map->Map(aMapSize); - MOZ_RELEASE_ASSERT(result); - } - +SharedPrefMap::SharedPrefMap(const FileDescriptor& aMapFile, size_t aMapSize) { + auto result = mMap.initWithHandle(aMapFile, aMapSize); + MOZ_RELEASE_ASSERT(result.isOk()); // We return literal nsCStrings pointing to the mapped data for preference // names and string values, which means that we may still have references to // the mapped data even after this instance is destroyed. That means that we // need to keep the mapping alive until process shutdown, in order to be safe. - mMappedMemory = map->TakeMapping(); - mHandle = map->TakeHandle(); + mMap.setPersistent(); } SharedPrefMap::SharedPrefMap(SharedPrefMapBuilder&& aBuilder) { - RefPtr map; - auto result = aBuilder.Finalize(map); - MOZ_RELEASE_ASSERT(result.isOk() && map); - - mMappedMemory = map->TakeMapping(); - mHandle = map->TakeHandle(); + auto result = aBuilder.Finalize(mMap); + MOZ_RELEASE_ASSERT(result.isOk()); + mMap.setPersistent(); } -mozilla::ipc::SharedMemoryHandle SharedPrefMap::CloneHandle() const { - return SharedMemory::CloneHandle(mHandle); +mozilla::ipc::FileDescriptor SharedPrefMap::CloneFileDescriptor() const { + return mMap.cloneHandle(); } bool SharedPrefMap::Has(const char* aKey) const { @@ -148,8 +136,7 @@ void SharedPrefMapBuilder::Add(const nsCString& aKey, const Flags& aFlags, }); } -Result SharedPrefMapBuilder::Finalize( - RefPtr& aMap) { +Result SharedPrefMapBuilder::Finalize(loader::AutoMemMap& aMap) { using Header = SharedPrefMap::Header; // Create an array of entry pointers for the entry array, and sort it by diff --git a/modules/libpref/SharedPrefMap.h b/modules/libpref/SharedPrefMap.h index ad99d331df23..7c949e137d80 100644 --- a/modules/libpref/SharedPrefMap.h +++ b/modules/libpref/SharedPrefMap.h @@ -7,11 +7,11 @@ #ifndef dom_ipc_SharedPrefMap_h #define dom_ipc_SharedPrefMap_h +#include "mozilla/AutoMemMap.h" #include "mozilla/HashFunctions.h" #include "mozilla/Preferences.h" #include "mozilla/Result.h" #include "mozilla/dom/ipc/StringTable.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsTHashMap.h" namespace mozilla { @@ -52,6 +52,8 @@ class SharedPrefMapBuilder; // instance has been initialized, the memory that it allocates can never be // freed before process shutdown. Do not use it for short-lived mappings. class SharedPrefMap { + using FileDescriptor = mozilla::ipc::FileDescriptor; + friend class SharedPrefMapBuilder; // Describes a block of memory within the shared memory region. @@ -419,7 +421,7 @@ class SharedPrefMap { // Note: These constructors are infallible, because the preference database is // critical to platform functionality, and we cannot operate without it. - SharedPrefMap(const mozilla::ipc::SharedMemoryHandle&, size_t); + SharedPrefMap(const FileDescriptor&, size_t); explicit SharedPrefMap(SharedPrefMapBuilder&&); // Searches for the given preference in the map, and returns true if it @@ -498,15 +500,15 @@ class SharedPrefMap { // makes its purpose slightly clearer. const SharedPrefMap& Iter() const { return *this; } - // Returns a copy of the read-only shared memory handle which backs the shared - // memory region for this map. The handle may be passed between processes, and - // used to construct new instances of SharedPrefMap with the same data as this - // instance. - mozilla::ipc::SharedMemoryHandle CloneHandle() const; + // Returns a copy of the read-only file descriptor which backs the shared + // memory region for this map. The file descriptor may be passed between + // processes, and used to construct new instances of SharedPrefMap with + // the same data as this instance. + FileDescriptor CloneFileDescriptor() const; // Returns the size of the mapped memory region. This size must be passed to // the constructor when mapping the shared region in another process. - size_t MapSize() const { return mMappedMemory.size(); } + size_t MapSize() const { return mMap.size(); } protected: ~SharedPrefMap() = default; @@ -516,9 +518,7 @@ class SharedPrefMap { using StringTable = mozilla::dom::ipc::StringTable; // Type-safe getters for values in the shared memory region: - const Header& GetHeader() const { - return *reinterpret_cast(mMappedMemory.data()); - } + const Header& GetHeader() const { return mMap.get
()[0]; } RangedPtr Entries() const { return {reinterpret_cast(&GetHeader() + 1), EntryCount()}; @@ -528,7 +528,7 @@ class SharedPrefMap { template RangedPtr GetBlock(const DataBlock& aBlock) const { - return RangedPtr(&mMappedMemory.data()[aBlock.mOffset], + return RangedPtr(&mMap.get()[aBlock.mOffset], aBlock.mSize) .ReinterpretCast(); } @@ -549,19 +549,15 @@ class SharedPrefMap { StringTable KeyTable() const { auto& block = GetHeader().mKeyStrings; - return {{&mMappedMemory.data()[block.mOffset], block.mSize}}; + return {{&mMap.get()[block.mOffset], block.mSize}}; } StringTable ValueTable() const { auto& block = GetHeader().mValueStrings; - return {{&mMappedMemory.data()[block.mOffset], block.mSize}}; + return {{&mMap.get()[block.mOffset], block.mSize}}; } - mozilla::ipc::SharedMemoryHandle mHandle; - // This is a leaked shared memory mapping (see the constructor definition for - // an explanation). It replaces AutoMemMap::setPersistent behavior as part of - // bug 1454816. - Span mMappedMemory; + loader::AutoMemMap mMap; }; // A helper class which builds the contiguous look-up table used by @@ -591,13 +587,13 @@ class MOZ_RAII SharedPrefMapBuilder { const nsCString& aDefaultValue, const nsCString& aUserValue); // Finalizes the binary representation of the map, writes it to a shared - // memory region, and then initializes the given SharedMemory with a reference + // memory region, and then initializes the given AutoMemMap with a reference // to the read-only copy of it. // // This should generally not be used directly by callers. The // SharedPrefMapBuilder instance should instead be passed to the SharedPrefMap // constructor as a move reference. - Result Finalize(RefPtr& aMap); + Result Finalize(loader::AutoMemMap& aMap); private: using StringTableEntry = mozilla::dom::ipc::StringTableEntry; diff --git a/netwerk/ipc/SocketProcessImpl.cpp b/netwerk/ipc/SocketProcessImpl.cpp index 95772ab2a06f..a863d4a8759b 100644 --- a/netwerk/ipc/SocketProcessImpl.cpp +++ b/netwerk/ipc/SocketProcessImpl.cpp @@ -6,6 +6,7 @@ #include "SocketProcessImpl.h" #include "base/command_line.h" +#include "base/shared_memory.h" #include "base/string_util.h" #include "mozilla/BackgroundHangMonitor.h" #include "mozilla/Preferences.h" diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp index dc6b9261b8eb..e93601435850 100644 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp +++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp @@ -8,6 +8,7 @@ #include "SandboxInfo.h" #include "SandboxLogging.h" +#include "base/shared_memory.h" #include "mozilla/Array.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Omnijar.h" @@ -18,7 +19,6 @@ #include "mozilla/StaticMutex.h" #include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" -#include "mozilla/ipc/SharedMemory.h" #include "nsComponentManagerUtils.h" #include "nsPrintfCString.h" #include "nsString.h" @@ -323,7 +323,7 @@ static void AddLdLibraryEnvPaths(SandboxBroker::Policy* aPolicy) { static void AddSharedMemoryPaths(SandboxBroker::Policy* aPolicy, pid_t aPid) { std::string shmPath("/dev/shm"); - if (ipc::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) { + if (base::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) { aPolicy->AddPrefix(rdwrcr, shmPath.c_str()); } } diff --git a/toolkit/xre/GeckoArgs.h b/toolkit/xre/GeckoArgs.h index 5bb83ad51cfc..d4f27b0c73a0 100644 --- a/toolkit/xre/GeckoArgs.h +++ b/toolkit/xre/GeckoArgs.h @@ -8,7 +8,6 @@ #include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/Maybe.h" #include "mozilla/UniquePtrExtensions.h" -#include "mozilla/ipc/SharedMemory.h" #include #include @@ -188,14 +187,14 @@ static CommandLineArg sProfile{"-profile", "profile"}; static CommandLineArg sIPCHandle{"-ipcHandle", "ipchandle"}; -static CommandLineArg sJsInitHandle{ - "-jsInitHandle", "jsinithandle"}; +static CommandLineArg sJsInitHandle{"-jsInitHandle", + "jsinithandle"}; static CommandLineArg sJsInitLen{"-jsInitLen", "jsinitlen"}; -static CommandLineArg sPrefsHandle{ - "-prefsHandle", "prefshandle"}; +static CommandLineArg sPrefsHandle{"-prefsHandle", + "prefshandle"}; static CommandLineArg sPrefsLen{"-prefsLen", "prefslen"}; -static CommandLineArg sPrefMapHandle{ - "-prefMapHandle", "prefmaphandle"}; +static CommandLineArg sPrefMapHandle{"-prefMapHandle", + "prefmaphandle"}; static CommandLineArg sPrefMapSize{"-prefMapSize", "prefmapsize"}; static CommandLineArg sSandboxingKind{"-sandboxingKind", diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp index f9e9f7c39a24..f3fc4093623f 100644 --- a/widget/gtk/WaylandBuffer.cpp +++ b/widget/gtk/WaylandBuffer.cpp @@ -52,7 +52,7 @@ RefPtr WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay, RefPtr shmPool = new WaylandShmPool(); - shmPool->mShm = MakeRefPtr(); + shmPool->mShm = MakeUnique(); if (!shmPool->mShm->Create(aSize)) { NS_WARNING("WaylandShmPool: Unable to allocate shared memory!"); return nullptr; @@ -77,7 +77,7 @@ void* WaylandShmPool::GetImageData() { NS_WARNING("WaylandShmPool: Failed to map Shm!"); return nullptr; } - mImageData = mShm->Memory(); + mImageData = mShm->memory(); return mImageData; } diff --git a/widget/gtk/WaylandBuffer.h b/widget/gtk/WaylandBuffer.h index 47f15b60e52c..1de136258b91 100644 --- a/widget/gtk/WaylandBuffer.h +++ b/widget/gtk/WaylandBuffer.h @@ -10,13 +10,12 @@ #include "DMABufSurface.h" #include "GLContext.h" #include "MozFramebuffer.h" -#include "mozilla/ipc/SharedMemory.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Types.h" #include "mozilla/Mutex.h" -#include "mozilla/RefPtr.h" #include "nsTArray.h" #include "nsWaylandDisplay.h" +#include "base/shared_memory.h" namespace mozilla::widget { @@ -37,7 +36,7 @@ class WaylandShmPool { wl_shm_pool* mShmPool = nullptr; void* mImageData = nullptr; - RefPtr mShm; + UniquePtr mShm; int mSize = 0; };