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/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<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.
@ -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*
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<arena_t> 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;
}

View File

@ -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.

View File

@ -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',
]