Merge "Specify sections to merge in segment_gap_outer.lds more precisely"
diff --git a/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index a0f3c3c..b8f2f59 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -218,7 +218,7 @@
 
 Each ELF file has additional information contained in the section
 headers. These headers must be present now, because the dynamic linker
-uses them for sanity checking. Some developers strip them in an
+uses them for validity checking. Some developers strip them in an
 attempt to obfuscate the binary and prevent reverse engineering. (This
 doesn't really help because it is possible to reconstruct the stripped
 information using widely-available tools.)
diff --git a/benchmarks/linker_relocation/regen/gen_bench.py b/benchmarks/linker_relocation/regen/gen_bench.py
index 7482319..09efa75 100755
--- a/benchmarks/linker_relocation/regen/gen_bench.py
+++ b/benchmarks/linker_relocation/regen/gen_bench.py
@@ -137,7 +137,7 @@
     return defs
 
 
-def sanity_check_rels(root: LoadedLibrary, defs: Definitions) -> None:
+def check_rels(root: LoadedLibrary, defs: Definitions) -> None:
     # Find every symbol for every relocation in the load group.
     has_missing = False
     for lib in bfs_walk(root):
@@ -389,7 +389,7 @@
     with open(Path(args.input)) as f:
         root = json_to_elf_tree(json.load(f))
     defs = build_symbol_index(root)
-    sanity_check_rels(root, defs)
+    check_rels(root, defs)
 
     if out.exists(): shutil.rmtree(out)
     os.makedirs(str(out))
diff --git a/benchmarks/pthread_benchmark.cpp b/benchmarks/pthread_benchmark.cpp
index 9a68a12..856f150 100644
--- a/benchmarks/pthread_benchmark.cpp
+++ b/benchmarks/pthread_benchmark.cpp
@@ -53,15 +53,14 @@
 }
 BIONIC_BENCHMARK(BM_pthread_setspecific);
 
-static void DummyPthreadOnceInitFunction() {
-}
+static void NoOpPthreadOnceInitFunction() {}
 
 static void BM_pthread_once(benchmark::State& state) {
   static pthread_once_t once = PTHREAD_ONCE_INIT;
-  pthread_once(&once, DummyPthreadOnceInitFunction);
+  pthread_once(&once, NoOpPthreadOnceInitFunction);
 
   while (state.KeepRunning()) {
-    pthread_once(&once, DummyPthreadOnceInitFunction);
+    pthread_once(&once, NoOpPthreadOnceInitFunction);
   }
 }
 BIONIC_BENCHMARK(BM_pthread_once);
diff --git a/benchmarks/semaphore_benchmark.cpp b/benchmarks/semaphore_benchmark.cpp
index cf51489..ffccc82 100644
--- a/benchmarks/semaphore_benchmark.cpp
+++ b/benchmarks/semaphore_benchmark.cpp
@@ -28,8 +28,8 @@
   sem_init(&semaphore, 1, 1);
 
   while (state.KeepRunning()) {
-    int dummy;
-    sem_getvalue(&semaphore, &dummy);
+    int unused;
+    sem_getvalue(&semaphore, &unused);
   }
 }
 BIONIC_BENCHMARK(BM_semaphore_sem_getvalue);
@@ -44,112 +44,3 @@
   }
 }
 BIONIC_BENCHMARK(BM_semaphore_sem_wait_sem_post);
