[asan] Support running without /proc

Summary:
This patch lets ASan run when /proc is not accessible (ex. not mounted
yet). It includes a special test-only flag that emulates this condition
in an unpriviledged process.

This only matters on Linux, where /proc is necessary to enumerate
virtual memory mappings.

Reviewers: vitalybuka, pcc, krytarowski

Subscribers: kubamracek, llvm-commits

Differential Revision: https://reviews.llvm.org/D56141

Change-Id: Id848e3ba7e757801fc3f93c63826d8078a755f2b
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@350590 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index faf423d..b8d6f92 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -223,9 +223,11 @@
   atomic_store(&stack_switching_, false, memory_order_release);
   CHECK_EQ(this->stack_size(), 0U);
   SetThreadStackAndTls(options);
-  CHECK_GT(this->stack_size(), 0U);
-  CHECK(AddrIsInMem(stack_bottom_));
-  CHECK(AddrIsInMem(stack_top_ - 1));
+  if (stack_top_ != stack_bottom_) {
+    CHECK_GT(this->stack_size(), 0U);
+    CHECK(AddrIsInMem(stack_bottom_));
+    CHECK(AddrIsInMem(stack_top_ - 1));
+  }
   ClearShadowForThreadStackAndTLS();
   fake_stack_ = nullptr;
   if (__asan_option_detect_stack_use_after_return)
@@ -295,14 +297,17 @@
   tls_end_ = tls_begin_ + tls_size;
   dtls_ = DTLS_Get();
 
-  int local;
-  CHECK(AddrIsInStack((uptr)&local));
+  if (stack_top_ != stack_bottom_) {
+    int local;
+    CHECK(AddrIsInStack((uptr)&local));
+  }
 }
 
 #endif  // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
 
 void AsanThread::ClearShadowForThreadStackAndTLS() {
-  PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
+  if (stack_top_ != stack_bottom_)
+    PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
   if (tls_begin_ != tls_end_) {
     uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY);
     uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY);
