From adde9e855646e4be4d04f3f6f56eb3afe0824e3b Mon Sep 17 00:00:00 2001 From: Chris Martin Date: Wed, 21 Nov 2018 01:52:26 +0000 Subject: [PATCH] Bug 1402282 - Change jemalloc to use secure random private arena ids r=glandium Previously the id for a new arena was just a counter that increased by one every time. For hardening purposes, we want to make private arenas use a secure random ID, so an attacker will have a more difficult time finding the memory they are looking for. Differential Revision: https://phabricator.services.mozilla.com/D10158 --HG-- extra : moz-landing-system : lando --- memory/build/mozjemalloc.cpp | 54 ++++++++++++++++++----- memory/replace/logalloc/replay/Replay.cpp | 6 --- memory/replace/logalloc/replay/moz.build | 4 +- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/memory/build/mozjemalloc.cpp b/memory/build/mozjemalloc.cpp index ac309e53e19c..d29339e00105 100644 --- a/memory/build/mozjemalloc.cpp +++ b/memory/build/mozjemalloc.cpp @@ -132,6 +132,7 @@ #include "mozilla/DoublyLinkedList.h" #include "mozilla/Likely.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/RandomNum.h" #include "mozilla/Sprintf.h" // Note: MozTaggedAnonymousMmap() could call an LD_PRELOADed mmap // instead of the one defined here; use only MozTagAnonymousMemory(). @@ -1158,8 +1159,10 @@ public: Mutex mLock; private: + inline arena_t* GetByIdInternal(arena_id_t aArenaId, bool aIsPrivate); + arena_t* mDefaultArena; - arena_id_t mLastArenaId; + arena_id_t mLastPublicArenaId; Tree mArenas; Tree mPrivateArenas; }; @@ -3734,10 +3737,34 @@ ArenaCollection::CreateArena(bool aIsPrivate, arena_params_t* aParams) MutexAutoLock lock(mLock); - // TODO: Use random Ids. - ret->mId = mLastArenaId++; - (aIsPrivate ? mPrivateArenas : mArenas).Insert(ret); - return ret; + // For public arenas, it's fine to just use incrementing arena id + if (!aIsPrivate) { + ret->mId = mLastPublicArenaId++; + mArenas.Insert(ret); + return ret; + } + + // For private arenas, generate a cryptographically-secure random id for the + // new arena. If an attacker manages to get control of the process, this + // should make it more difficult for them to "guess" the ID of a memory + // arena, stopping them from getting data they may want + + while(true) { + mozilla::Maybe maybeRandomId = mozilla::RandomUint64(); + MOZ_RELEASE_ASSERT(maybeRandomId.isSome()); + + // Keep looping until we ensure that the random number we just generated + // isn't already in use by another active arena + arena_t* existingArena = + GetByIdInternal(maybeRandomId.value(), true /*aIsPrivate*/); + + if (!existingArena) { + ret->mId = static_cast(maybeRandomId.value()); + mPrivateArenas.Insert(ret); + return ret; + } + } + } // End arena. @@ -4561,18 +4588,25 @@ MozJemalloc::jemalloc_free_dirty_pages(void) } } +inline arena_t* +ArenaCollection::GetByIdInternal(arena_id_t aArenaId, bool aIsPrivate) +{ + // Use AlignedStorage2 to avoid running the arena_t constructor, while + // we only need it as a placeholder for mId. + mozilla::AlignedStorage2 key; + key.addr()->mId = aArenaId; + return (aIsPrivate ? mPrivateArenas : mArenas).Search(key.addr()); +} + inline arena_t* ArenaCollection::GetById(arena_id_t aArenaId, bool aIsPrivate) { if (!malloc_initialized) { return nullptr; } - // Use AlignedStorage2 to avoid running the arena_t constructor, while - // we only need it as a placeholder for mId. - mozilla::AlignedStorage2 key; - key.addr()->mId = aArenaId; + MutexAutoLock lock(mLock); - arena_t* result = (aIsPrivate ? mPrivateArenas : mArenas).Search(key.addr()); + arena_t* result = GetByIdInternal(aArenaId, aIsPrivate); MOZ_RELEASE_ASSERT(result); return result; } diff --git a/memory/replace/logalloc/replay/Replay.cpp b/memory/replace/logalloc/replay/Replay.cpp index 4426e270ddb3..792795b1bb72 100644 --- a/memory/replace/logalloc/replay/Replay.cpp +++ b/memory/replace/logalloc/replay/Replay.cpp @@ -300,12 +300,6 @@ MOZ_BEGIN_EXTERN_C #include "malloc_decls.h" #ifdef ANDROID -/* mozjemalloc uses MozTagAnonymousMemory, which doesn't have an inline - * implementation on Android */ -void -MozTagAnonymousMemory(const void* aPtr, size_t aLength, const char* aTag) -{ -} /* mozjemalloc and jemalloc use pthread_atfork, which Android doesn't have. * While gecko has one in libmozglue, the replay program can't use that. diff --git a/memory/replace/logalloc/replay/moz.build b/memory/replace/logalloc/replay/moz.build index 58c0335c73b6..7f0e979e8089 100644 --- a/memory/replace/logalloc/replay/moz.build +++ b/memory/replace/logalloc/replay/moz.build @@ -8,6 +8,9 @@ Program('logalloc-replay') SOURCES += [ '/mfbt/Assertions.cpp', + '/mfbt/Poison.cpp', + '/mfbt/RandomNum.cpp', + '/mfbt/TaggedAnonymousMemory.cpp', '/mfbt/Unused.cpp', 'Replay.cpp', ] @@ -16,7 +19,6 @@ if CONFIG['MOZ_REPLACE_MALLOC_STATIC'] and CONFIG['MOZ_DMD']: UNIFIED_SOURCES += [ '/mfbt/HashFunctions.cpp', '/mfbt/JSONWriter.cpp', - '/mfbt/Poison.cpp', '/mozglue/misc/StackWalk.cpp', ]