-
-// This test reports the overhead of the underlying futex wake syscall on
-// the producer. It does not report the overhead from issuing the wake to the
-// point where the posted consumer thread wakes up. It suffers from
-// clock_gettime syscall overhead. Lock the CPU speed for consistent results
-// as we may not reach >50% cpu utilization.
-//
-// We will run a background thread that catches the sem_post wakeup and
-// loops immediately returning back to sleep in sem_wait for the next one. This
-// thread is run with policy SCHED_OTHER (normal policy), a middle policy.
-//
-// The primary thread will run at SCHED_IDLE (lowest priority policy) when
-// monitoring the background thread to detect when it hits sem_wait sleep. It
-// will do so with no clock running. Once we are ready, we will switch to
-// SCHED_FIFO (highest priority policy) to time the act of running sem_post
-// with the benchmark clock running. This ensures nothing else in the system
-// can preempt our timed activity, including the background thread. We are
-// also protected with the scheduling policy of letting a process hit a
-// resource limit rather than get hit with a context switch.
-//
-// The background thread will start executing either on another CPU, or
-// after we back down from SCHED_FIFO, but certainly not in the context of
-// the timing of the sem_post.
-
-static atomic_int BM_semaphore_sem_post_running;
-
-static void* BM_semaphore_sem_post_start_thread(void* arg) {
-  sem_t* semaphore = reinterpret_cast<sem_t*>(arg);
-  while ((BM_semaphore_sem_post_running > 0) && !sem_wait(semaphore)) {
-  }
-  BM_semaphore_sem_post_running = -1;
-  return nullptr;
-}
-
-class SemaphoreFixture : public benchmark::Fixture {
- public:
-  void SetUp(const benchmark::State&) override {
-    sem_init(&semaphore, 0, 0);
-
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-
-    memset(&param, 0, sizeof(param));
-    pthread_attr_setschedparam(&attr, &param);
-    pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    pthread_t pthread;
-    pthread_create(&pthread, &attr, BM_semaphore_sem_post_start_thread, &semaphore);
-    pthread_attr_destroy(&attr);
-
-    sched_setscheduler(0, SCHED_IDLE, &param);
-
-    BM_semaphore_sem_post_running = 1;
-    setup = true;
-  }
-
-  ~SemaphoreFixture() override {
-    if (setup) {
-      // Only do this if the test was actually run.
-      sched_setscheduler(0, SCHED_OTHER, &param);
-
-      if (BM_semaphore_sem_post_running > 0) {
-        BM_semaphore_sem_post_running = 0;
-      }
-      do {
-        sem_post(&semaphore);
-        sched_yield();
-      } while (BM_semaphore_sem_post_running != -1);
-    }
-  }
-
-  sem_t semaphore;
-  sched_param param;
-  bool setup = false;
-};
-
-// This is commented out because dynamic benchmark registering doesn't currently support fixtures.
-// Uncomment it and recompile to run this benchmark on every run.
-/* BENCHMARK_F(SemaphoreFixture, semaphore_sem_post)(benchmark::State& state) {
-  while (state.KeepRunning()) {
-    state.PauseTiming();
-
-    int trys = 3, dummy = 0;
-    do {
-      if (BM_semaphore_sem_post_running < 0) {
-        sched_setscheduler(0, SCHED_OTHER, &param);
-        fprintf(stderr, "BM_semaphore_sem_post: start_thread died unexpectedly\n");
-        abort();
-      }
-      sched_yield();
-      sem_getvalue(&semaphore, &dummy);
-      if (dummy < 0) {  // POSIX.1-2001 possibility 1
-        break;
-      }
-      if (dummy == 0) { // POSIX.1-2001 possibility 2
-        --trys;
-      }
-    } while (trys);
-
-    param.sched_priority = 1;
-    sched_setscheduler(0, SCHED_FIFO, &param);
-
-    state.ResumeTiming();
-    sem_post(&semaphore);
-
-    param.sched_priority = 0;
-    sched_setscheduler(0, SCHED_IDLE, &param);
-  }
-}*/
diff --git a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
index 5c44b4e..db931d1 100644
--- a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
+++ b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
@@ -37,8 +37,8 @@
 }
 
 __LIBC_HIDDEN__ void __libc_init_sysinfo() {
-  bool dummy;
-  __libc_sysinfo = reinterpret_cast<void*>(__bionic_getauxval(AT_SYSINFO, dummy));
+  bool unused;
+  __libc_sysinfo = reinterpret_cast<void*>(__bionic_getauxval(AT_SYSINFO, &unused));
 }
 
 // TODO: lose this function and just access __libc_sysinfo directly.
diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp
index d6f75f8..a3c6b19 100644
--- a/libc/bionic/getauxval.cpp
+++ b/libc/bionic/getauxval.cpp
@@ -37,20 +37,20 @@
 
 // This function needs to be safe to call before TLS is set up, so it can't
 // access errno or the stack protector.
-__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool& exists) {
+__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool* exists) {
   for (ElfW(auxv_t)* v = __libc_shared_globals()->auxv; v->a_type != AT_NULL; ++v) {
     if (v->a_type == type) {
-      exists = true;
+      *exists = true;
       return v->a_un.a_val;
     }
   }
-  exists = false;
+  *exists = false;
   return 0;
 }
 
 extern "C" unsigned long getauxval(unsigned long type) {
   bool exists;
-  unsigned long result = __bionic_getauxval(type, exists);
+  unsigned long result = __bionic_getauxval(type, &exists);
   if (!exists) errno = ENOENT;
   return result;
 }
diff --git a/libc/bionic/ifaddrs.cpp b/libc/bionic/ifaddrs.cpp
index e89b0bf..1536333 100644
--- a/libc/bionic/ifaddrs.cpp
+++ b/libc/bionic/ifaddrs.cpp
@@ -28,6 +28,7 @@
 
 #include <ifaddrs.h>
 
+#include <async_safe/log.h>
 #include <cutils/misc.h>           // FIRST_APPLICATION_UID
 #include <errno.h>
 #include <linux/if_packet.h>
@@ -205,12 +206,12 @@
     new_addr->interface_index = static_cast<int>(msg->ifa_index);
 
     // If this is a known interface, copy what we already know.
+    // If we don't know about this interface yet, we try to resolve the name and flags using ioctl
+    // calls during postprocessing.
     if (known_addr != nullptr) {
       strcpy(new_addr->name, known_addr->name);
       new_addr->ifa.ifa_name = new_addr->name;
       new_addr->ifa.ifa_flags = known_addr->ifa.ifa_flags;
-    } else {
-      new_addr->ifa.ifa_flags = msg->ifa_flags;
     }
 
     // Go through the various bits of information and find the name, address