@@ -314,6 +319,9 @@
 
 bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
                                            StackFrameAccess *access) {
+  if (stack_top_ == stack_bottom_)
+    return false;
+
   uptr bottom = 0;
   if (AddrIsInStack(addr)) {
     bottom = stack_bottom();
diff --git a/lib/hwasan/hwasan_thread.cc b/lib/hwasan/hwasan_thread.cc
index 0d15c7e..631c281 100644
--- a/lib/hwasan/hwasan_thread.cc
+++ b/lib/hwasan/hwasan_thread.cc
@@ -43,27 +43,18 @@
   // ScopedTaggingDisable needs GetCurrentThread to be set up.
   ScopedTaggingDisabler disabler;
 
-  // If this process is "init" (pid 1), /proc may not be mounted yet.
-  if (IsMainThread() && !FileExists("/proc/self/maps")) {
-    stack_top_ = stack_bottom_ = 0;
-    tls_begin_ = tls_end_ = 0;
-  } else {
-    uptr tls_size;
-    uptr stack_size;
-    GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size,
-                         &tls_begin_, &tls_size);
-    stack_top_ = stack_bottom_ + stack_size;
-    tls_end_ = tls_begin_ + tls_size;
+  uptr tls_size;
+  uptr stack_size;
+  GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
+                       &tls_size);
+  stack_top_ = stack_bottom_ + stack_size;
+  tls_end_ = tls_begin_ + tls_size;
 
+  if (stack_bottom_) {
     int local;
     CHECK(AddrIsInStack((uptr)&local));
     CHECK(MemIsApp(stack_bottom_));
     CHECK(MemIsApp(stack_top_ - 1));
-
-    if (stack_bottom_) {
-      CHECK(MemIsApp(stack_bottom_));
-      CHECK(MemIsApp(stack_top_ - 1));
-    }
   }
 
   if (flags()->verbose_threads) {
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
index 8e0d724..b047741 100644
--- a/lib/sanitizer_common/sanitizer_flags.inc
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -243,3 +243,6 @@
 COMMON_FLAG(bool, detect_write_exec, false,
           "If true, triggers warning when writable-executable pages requests "
           "are being made")
+COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
+            "TEST ONLY fail to read memory mappings to emulate sanitized "
+            "\"init\"")
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index ecc5bac..d26925f 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -453,6 +453,8 @@
 
 // ----------------- sanitizer_common.h
 bool FileExists(const char *filename) {
+  if (ShouldMockFailureToOpen(filename))
+    return false;
   struct stat st;
 #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
   if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
@@ -1008,6 +1010,8 @@
   // Firstly check if there are writable segments
   // mapped to top gigabyte (e.g. stack).
   MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  if (proc_maps.Error())
+    return 0;
   MemoryMappedSegment segment;
   while (proc_maps.Next(&segment)) {
     if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 84da23e..6ce47ec 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -100,6 +100,10 @@
 
     // Find the mapping that contains a stack variable.
     MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+    if (proc_maps.Error()) {
+      *stack_top = *stack_bottom = 0;
+      return;
+    }
     MemoryMappedSegment segment;
     uptr prev_end = 0;
     while (proc_maps.Next(&segment)) {
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index e738ff1..0828849 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -282,6 +282,8 @@
 
 // ----------------- sanitizer_common.h
 bool FileExists(const char *filename) {
+  if (ShouldMockFailureToOpen(filename))
+    return false;
   struct stat st;
   if (stat(filename, &st))
     return false;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index c92986c..940c199 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -18,6 +18,7 @@
 
 #include "sanitizer_common.h"
 #include "sanitizer_file.h"
+#include "sanitizer_flags.h"
 #include "sanitizer_libc.h"
 #include "sanitizer_posix.h"
 #include "sanitizer_procmaps.h"
@@ -157,6 +158,8 @@
 #endif
 
 fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+  if (ShouldMockFailureToOpen(filename))
+    return kInvalidFd;
   int flags;
   switch (mode) {
     case RdOnly: flags = O_RDONLY; break;
@@ -230,6 +233,8 @@
 // memory).
 bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
   MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  if (proc_maps.Error())
+    return true; // and hope for the best
   MemoryMappedSegment segment;
   while (proc_maps.Next(&segment)) {
     if (segment.start == segment.end) continue;  // Empty range.
@@ -333,6 +338,11 @@
   return fd;
 }
 
+bool ShouldMockFailureToOpen(const char *path) {
+  return common_flags()->test_only_emulate_no_memorymap &&
+         internal_strncmp(path, "/proc/", 6) == 0;
+}
+
 } // namespace __sanitizer
 
 #endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h
index 37f27d5..04a7644 100644
--- a/lib/sanitizer_common/sanitizer_posix.h
+++ b/lib/sanitizer_common/sanitizer_posix.h
@@ -103,6 +103,8 @@
 // Move the fd out of {0, 1, 2} range.
 fd_t ReserveStandardFds(fd_t fd);
 
+bool ShouldMockFailureToOpen(const char *path);
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_POSIX_H
diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h
index 9fde040..acb7104 100644
--- a/lib/sanitizer_common/sanitizer_procmaps.h
+++ b/lib/sanitizer_common/sanitizer_procmaps.h
@@ -70,6 +70,7 @@
   explicit MemoryMappingLayout(bool cache_enabled);
   ~MemoryMappingLayout();
   bool Next(MemoryMappedSegment *segment);
+  bool Error() const;
   void Reset();
   // In some cases, e.g. when running under a sandbox on Linux, ASan is unable
   // to obtain the memory mappings. It should fall back to pre-cached data
diff --git a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
index 4cebd98..362a424 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
@@ -99,6 +99,7 @@
 }
 
 bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+  CHECK(!Error()); // can not fail
   char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
   if (data_.current >= last)
     return false;
diff --git a/lib/sanitizer_common/sanitizer_procmaps_common.cc b/lib/sanitizer_common/sanitizer_procmaps_common.cc
index 1f2b431..17d61b6 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_common.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_common.cc
@@ -80,12 +80,14 @@
   ReadProcMaps(&data_.proc_self_maps);
   if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)
     LoadFromCache();
-  CHECK_GT(data_.proc_self_maps.mmaped_size, 0);
-  CHECK_GT(data_.proc_self_maps.len, 0);
 
   Reset();
 }
 
+bool MemoryMappingLayout::Error() const {
+  return data_.current == nullptr;
+}
+
 MemoryMappingLayout::~MemoryMappingLayout() {
   // Only unmap the buffer if it is different from the cached one. Otherwise
   // it will be unmapped when the cache is refreshed.
diff --git a/lib/sanitizer_common/sanitizer_procmaps_linux.cc b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
index 633e939..cf9cb25 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_linux.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
@@ -31,6 +31,7 @@
 }
 
 bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+  if (Error()) return false; // simulate empty maps
   char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
   if (data_.current >= last) return false;
   char *next_line =
diff --git a/lib/sanitizer_common/sanitizer_procmaps_solaris.cc b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
index 9e5e37e..49bb46c 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
@@ -21,11 +21,16 @@
 namespace __sanitizer {
 
 void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
-  ReadFileToBuffer("/proc/self/xmap", &proc_maps->data, &proc_maps->mmaped_size,
-                   &proc_maps->len);
+  if (!ReadFileToBuffer("/proc/self/xmap", &proc_maps->data,
+                        &proc_maps->mmaped_size, &proc_maps->len)) {
+    proc_maps->data = nullptr;
+    proc_maps->mmaped_size = 0;
+    proc_maps->len = 0;
+  }
 }
 
 bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+  if (Error()) return false; // simulate empty maps
   char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
   if (data_.current >= last) return false;
 
diff --git a/test/asan/TestCases/Posix/no-fd.cc b/test/asan/TestCases/Posix/no-fd.cc
index 1c94545..b0441e9 100644
--- a/test/asan/TestCases/Posix/no-fd.cc
+++ b/test/asan/TestCases/Posix/no-fd.cc
@@ -9,6 +9,10 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+extern "C" const char *__asan_default_options() {
+  return "test_only_emulate_no_memorymap=1";
+}
+
 void parent(int argc, char **argv) {
   fprintf(stderr, "hello\n");
   // CHECK: hello