|
10 | 10 |
|
11 | 11 | #if SCUDO_LINUX
|
12 | 12 |
|
| 13 | +#include "atomic_helpers.h" |
13 | 14 | #include "common.h"
|
14 | 15 | #include "linux.h"
|
15 | 16 | #include "mutex.h"
|
@@ -89,9 +90,40 @@ void setMemoryPermission(uptr Addr, uptr Size, uptr Flags,
|
89 | 90 | dieOnMapUnmapError();
|
90 | 91 | }
|
91 | 92 |
|
| 93 | +static bool madviseNeedsMemset() { |
| 94 | + uptr Size = getPageSizeCached(); |
| 95 | + char *P = (char *)mmap(0, Size, PROT_READ | PROT_WRITE, |
| 96 | + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
| 97 | + if (!P) |
| 98 | + dieOnMapUnmapError(errno == ENOMEM); |
| 99 | + *P = -1; |
| 100 | + while (madvise(P, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) { |
| 101 | + } |
| 102 | + bool R = (*P != 0); |
| 103 | + if (munmap(P, Size) != 0) |
| 104 | + dieOnMapUnmapError(); |
| 105 | + return R; |
| 106 | +} |
| 107 | + |
| 108 | +static bool madviseNeedsMemsetCached() { |
| 109 | + static atomic_u8 Cache; |
| 110 | + enum State : u8 { Unknown = 0, Yes = 1, No = 2 }; |
| 111 | + State NeedsMemset = static_cast<State>(atomic_load_relaxed(&Cache)); |
| 112 | + if (NeedsMemset == Unknown) { |
| 113 | + NeedsMemset = madviseNeedsMemset() ? Yes : No; |
| 114 | + atomic_store_relaxed(&Cache, NeedsMemset); |
| 115 | + } |
| 116 | + return NeedsMemset == Yes; |
| 117 | +} |
| 118 | + |
92 | 119 | void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
|
93 | 120 | UNUSED MapPlatformData *Data) {
|
94 | 121 | void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
|
| 122 | + if (madviseNeedsMemsetCached()) { |
| 123 | + // Workaround for QEMU-user ignoring MADV_DONTNEED. |
| 124 | + // https://github.com/qemu/qemu/blob/b1cffefa1b163bce9aebc3416f562c1d3886eeaa/linux-user/syscall.c#L11941 |
| 125 | + memset(Addr, 0, Size); |
| 126 | + } |
95 | 127 | while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
|
96 | 128 | }
|
97 | 129 | }
|
|
0 commit comments