@@ -271,10 +272,34 @@
     } else {
       prev_addr = addr;
     }
+
     addr = reinterpret_cast<ifaddrs_storage*>(next_addr);
   }
 }
 
+static void get_interface_flags_via_ioctl(ifaddrs** list) {
+  ScopedFd s(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+  if (s.get() == -1) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC) failed in ifaddrs: %s",
+                          strerror(errno));
+    return;
+  }
+
+  for (ifaddrs_storage* addr = reinterpret_cast<ifaddrs_storage*>(*list); addr != nullptr;
+       addr = reinterpret_cast<ifaddrs_storage*>(addr->ifa.ifa_next)) {
+    ifreq ifr = {};
+    strlcpy(ifr.ifr_name, addr->ifa.ifa_name, sizeof(ifr.ifr_name));
+    if (ioctl(s.get(), SIOCGIFFLAGS, &ifr) != -1) {
+      addr->ifa.ifa_flags = ifr.ifr_flags;
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                            "ioctl(SIOCGIFFLAGS) for \"%s\" failed in ifaddrs: %s",
+                            addr->ifa.ifa_name, strerror(errno));
+    }
+  }
+}
+
 int getifaddrs(ifaddrs** out) {
   // We construct the result directly into `out`, so terminate the list.
   *out = nullptr;
@@ -303,6 +328,9 @@
     // If we weren't able to depend on GETLINK messages, it's possible some
     // interfaces never got their name set. Resolve them using if_indextoname or remove them.
     resolve_or_remove_nameless_interfaces(out);
+    // Similarly, without GETLINK messages, interfaces will not have their flags set.
+    // Resolve them using the SIOCGIFFLAGS ioctl call.
+    get_interface_flags_via_ioctl(out);
   }
 
   return 0;
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 1ede969..f1350d5 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -359,10 +359,8 @@
   Dtor* fini_array = reinterpret_cast<Dtor*>(array);
   const Dtor minus1 = reinterpret_cast<Dtor>(static_cast<uintptr_t>(-1));
 
-  // Sanity check - first entry must be -1.
-  if (array == nullptr || fini_array[0] != minus1) {
-    return;
-  }
+  // Validity check: the first entry must be -1.
+  if (array == nullptr || fini_array[0] != minus1) return;
 
   // Skip over it.
   fini_array += 1;
@@ -373,15 +371,9 @@
     ++count;
   }
 
-  // Now call each destructor in reverse order.
+  // Now call each destructor in reverse order, ignoring any -1s.
   while (count > 0) {
     Dtor dtor = fini_array[--count];
-
-    // Sanity check, any -1 in the list is ignored.
-    if (dtor == minus1) {
-      continue;
-    }
-
-    dtor();
+    if (dtor != minus1) dtor();
   }
 }
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index 1551c1f..89aa289 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -192,7 +192,8 @@
     return errno;
   }
 
-  // If the current RLIMIT_STACK is RLIM_INFINITY, only admit to an 8MiB stack for sanity's sake.
+  // If the current RLIMIT_STACK is RLIM_INFINITY, only admit to an 8MiB stack
+  // in case callers such as ART take infinity too literally.
   if (stack_limit.rlim_cur == RLIM_INFINITY) {
     stack_limit.rlim_cur = 8 * 1024 * 1024;
   }
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index d4a8bef..c528105 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -350,7 +350,7 @@
   return 0;
 }
 
-// A dummy start routine for pthread_create failures where we've created a thread but aren't
+// A no-op start routine for pthread_create failures where we've created a thread but aren't
 // going to run user code on it. We swap out the user's start routine for this and take advantage
 // of the regular thread teardown to free up resources.
 static void* __do_nothing(void*) {
diff --git a/libc/bionic/sysinfo.cpp b/libc/bionic/sysinfo.cpp
index 1d1070e..7ab8e9e 100644
--- a/libc/bionic/sysinfo.cpp
+++ b/libc/bionic/sysinfo.cpp
@@ -38,9 +38,10 @@
 
 static bool __matches_cpuN(const char* s) {
   // The %c trick is to ensure that we have the anchored match "^cpu[0-9]+$".
+  // We can't use %*c because the return value is how many were *assigned*.
   unsigned cpu;
-  char dummy;
-  return (sscanf(s, "cpu%u%c", &cpu, &dummy) == 1);
+  char unused;
+  return (sscanf(s, "cpu%u%c", &cpu, &unused) == 1);
 }
 
 int get_nprocs_conf() {
diff --git a/libc/include/nl_types.h b/libc/include/nl_types.h
index 622880a..1c80e4e 100644
--- a/libc/include/nl_types.h
+++ b/libc/include/nl_types.h
@@ -32,7 +32,7 @@
  * @file nl_types.h
  * @brief Message catalogs.
  *
- * Android offers a dummy implementation of these functions to ease porting of historical software.
+ * Android offers a no-op implementation of these functions to ease porting of historical software.
  */
 
 #include <sys/cdefs.h>
diff --git a/libc/private/bionic_auxv.h b/libc/private/bionic_auxv.h
index 8e33c1c..9d2cfdd 100644
--- a/libc/private/bionic_auxv.h
+++ b/libc/private/bionic_auxv.h
@@ -30,4 +30,4 @@
 
 #include <sys/cdefs.h>
 
-__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool& exists);
+__LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool* exists);
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 1ecf122..6ffda49 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -259,8 +259,8 @@
 size_t parsefloat(FILE*, char*, char*);
 size_t wparsefloat(FILE*, wchar_t*, wchar_t*);
 
-// Sanity check a FILE* for nullptr, so we can emit a message while crashing
-// instead of doing a blind null-dereference.
+// Check a FILE* isn't nullptr, so we can emit a clear diagnostic message
+// instead of just crashing with SIGSEGV.
 #define CHECK_FP(fp) \
   if (fp == nullptr) __fortify_fatal("%s: null FILE*", __FUNCTION__)
 
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index a0b4219..afc2c48 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -1026,9 +1026,9 @@
   __check_count("vsnprintf", "size", n);
 
   // Stdio internals do not deal correctly with zero length buffer.
-  char dummy;
+  char one_byte_buffer[1];
   if (n == 0) {
-    s = &dummy;
+    s = one_byte_buffer;
     n = 1;
   }
 
diff --git a/libc/tzcode/bionic.cpp b/libc/tzcode/bionic.cpp
index 182fa89..e134aaa 100644
--- a/libc/tzcode/bionic.cpp
+++ b/libc/tzcode/bionic.cpp
@@ -90,12 +90,12 @@
 // byte[12] tzdata_version  -- "tzdata2012f\0"
 // int index_offset
 // int data_offset
-// int zonetab_offset
+// int final_offset
 struct bionic_tzdata_header_t {
   char tzdata_version[12];
   int32_t index_offset;
   int32_t data_offset;
-  int32_t zonetab_offset;
+  int32_t final_offset;
 };
 static constexpr size_t NAME_LENGTH = 40;
 struct index_entry_t {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 10608f4..7cfe87b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -311,6 +311,10 @@
     }
   }
 
+  if (si->has_min_version(6) && si->get_gap_size()) {
+    munmap(reinterpret_cast<void*>(si->get_gap_start()), si->get_gap_size());
+  }
+
   TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);
 
   if (!solist_remove_soinfo(si)) {
@@ -599,6 +603,8 @@
     si_->load_bias = elf_reader.load_bias();
     si_->phnum = elf_reader.phdr_count();
     si_->phdr = elf_reader.loaded_phdr();
+    si_->set_gap_start(elf_reader.gap_start());
+    si_->set_gap_size(elf_reader.gap_size());
 
     return true;
   }
@@ -3162,7 +3168,7 @@
   DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
         reinterpret_cast<void*>(base), strtab_, symtab_);
 
