[scudo] Check if MADV_DONTNEED zeroes memory am: 2291410e54 am: 7828a5f1a8 am: f4abe420a0

Original change: https://android-review.googlesource.com/c/platform/external/scudo/+/1683518

Change-Id: I53b960a1a1f60797065f5ab8258f20d9c6a1d584
diff --git a/standalone/linux.cpp b/standalone/linux.cpp
index c2fdf21..9b5ed67 100644
--- a/standalone/linux.cpp
+++ b/standalone/linux.cpp
@@ -10,6 +10,7 @@
 
 #if SCUDO_LINUX
 
+#include "atomic_helpers.h"
 #include "common.h"
 #include "linux.h"
 #include "mutex.h"
@@ -89,9 +90,40 @@
     dieOnMapUnmapError();
 }
 
+static bool madviseNeedsMemset() {
+  uptr Size = getPageSizeCached();
+  char *P = (char *)mmap(0, Size, PROT_READ | PROT_WRITE,
+                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (!P)
+    dieOnMapUnmapError(errno == ENOMEM);
+  *P = -1;
+  while (madvise(P, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
+  }
+  bool R = (*P != 0);
+  if (munmap(P, Size) != 0)
+    dieOnMapUnmapError();
+  return R;
+}
+
+static bool madviseNeedsMemsetCached() {
+  static atomic_u8 Cache;
+  enum State : u8 { Unknown = 0, Yes = 1, No = 2 };
+  State NeedsMemset = static_cast<State>(atomic_load_relaxed(&Cache));
+  if (NeedsMemset == Unknown) {
+    NeedsMemset = madviseNeedsMemset() ? Yes : No;
+    atomic_store_relaxed(&Cache, NeedsMemset);
+  }
+  return NeedsMemset == Yes;
+}
+
 void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
                       UNUSED MapPlatformData *Data) {
   void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
+  if (madviseNeedsMemsetCached()) {
+    // Workaround for QEMU-user ignoring MADV_DONTNEED.
+    // https://github.com/qemu/qemu/blob/b1cffefa1b163bce9aebc3416f562c1d3886eeaa/linux-user/syscall.c#L11941
+    memset(Addr, 0, Size);
+  }
   while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
   }
 }