diff --git a/compiler-rt/lib/scudo/standalone/linux.cpp b/compiler-rt/lib/scudo/standalone/linux.cpp --- a/compiler-rt/lib/scudo/standalone/linux.cpp +++ b/compiler-rt/lib/scudo/standalone/linux.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,10 @@ #include #include +#ifndef PROT_MTE +#define PROT_MTE 0x20 +#endif + #if SCUDO_ANDROID #include // Definitions of prctl arguments to set a vma name in Android kernels. @@ -53,9 +58,6 @@ MmapProt = PROT_READ | PROT_WRITE; } #if defined(__aarch64__) -#ifndef PROT_MTE -#define PROT_MTE 0x20 -#endif if (Flags & MAP_MEMTAG) MmapProt |= PROT_MTE; #endif @@ -116,6 +118,55 @@ return NeedsMemset == Yes; } +static uptr getProt(FILE *F, uptr Ptr, int *Prot) { + CHECK(F); + char line[1024]; + u64 B, E; + do { + CHECK_NE(fgets(line, sizeof(line), F), nullptr); + } while (sscanf(line, "%llx-%llx ", &B, &E) != 2 || Ptr < B || Ptr >= E); + + CHECK_LE(B, Ptr); + CHECK_LT(Ptr, E); + + static const char Field[] = "VmFlags:"; + do { + CHECK_NE(fgets(line, sizeof(line), F), nullptr); + } while (strncmp(line, Field, sizeof(Field) - 1) != 0); + + *Prot = 0; + char *Saved = nullptr; + char *Token = strtok_r(line + sizeof(Field) - 1, " \n", &Saved); + for (; Token; Token = strtok_r(nullptr, " \n", &Saved)) { + if (strcmp(Token, "rd") == 0) + *Prot |= PROT_READ; + else if (strcmp(Token, "wr") == 0) + *Prot |= PROT_WRITE; + else if (strcmp(Token, "mt") == 0) + *Prot |= PROT_MTE; + } + return static_cast(E); +} + +static void releaseByReMap(void *Addr, uptr Size) { + FILE *F = fopen("/proc/self/smaps", "r"); + while (Size) { + uptr B = reinterpret_cast(Addr); + int Prot; + // FIXME: Tests do not catch if we just set PROT_READ | PROT_WRITE. + uptr E = getProt(F, B, &Prot); + uptr MapSize = Min(E - B, Size); + if (Addr != mmap(Addr, MapSize, Prot, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, + -1, 0)) { + dieOnMapUnmapError(); + } + Addr = reinterpret_cast(B + MapSize); + Size -= MapSize; + } + fclose(F); +} + void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size, UNUSED MapPlatformData *Data) { void *Addr = reinterpret_cast(BaseAddress + Offset); @@ -123,7 +174,7 @@ // Workaround for QEMU-user ignoring MADV_DONTNEED. // https://github.com/qemu/qemu/blob/b1cffefa1b163bce9aebc3416f562c1d3886eeaa/linux-user/syscall.c#L11941 // https://bugs.launchpad.net/qemu/+bug/1926521 - memset(Addr, 0, Size); + return releaseByReMap(Addr, Size); } while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) { } diff --git a/compiler-rt/lib/scudo/standalone/tests/common_test.cpp b/compiler-rt/lib/scudo/standalone/tests/common_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/common_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/common_test.cpp @@ -47,9 +47,8 @@ EXPECT_GT(getResidentMemorySize() - OnStart, Size - Threshold); releasePagesToOS((uptr)P, 0, Size, &Data); + EXPECT_LT(getResidentMemorySize() - OnStart, Threshold); EXPECT_EQ(std::count(P, P + N, 0), N); - // FIXME: does not work with QEMU-user. - // EXPECT_LT(getResidentMemorySize() - OnStart, Threshold); memset(P, 1, Size); EXPECT_EQ(std::count(P, P + N, 0), 0);