-  // Sanity checks.
+  // Validity checks.
   if (relocating_linker && needed_count != 0) {
     DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
     return false;
diff --git a/linker/linker_block_allocator_test.cpp b/linker/linker_block_allocator_test.cpp
index 359eefb..6fb2b26 100644
--- a/linker/linker_block_allocator_test.cpp
+++ b/linker/linker_block_allocator_test.cpp
@@ -47,14 +47,14 @@
  * this one has size below allocator cap which is 2*sizeof(void*)
  */
 struct test_struct_small {
-  char dummy_str[5];
+  char str[5];
 };
 
 /*
  * 1009 byte struct (1009 is prime)
  */
 struct test_struct_larger {
-  char dummy_str[1009];
+  char str[1009];
 };
 
 static size_t kPageSize = sysconf(_SC_PAGE_SIZE);
@@ -131,14 +131,14 @@
   allocator.protect_all(PROT_READ);
   allocator.protect_all(PROT_READ | PROT_WRITE);
   // check access
-  page2_ptr->dummy_str[23] = 27;
-  page1_ptr->dummy_str[13] = 11;
+  page2_ptr->str[23] = 27;
+  page1_ptr->str[13] = 11;
 
   allocator.protect_all(PROT_READ);
   fprintf(stderr, "trying to access protected page");
 
   // this should result in segmentation fault
-  page1_ptr->dummy_str[11] = 7;
+  page1_ptr->str[11] = 7;
 }
 
 TEST(linker_allocator, test_protect) {
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 9b7a461..1e89094 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -520,7 +520,8 @@
 
 // Reserve a virtual address range such that if it's limits were extended to the next 2**align
 // boundary, it would not overlap with any existing mappings.
-static void* ReserveAligned(size_t size, size_t align) {
+static void* ReserveWithAlignmentPadding(size_t size, size_t align, void** out_gap_start,
+                                         size_t* out_gap_size) {
   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
   if (align == PAGE_SIZE) {
     void* mmap_ptr = mmap(nullptr, size, PROT_NONE, mmap_flags, -1, 0);
@@ -530,6 +531,15 @@
     return mmap_ptr;
   }
 
+  // Minimum alignment of shared library gap. For efficiency, this should match the second level
+  // page size of the platform.
+#if defined(__LP64__)
+  constexpr size_t kGapAlignment = 1ul << 21;  // 2MB
+#else
+  constexpr size_t kGapAlignment = 0;
+#endif
+  // Maximum gap size, in the units of kGapAlignment.
+  constexpr size_t kMaxGapUnits = 32;
   // Allocate enough space so that the end of the desired region aligned up is still inside the
   // mapping.
   size_t mmap_size = align_up(size, align) + align - PAGE_SIZE;
@@ -538,16 +548,49 @@
   if (mmap_ptr == MAP_FAILED) {
     return nullptr;
   }
+  size_t gap_size = 0;
+  size_t first_byte = reinterpret_cast<size_t>(align_up(mmap_ptr, align));
+  size_t last_byte = reinterpret_cast<size_t>(align_down(mmap_ptr + mmap_size, align) - 1);
+  if (kGapAlignment && first_byte / kGapAlignment != last_byte / kGapAlignment) {
+    // This library crosses a 2MB boundary and will fragment a new huge page.
+    // Lets take advantage of that and insert a random number of inaccessible huge pages before that
+    // to improve address randomization and make it harder to locate this library code by probing.
+    munmap(mmap_ptr, mmap_size);
+    align = std::max(align, kGapAlignment);
+    gap_size =
+        kGapAlignment * (is_first_stage_init() ? 1 : arc4random_uniform(kMaxGapUnits - 1) + 1);
+    mmap_size = align_up(size + gap_size, align) + align - PAGE_SIZE;
+    mmap_ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, mmap_size, PROT_NONE, mmap_flags, -1, 0));
+    if (mmap_ptr == MAP_FAILED) {
+      return nullptr;
+    }
+  }
+
+  uint8_t *gap_end, *gap_start;
+  if (gap_size) {
+    gap_end = align_down(mmap_ptr + mmap_size, kGapAlignment);
+    gap_start = gap_end - gap_size;
+  } else {
+    gap_start = gap_end = mmap_ptr + mmap_size;
+  }
 
   uint8_t* first = align_up(mmap_ptr, align);
-  uint8_t* last = align_down(mmap_ptr + mmap_size, align) - size;
+  uint8_t* last = align_down(gap_start, align) - size;
 
   // arc4random* is not available in first stage init because /dev/urandom hasn't yet been
   // created. Don't randomize then.
   size_t n = is_first_stage_init() ? 0 : arc4random_uniform((last - first) / PAGE_SIZE + 1);
   uint8_t* start = first + n * PAGE_SIZE;
+  // Unmap the extra space around the allocation.
+  // Keep it mapped PROT_NONE on 64-bit targets where address space is plentiful to make it harder
+  // to defeat ASLR by probing for readable memory mappings.
   munmap(mmap_ptr, start - mmap_ptr);
-  munmap(start + size, mmap_ptr + mmap_size - (start + size));
+  munmap(start + size, gap_start - (start + size));
+  if (gap_end != mmap_ptr + mmap_size) {
+    munmap(gap_end, mmap_ptr + mmap_size - gap_end);
+  }
+  *out_gap_start = gap_start;
+  *out_gap_size = gap_size;
   return start;
 }
 
@@ -571,13 +614,15 @@
              load_size_ - address_space->reserved_size, load_size_, name_.c_str());
       return false;
     }
-    start = ReserveAligned(load_size_, kLibraryAlignment);
+    start = ReserveWithAlignmentPadding(load_size_, kLibraryAlignment, &gap_start_, &gap_size_);
     if (start == nullptr) {
       DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
       return false;
     }
   } else {
     start = address_space->start_addr;
+    gap_start_ = nullptr;
+    gap_size_ = 0;
     mapped_by_caller_ = true;
 
     // Update the reserved address space to subtract the space used by this library.
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 5d1cfc2..4cb48f5 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -49,6 +49,8 @@
   size_t phdr_count() const { return phdr_num_; }
   ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); }
   size_t load_size() const { return load_size_; }
+  ElfW(Addr) gap_start() const { return reinterpret_cast<ElfW(Addr)>(gap_start_); }
+  size_t gap_size() const { return gap_size_; }
   ElfW(Addr) load_bias() const { return load_bias_; }
   const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; }
   const ElfW(Dyn)* dynamic() const { return dynamic_; }
@@ -96,6 +98,10 @@
   void* load_start_;
   // Size in bytes of reserved address space.
   size_t load_size_;
+  // First page of inaccessible gap mapping reserved for this DSO.
+  void* gap_start_;
+  // Size in bytes of the gap mapping.
+  size_t gap_size_;
   // Load bias.
   ElfW(Addr) load_bias_;
 
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 4f67003..60fd242 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -900,6 +900,24 @@
   g_soinfo_handles_map[handle_] = this;
 }
 
