ART: Randomize mem_map start address for linear scan search

When using linear scan for mem_map, randomize the start of the
search with getauxval(AT_RANDOM).

Change-Id: Id1e4c86b928147d74b9b0b73ff704de5d87b4500
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 8a555fb..49e0b54 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -20,6 +20,11 @@
 #include <backtrace/BacktraceMap.h>
 #include <memory>
 
+// See CreateStartPos below.
+#ifdef __BIONIC__
+#include <sys/auxv.h>
+#endif
+
 #include "base/stringprintf.h"
 #include "ScopedFd.h"
 #include "utils.h"
@@ -47,10 +52,61 @@
 }
 
 #if defined(__LP64__) && !defined(__x86_64__)
-// Where to start with low memory allocation. The first 64KB is protected by SELinux.
+// Handling mem_map in 32b address range for 64b architectures that do not support MAP_32BIT.
+
+// The regular start of memory allocations. The first 64KB is protected by SELinux.
 static constexpr uintptr_t LOW_MEM_START = 64 * KB;
 
-uintptr_t MemMap::next_mem_pos_ = LOW_MEM_START;   // first page to check for low-mem extent
+// Generate random starting position.
+// To not interfere with image position, take the image's address and only place it below. Current
+// formula (sketch):
+//
+// ART_BASE_ADDR      = 0001XXXXXXXXXXXXXXX
+// ----------------------------------------
+//                    = 0000111111111111111
+// & ~(kPageSize - 1) =~0000000000000001111
+// ----------------------------------------
+// mask               = 0000111111111110000
+// & random data      = YYYYYYYYYYYYYYYYYYY
+// -----------------------------------
+// tmp                = 0000YYYYYYYYYYY0000
+// + LOW_MEM_START    = 0000000000001000000
+// --------------------------------------
+// start
+//
+// getauxval as an entropy source is exposed in Bionic, but not in glibc before 2.16. When we
+// do not have Bionic, simply start with LOW_MEM_START.
+
+// Function is standalone so it can be tested somewhat in mem_map_test.cc.
+#ifdef __BIONIC__
+uintptr_t CreateStartPos(uint64_t input) {
+  CHECK_NE(0, ART_BASE_ADDRESS);
+
+  // Start with all bits below highest bit in ART_BASE_ADDRESS.
+  constexpr size_t leading_zeros = CLZ(static_cast<uint32_t>(ART_BASE_ADDRESS));
+  constexpr uintptr_t mask_ones = (1 << (31 - leading_zeros)) - 1;
+
+  // Lowest (usually 12) bits are not used, as aligned by page size.
+  constexpr uintptr_t mask = mask_ones & ~(kPageSize - 1);
+
+  // Mask input data.
+  return (input & mask) + LOW_MEM_START;
+}
+#endif
+
+static uintptr_t GenerateNextMemPos() {
+#ifdef __BIONIC__
+  uint8_t* random_data = reinterpret_cast<uint8_t*>(getauxval(AT_RANDOM));
+  // The lower 8B are taken for the stack guard. Use the upper 8B (with mask).
+  return CreateStartPos(*reinterpret_cast<uintptr_t*>(random_data + 8));
+#else
+  // No auxv on host, see above.
+  return LOW_MEM_START;
+#endif
+}
+
+// Initialize linear scan to random position.
+uintptr_t MemMap::next_mem_pos_ = GenerateNextMemPos();
 #endif
 
 static bool CheckMapRequest(byte* expected_ptr, void* actual_ptr, size_t byte_count,
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 4255d17..1411856 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -28,6 +28,12 @@
 namespace art {
 
 // Used to keep track of mmap segments.
+//
+// On 64b systems not supporting MAP_32BIT, the implementation of MemMap will do a linear scan
+// for free pages. For security, the start of this scan should be randomized. This requires a
+// dynamic initializer.
+// For this to work, it is paramount that there are no other static initializers that access MemMap.
+// Otherwise, calls might see uninitialized values.
 class MemMap {
  public:
   // Request an anonymous region of length 'byte_count' and a requested base address.
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index c07a9a3..c108a5f 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -84,8 +84,41 @@
     }
     delete m1;
   }
+
+#if defined(__LP64__) && !defined(__x86_64__)
+  static uintptr_t GetLinearScanPos() {
+    return MemMap::next_mem_pos_;
+  }
+#endif
 };
 
+#if defined(__LP64__) && !defined(__x86_64__)
+
+#ifdef __BIONIC__
+extern uintptr_t CreateStartPos(uint64_t input);
+#endif
+
+TEST_F(MemMapTest, Start) {
+  uintptr_t start = GetLinearScanPos();
+  EXPECT_LE(64 * KB, start);
+  EXPECT_LT(start, static_cast<uintptr_t>(ART_BASE_ADDRESS));
+
+#ifdef __BIONIC__
+  // Test a couple of values. Make sure they are different.
+  uintptr_t last = 0;
+  for (size_t i = 0; i < 100; ++i) {
+    uintptr_t random_start = CreateStartPos(i * kPageSize);
+    EXPECT_NE(last, random_start);
+    last = random_start;
+  }
+
+  // Even on max, should be below ART_BASE_ADDRESS.
+  EXPECT_LT(CreateStartPos(~0), static_cast<uintptr_t>(ART_BASE_ADDRESS));
+#endif
+  // End of test.
+}
+#endif
+
 TEST_F(MemMapTest, MapAnonymousEmpty) {
   std::string error_msg;
   std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty",