[scudo] Check if MADV_DONTNEED zeroes memory
QEMU just ignores MADV_DONTNEED
https://github.com/qemu/qemu/blob/b1cffefa1b163bce9aebc3416f562c1d3886eeaa/linux-user/syscall.c#L11941
Depends on D100998.
Differential Revision: https://reviews.llvm.org/D101031
GitOrigin-RevId: d423509b8036c29bbf94dab192d12097555ce0f8
diff --git a/linux.cpp b/linux.cpp
index c2fdf21..9b5ed67 100644
--- a/linux.cpp
+++ b/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) {
}
}