+void soinfo::set_gap_start(ElfW(Addr) gap_start) {
+  CHECK(has_min_version(6));
+  gap_start_ = gap_start;
+}
+ElfW(Addr) soinfo::get_gap_start() const {
+  CHECK(has_min_version(6));
+  return gap_start_;
+}
+
+void soinfo::set_gap_size(size_t gap_size) {
+  CHECK(has_min_version(6));
+  gap_size_ = gap_size;
+}
+size_t soinfo::get_gap_size() const {
+  CHECK(has_min_version(6));
+  return gap_size_;
+}
+
 // TODO(dimitry): Move SymbolName methods to a separate file.
 
 uint32_t calculate_elf_hash(const char* name) {
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index e1a3c30..7372a51 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -66,7 +66,7 @@
 #define FLAG_PRELINKED        0x00000400 // prelink_image has successfully processed this soinfo
 #define FLAG_NEW_SOINFO       0x40000000 // new soinfo format
 
-#define SOINFO_VERSION 5
+#define SOINFO_VERSION 6
 
 ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr);
 
@@ -345,6 +345,12 @@
 
   SymbolLookupLib get_lookup_lib();
 
+  void set_gap_start(ElfW(Addr) gap_start);
+  ElfW(Addr) get_gap_start() const;
+
+  void set_gap_size(size_t gap_size);
+  size_t get_gap_size() const;
+
  private:
   bool is_image_linked() const;
   void set_image_linked();
@@ -423,6 +429,10 @@
   // version >= 5
   std::unique_ptr<soinfo_tls> tls_;
   std::vector<TlsDynamicResolverArg> tlsdesc_args_;
+
+  // version >= 6
+  ElfW(Addr) gap_start_;
+  size_t gap_size_;
 };
 
 // This function is used by dlvsym() to calculate hash of sym_ver
diff --git a/tests/bionic_allocator_test.cpp b/tests/bionic_allocator_test.cpp
index f710907..fdcf868 100644
--- a/tests/bionic_allocator_test.cpp
+++ b/tests/bionic_allocator_test.cpp
@@ -42,19 +42,19 @@
  * this one has size below allocator cap which is 2*sizeof(void*)
  */
 struct test_struct_small {
-  char dummy_str[5];
+  char str[5];
 };
 
 struct test_struct_large {
-  char dummy_str[1009];
+  char str[1009];
 };
 
 struct test_struct_huge {
-  char dummy_str[73939];
+  char str[73939];
 };
 
 struct test_struct_512 {
-  char dummy_str[503];
+  char str[503];
 };
 
 };
diff --git a/tests/cfi_test.cpp b/tests/cfi_test.cpp
index 792f917..e0ae3af 100644
--- a/tests/cfi_test.cpp
+++ b/tests/cfi_test.cpp
@@ -95,9 +95,6 @@
   EXPECT_EQ(get_global_address(), get_last_address());
   EXPECT_EQ(c, get_count());
 
-  // CFI check for a stack address. This is always invalid and gets the process killed.
-  EXPECT_DEATH(__cfi_slowpath(45, reinterpret_cast<void*>(&c)), "");
-
   // CFI check for a heap address.
   // It's possible that this allocation could wind up in the same CFI granule as
   // an unchecked library, which means the below might not crash. To force a
diff --git a/tests/clang_fortify_tests.cpp b/tests/clang_fortify_tests.cpp
index 715f9c8..0d17284 100644
--- a/tests/clang_fortify_tests.cpp
+++ b/tests/clang_fortify_tests.cpp
@@ -18,17 +18,24 @@
 #error "Non-clang isn't supported"
 #endif
 
+//
 // Clang compile-time and run-time tests for Bionic's FORTIFY.
 //
-// This file is compiled in two configurations ways to give us a sane set of tests for clang's
-// FORTIFY implementation.
+
+// This file is compiled in two configurations to give us reasonable coverage of clang's
+// FORTIFY implementation:
 //
-// One configuration uses clang's diagnostic consumer
+// 1. For compile-time checks, we use clang's diagnostic consumer
 // (https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html#details)
 // to check diagnostics (e.g. the expected-* comments everywhere).
 //
-// Please note that this test does things like leaking memory. That's WAI.
+// 2. For run-time checks, we build and run as regular gtests.
 
+// Note that these tests do things like leaking memory. That's WAI.
+
+//
+// Configuration for the compile-time checks. (These comments have side effects!)
+//
 // Silence all "from 'diagnose_if'" `note`s from anywhere, including headers; they're uninteresting
 // for this test case, and their line numbers may change over time.
 // expected-note@* 0+{{from 'diagnose_if'}}
@@ -39,8 +46,8 @@
 // And finally, all explicitly-unavailable-here complaints from headers are
 // uninteresting
 // expected-note@* 0+{{has been explicitly marked unavailable here}}
-
-// Note that some of these diags come from clang itself, while others come from
+//
+// Note that some of these diagnostics come from clang itself, while others come from
 // `diagnose_if`s sprinkled throughout Bionic.
 
 #ifndef _FORTIFY_SOURCE
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 1139e53..4c4a1c3 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -2022,7 +2022,7 @@
     }
   }
 
