Bug 1402282 - Change jemalloc to use secure random private arena ids r=glandium
authorChris Martin <cmartin@mozilla.com>
Wed, 21 Nov 2018 01:52:26 +0000
changeset 503854 f425ff2bab2dcfd4a2db80822709244b5a35d8ed
parent 503853 b4754021fc2dbeb9a96fc85cacc52ec1c63d1453
child 503855 156f5b9586a6f0ea81d9bf4f07c8b84f7e5f19f3
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1402282
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
memory/build/mozjemalloc.cpp
memory/replace/logalloc/replay/Replay.cpp
memory/replace/logalloc/replay/moz.build
--- a/memory/build/mozjemalloc.cpp
+++ b/memory/build/mozjemalloc.cpp
@@ -127,16 +127,17 @@
 #include "mozilla/Alignment.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CheckedInt.h"
 #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().
 #include "mozilla/TaggedAnonymousMemory.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/fallible.h"
@@ -1153,18 +1154,20 @@ public:
 
   Iterator iter() { return Iterator(&mArenas, &mPrivateArenas); }
 
   inline arena_t* GetDefault() { return mDefaultArena; }
 
   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;
 };
 
 static ArenaCollection gArenas;
 
 // ******
 // Chunks.
@@ -3729,20 +3732,44 @@ ArenaCollection::CreateArena(bool aIsPri
     // In practice, this is an extremely unlikely failure.
     _malloc_message(_getprogname(), ": (malloc) Error initializing arena\n");
 
     return mDefaultArena;
   }
 
   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.
 // ***************************************************************************
 // Begin general internal functions.
 
 void*
 arena_t::MallocHuge(size_t aSize, bool aZero)
@@ -4557,27 +4584,34 @@ MozJemalloc::jemalloc_free_dirty_pages(v
     for (auto arena : gArenas.iter()) {
       MutexAutoLock arena_lock(arena->mLock);
       arena->Purge(true);
     }
   }
 }
 
 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;
 }
 
 template<>
 inline arena_id_t
 MozJemalloc::moz_create_arena_with_params(arena_params_t* aParams)
 {
--- a/memory/replace/logalloc/replay/Replay.cpp
+++ b/memory/replace/logalloc/replay/Replay.cpp
@@ -295,22 +295,16 @@ MOZ_BEGIN_EXTERN_C
 #define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
 #include "malloc_decls.h"
 
 #define MALLOC_DECL(name, return_type, ...) return_type name(__VA_ARGS__);
 #define MALLOC_FUNCS MALLOC_FUNCS_JEMALLOC
 #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.
  * Since we're not going to fork anyways, make it a dummy function. */
 int
 pthread_atfork(void (*aPrepare)(void),
                void (*aParent)(void),
                void (*aChild)(void))
--- a/memory/replace/logalloc/replay/moz.build
+++ b/memory/replace/logalloc/replay/moz.build
@@ -3,25 +3,27 @@
 # 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/.
 
 Program('logalloc-replay')
 
 SOURCES += [
     '/mfbt/Assertions.cpp',
+    '/mfbt/Poison.cpp',
+    '/mfbt/RandomNum.cpp',
+    '/mfbt/TaggedAnonymousMemory.cpp',
     '/mfbt/Unused.cpp',
     'Replay.cpp',
 ]
 
 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',
     ]
 
 if not CONFIG['MOZ_REPLACE_MALLOC_STATIC']:
     SOURCES += [
         '../FdPrintf.cpp',
     ]