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
This commit is contained in:
Chris Martin 2018-11-21 01:52:26 +00:00
parent 29879177ba
commit adde9e8556
3 changed files with 47 additions and 17 deletions

View file

@ -132,6 +132,7 @@
#include "mozilla/DoublyLinkedList.h" #include "mozilla/DoublyLinkedList.h"
#include "mozilla/Likely.h" #include "mozilla/Likely.h"
#include "mozilla/MathAlgorithms.h" #include "mozilla/MathAlgorithms.h"
#include "mozilla/RandomNum.h"
#include "mozilla/Sprintf.h" #include "mozilla/Sprintf.h"
// Note: MozTaggedAnonymousMmap() could call an LD_PRELOADed mmap // Note: MozTaggedAnonymousMmap() could call an LD_PRELOADed mmap
// instead of the one defined here; use only MozTagAnonymousMemory(). // instead of the one defined here; use only MozTagAnonymousMemory().
@ -1158,8 +1159,10 @@ public:
Mutex mLock; Mutex mLock;
private: private:
inline arena_t* GetByIdInternal(arena_id_t aArenaId, bool aIsPrivate);
arena_t* mDefaultArena; arena_t* mDefaultArena;
arena_id_t mLastArenaId; arena_id_t mLastPublicArenaId;
Tree mArenas; Tree mArenas;
Tree mPrivateArenas; Tree mPrivateArenas;
}; };
@ -3734,10 +3737,34 @@ ArenaCollection::CreateArena(bool aIsPrivate, arena_params_t* aParams)
MutexAutoLock lock(mLock); MutexAutoLock lock(mLock);
// TODO: Use random Ids. // For public arenas, it's fine to just use incrementing arena id
ret->mId = mLastArenaId++; if (!aIsPrivate) {
(aIsPrivate ? mPrivateArenas : mArenas).Insert(ret); ret->mId = mLastPublicArenaId++;
return ret; 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<uint64_t> 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<arena_id_t>(maybeRandomId.value());
mPrivateArenas.Insert(ret);
return ret;
}
}
} }
// End arena. // 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<arena_t> key;
key.addr()->mId = aArenaId;
return (aIsPrivate ? mPrivateArenas : mArenas).Search(key.addr());
}
inline arena_t* inline arena_t*
ArenaCollection::GetById(arena_id_t aArenaId, bool aIsPrivate) ArenaCollection::GetById(arena_id_t aArenaId, bool aIsPrivate)
{ {
if (!malloc_initialized) { if (!malloc_initialized) {
return nullptr; return nullptr;
} }
// Use AlignedStorage2 to avoid running the arena_t constructor, while
// we only need it as a placeholder for mId.
mozilla::AlignedStorage2<arena_t> key;
key.addr()->mId = aArenaId;
MutexAutoLock lock(mLock); MutexAutoLock lock(mLock);
arena_t* result = (aIsPrivate ? mPrivateArenas : mArenas).Search(key.addr()); arena_t* result = GetByIdInternal(aArenaId, aIsPrivate);
MOZ_RELEASE_ASSERT(result); MOZ_RELEASE_ASSERT(result);
return result; return result;
} }

View file

@ -300,12 +300,6 @@ MOZ_BEGIN_EXTERN_C
#include "malloc_decls.h" #include "malloc_decls.h"
#ifdef ANDROID #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. /* mozjemalloc and jemalloc use pthread_atfork, which Android doesn't have.
* While gecko has one in libmozglue, the replay program can't use that. * While gecko has one in libmozglue, the replay program can't use that.

View file

@ -8,6 +8,9 @@ Program('logalloc-replay')
SOURCES += [ SOURCES += [
'/mfbt/Assertions.cpp', '/mfbt/Assertions.cpp',
'/mfbt/Poison.cpp',
'/mfbt/RandomNum.cpp',
'/mfbt/TaggedAnonymousMemory.cpp',
'/mfbt/Unused.cpp', '/mfbt/Unused.cpp',
'Replay.cpp', 'Replay.cpp',
] ]
@ -16,7 +19,6 @@ if CONFIG['MOZ_REPLACE_MALLOC_STATIC'] and CONFIG['MOZ_DMD']:
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'/mfbt/HashFunctions.cpp', '/mfbt/HashFunctions.cpp',
'/mfbt/JSONWriter.cpp', '/mfbt/JSONWriter.cpp',
'/mfbt/Poison.cpp',
'/mozglue/misc/StackWalk.cpp', '/mozglue/misc/StackWalk.cpp',
] ]