-  // some sanity checks..
+  // Some validity checks.
   ASSERT_TRUE(addr_start > 0);
   ASSERT_TRUE(addr_end > 0);
   ASSERT_TRUE(maps_to_copy.size() > 0);
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index 9a4b781..6907abe 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -14,14 +14,6 @@
  * limitations under the License.
  */
 
-// -Werror is on whether we like it or not, and we're intentionally doing awful
-// things in this file. GCC is dumb and doesn't have a specific error class for
-// the fortify failures (it's just -Werror), so we can't use anything more
-// constrained than disabling all the warnings in the file :( It also won't let
-// us use system_header in a .cpp file, so we have to #include this from
-// fortify_test_main.cpp.
-#pragma GCC system_header
-
 #include <gtest/gtest.h>
 #include "BionicDeathTest.h"
 
diff --git a/tests/ftw_test.cpp b/tests/ftw_test.cpp
index dfc4d72..200ed4b 100644
--- a/tests/ftw_test.cpp
+++ b/tests/ftw_test.cpp
@@ -49,7 +49,7 @@
   ASSERT_EQ(0, close(fd));
 }
 
-void sanity_check_ftw(const char* fpath, const struct stat* sb, int tflag) {
+void smoke_test_ftw(const char* fpath, const struct stat* sb, int tflag) {
   ASSERT_TRUE(fpath != nullptr);
   ASSERT_TRUE(sb != nullptr);
 
@@ -75,28 +75,28 @@
   }
 }
 
-void sanity_check_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
-  sanity_check_ftw(fpath, sb, tflag);
+void smoke_test_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
+  smoke_test_ftw(fpath, sb, tflag);
   ASSERT_EQ('/', fpath[ftwbuf->base - 1]) << fpath;
 }
 
 int check_ftw(const char* fpath, const struct stat* sb, int tflag) {
-  sanity_check_ftw(fpath, sb, tflag);
+  smoke_test_ftw(fpath, sb, tflag);
   return 0;
 }
 
 int check_ftw64(const char* fpath, const struct stat64* sb, int tflag) {
-  sanity_check_ftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag);
+  smoke_test_ftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag);
   return 0;
 }
 
 int check_nftw(const char* fpath, const struct stat* sb, int tflag, FTW* ftwbuf) {
-  sanity_check_nftw(fpath, sb, tflag, ftwbuf);
+  smoke_test_nftw(fpath, sb, tflag, ftwbuf);
   return 0;
 }
 
 int check_nftw64(const char* fpath, const struct stat64* sb, int tflag, FTW* ftwbuf) {
-  sanity_check_nftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag, ftwbuf);
+  smoke_test_nftw(fpath, reinterpret_cast<const struct stat*>(sb), tflag, ftwbuf);
   return 0;
 }
 
diff --git a/tests/libs/dlopen_b.cpp b/tests/libs/dlopen_b.cpp
index cd81e16..092c96c 100644
--- a/tests/libs/dlopen_b.cpp
+++ b/tests/libs/dlopen_b.cpp
@@ -1,15 +1,14 @@
 #include <dlfcn.h>
 extern "C" void *dlopen_b() {
-  // TODO (dimitry): this is to work around http://b/20049306
-  // remove once it is fixed
-  static int dummy = 0;
+  // Work around for http://b/20049306, which isn't going to be fixed.
+  static int defeat_sibling_call_optimization = 0;
 
   // This is supposed to succeed because this library has DT_RUNPATH
   // for libtest_dt_runpath_x.so which should be taken into account
   // by dlopen.
   void *handle = dlopen("libtest_dt_runpath_x.so", RTLD_NOW);
   if (handle != nullptr) {
-    dummy++;
+    defeat_sibling_call_optimization++;
     return handle;
   }
   return nullptr;
diff --git a/tests/libs/elftls_dynamic.cpp b/tests/libs/elftls_dynamic.cpp
index 6da2f6f..2500484 100644
--- a/tests/libs/elftls_dynamic.cpp
+++ b/tests/libs/elftls_dynamic.cpp
@@ -41,8 +41,8 @@
 // section, but does not have an entry in the dynsym table and whose
 // solib-relative address appears to overlap with the large TLS variable.
 extern "C" void* get_local_addr() {
-  static char dummy[1024];
-  return &dummy[512];
+  static char buf[1024];
+  return &buf[512];
 }
 
 // This variable comes from libtest_elftls_shared_var.so, which is part of
diff --git a/tests/link_test.cpp b/tests/link_test.cpp
index 75bb4d6..127a3d9 100644
--- a/tests/link_test.cpp
+++ b/tests/link_test.cpp
@@ -258,7 +258,7 @@
   ASSERT_TRUE(entries != nullptr);
   ASSERT_GT(count, 0);
 
-  // Sanity checks
+  // Validity checks.
   uintptr_t func = reinterpret_cast<uintptr_t>(read_exidx_func);
   bool found = false;
   for (int i = 0; i < count; ++i) {
diff --git a/tests/stdatomic_test.cpp b/tests/stdatomic_test.cpp
index 11d41b4..7b98df2 100644
--- a/tests/stdatomic_test.cpp
+++ b/tests/stdatomic_test.cpp
@@ -192,7 +192,7 @@
   atomic_uint_least32_t z;
 };
 
-// Very simple acquire/release memory ordering sanity check.
+// Very simple acquire/release memory ordering smoke test.
 static void* writer(void* arg) {
   three_atomics* a = reinterpret_cast<three_atomics*>(arg);
   for (uint_least32_t i = 0; i <= BIG; i+=2) {
@@ -239,7 +239,7 @@
 }
 
 TEST(stdatomic, ordering) {
-  // Run a memory ordering sanity test.
+  // Run a memory ordering smoke test.
   void* result;
   three_atomics a;
   atomic_init(&a.x, 0ul);
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index b41c865..ec2e38d 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -190,8 +190,8 @@
     auto symbol_it = database.symbols.find(declaration_name);
     if (symbol_it == database.symbols.end()) {
       Symbol symbol = {.name = declaration_name };
-      bool dummy;
-      std::tie(symbol_it, dummy) = database.symbols.insert({ declaration_name, symbol });
+      bool unused;
+      std::tie(symbol_it, unused) = database.symbols.insert({declaration_name, symbol});
     }
 
     auto expansion_range = src_manager.getExpansionRange(range);
diff --git a/tools/versioner/src/Preprocessor.cpp b/tools/versioner/src/Preprocessor.cpp
index 7a5b502..eb88c46 100644
--- a/tools/versioner/src/Preprocessor.cpp
+++ b/tools/versioner/src/Preprocessor.cpp
@@ -237,7 +237,7 @@
   return "("s + Join(expressions, ") || (") + ")";
 }
 
-// Assumes that nothing crazy is happening (e.g. having the semicolon be in a macro)
+// Assumes that nothing weird is happening (e.g. having the semicolon be in a macro).
 static FileLocation findNextSemicolon(const std::deque<std::string>& lines, FileLocation start) {
   unsigned current_line = start.line;
   unsigned current_column = start.column;
@@ -373,8 +373,8 @@
 
     guard_map.erase(current);
     guard_map.erase(next);
-    bool dummy;
-    std::tie(current, dummy) = guard_map.insert(std::make_pair(merged, avail));
+    bool unused;
+    std::tie(current, unused) = guard_map.insert(std::make_pair(merged, avail));
     next = current;
     ++next;
   }
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 99228dd..c818197 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -276,7 +276,7 @@
   return intersection;
 }
 
-// Perform a sanity check on a symbol's declarations, enforcing the following invariants:
+// Perform a validity check on a symbol's declarations, enforcing the following invariants:
 //   1. At most one inline definition of the function exists (overloaded inline functions for
 //      _FORTIFY_SOURCE do not count because they are usually introduced to intercept the original
 //      functions or usually have enable_if attributes).
@@ -334,7 +334,7 @@
   return true;
 }
 
-static bool sanityCheck(const HeaderDatabase* database) {
+static bool validityCheck(const HeaderDatabase* database) {
   bool error = false;
   std::string cwd = getWorkingDir() + "/";
 
@@ -676,8 +676,8 @@
   if (dump) {
     declaration_database->dump(location.header_path + "/");
   } else {
-    if (!sanityCheck(declaration_database.get())) {
-      printf("versioner: sanity check failed\n");
+    if (!validityCheck(declaration_database.get())) {
+      printf("versioner: validity check failed\n");
       failed = true;
     }
 
diff --git a/tools/versioner/tests/multiple_decl_mismatch/expected_fail b/tools/versioner/tests/multiple_decl_mismatch/expected_fail
index 1d1f266..30e7233 100644
--- a/tools/versioner/tests/multiple_decl_mismatch/expected_fail
+++ b/tools/versioner/tests/multiple_decl_mismatch/expected_fail
@@ -5,4 +5,4 @@
       obsoleted = 12
     extern declaration @ headers/foo.h:5:1
       obsoleted = 9
-versioner: sanity check failed
+versioner: validity check failed
diff --git a/tools/versioner/tests/multiple_definition/expected_fail b/tools/versioner/tests/multiple_definition/expected_fail
index cb4acc6..5abb833 100644
--- a/tools/versioner/tests/multiple_definition/expected_fail
+++ b/tools/versioner/tests/multiple_definition/expected_fail
@@ -4,4 +4,4 @@
       no availability
     static definition @ headers/bar.h:5:1
       no availability
-versioner: sanity check failed
+versioner: validity check failed
diff --git a/tools/versioner/tests/version_mismatch/expected_fail b/tools/versioner/tests/version_mismatch/expected_fail
index f2143a3..95d284b 100644
--- a/tools/versioner/tests/version_mismatch/expected_fail
+++ b/tools/versioner/tests/version_mismatch/expected_fail
@@ -5,4 +5,4 @@
       introduced = 9
     extern declaration @ headers/foo.h:8:1
       introduced = 10
-versioner: sanity check failed
+versioner: validity check failed