Skip to content

Conversation

@cferris1000
Copy link
Contributor

If a caller has locked memory, then the madvise call will fail. In that case, zero the memory so that we don't return non-zeroed memory for calloc calls since we thought the memory had been released.

If a caller has locked memory, then the madvise call will fail. In that case, zero the memory so that we don't return non-zeroed memory for calloc calls since we thought the memory had been released.
@llvmbot
Copy link
Member

llvmbot commented Nov 12, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Christopher Ferris (cferris1000)

Changes

If a caller has locked memory, then the madvise call will fail. In that case, zero the memory so that we don't return non-zeroed memory for calloc calls since we thought the memory had been released.


Full diff: https://github.com/llvm/llvm-project/pull/167788.diff

2 Files Affected:

  • (modified) compiler-rt/lib/scudo/standalone/mem_map_linux.cpp (+6-1)
  • (modified) compiler-rt/lib/scudo/standalone/tests/map_test.cpp (+41)
diff --git a/compiler-rt/lib/scudo/standalone/mem_map_linux.cpp b/compiler-rt/lib/scudo/standalone/mem_map_linux.cpp index 783c4f0d9ab0f..df3e54cab6695 100644 --- a/compiler-rt/lib/scudo/standalone/mem_map_linux.cpp +++ b/compiler-rt/lib/scudo/standalone/mem_map_linux.cpp @@ -122,7 +122,12 @@ void MemMapLinux::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) { void MemMapLinux::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) { void *Addr = reinterpret_cast<void *>(From); - while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) { + int rc; + while ((rc = madvise(Addr, Size, MADV_DONTNEED)) == -1 && errno == EAGAIN) { + } + if (rc == -1) { + // If we can't madvies the memory, then we still need to zero it. + memset(Addr, 0, Size); } } diff --git a/compiler-rt/lib/scudo/standalone/tests/map_test.cpp b/compiler-rt/lib/scudo/standalone/tests/map_test.cpp index cc7d3ee4dc6c2..fadc0e00c7844 100644 --- a/compiler-rt/lib/scudo/standalone/tests/map_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/map_test.cpp @@ -14,6 +14,10 @@ #include <string.h> #include <unistd.h> +#if SCUDO_LINUX +#include <sys/mman.h> +#endif + static const char *MappingName = "scudo:test"; TEST(ScudoMapTest, PageSize) { @@ -89,3 +93,40 @@ TEST(ScudoMapTest, MapGrowUnmap) { memset(reinterpret_cast<void *>(Q), 0xbb, PageSize); MemMap.unmap(); } + +// Verify that zeroing works properly. +TEST(ScudoMapTest, Zeroing) { + scudo::ReservedMemoryT ReservedMemory; + const scudo::uptr PageSize = scudo::getPageSizeCached(); + const scudo::uptr Size = 3 * PageSize; + ReservedMemory.create(/*Addr=*/0U, Size, MappingName); + ASSERT_TRUE(ReservedMemory.isCreated()); + + scudo::MemMapT MemMap = ReservedMemory.dispatch(ReservedMemory.getBase(), ReservedMemory.getCapacity()); + EXPECT_TRUE(MemMap.remap(MemMap.getBase(), MemMap.getCapacity(), MappingName)); + unsigned char *Data = reinterpret_cast<unsigned char*>(MemMap.getBase()); + memset(Data, 1U, MemMap.getCapacity()); + // Spot check some values. + EXPECT_EQ(1U, Data[0]); + EXPECT_EQ(1U, Data[PageSize]); + EXPECT_EQ(1U, Data[PageSize * 2]); + MemMap.releaseAndZeroPagesToOS(MemMap.getBase(), MemMap.getCapacity()); + EXPECT_EQ(0U, Data[0]); + EXPECT_EQ(0U, Data[PageSize]); + EXPECT_EQ(0U, Data[PageSize * 2]); + +#if SCUDO_LINUX + // Now verify that if madvise fails, the data is still zeroed. + memset(Data, 1U, MemMap.getCapacity()); + EXPECT_NE(-1, mlock(Data, MemMap.getCapacity())); + EXPECT_EQ(1U, Data[0]); + EXPECT_EQ(1U, Data[PageSize]); + EXPECT_EQ(1U, Data[PageSize * 2]); + MemMap.releaseAndZeroPagesToOS(MemMap.getBase(), MemMap.getCapacity()); + EXPECT_EQ(0U, Data[0]); + EXPECT_EQ(0U, Data[PageSize]); + EXPECT_EQ(0U, Data[PageSize * 2]); +#endif + + MemMap.unmap(); +} 
@github-actions
Copy link

github-actions bot commented Nov 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Comment on lines +128 to +130
if (rc == -1) {
// If we can't madvies the memory, then we still need to zero it.
memset(Addr, 0, Size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking if we want to check something like "errno == EPERM". It seems not making any difference to the problem but maybe it helps with some other cases. I guess not...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, when I checked this the errno was EINVAL, which is not what I was expecting. It seems safer to just memset when it fails for whatever reason.

@cferris1000 cferris1000 merged commit 3e28992 into llvm:main Nov 13, 2025
10 checks passed
@cferris1000 cferris1000 deleted the zero_fix branch November 13, 2025 21:01
@mgorny
Copy link
Member

mgorny commented Nov 15, 2025

The new test is failing for me while testing on Gentoo/arm64:

FAIL: ScudoStandalone-Unit :: ./ScudoUnitTest-aarch64-Test/74/171 (4339 of 5799) ******************** TEST 'ScudoStandalone-Unit :: ./ScudoUnitTest-aarch64-Test/74/171' FAILED ******************** Script(shard): -- GTEST_OUTPUT=json:/var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test-ScudoStandalone-Unit-190-74-171.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=171 GTEST_SHARD_INDEX=74 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test -- Script: -- /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test --gtest_filter=ScudoMapTest.Zeroing -- /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123: Failure Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:133: Failure Expected: (-1) != (munlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123 Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:133 Expected: (-1) != (munlock(Data, MemMap.getCapacity())), actual: -1 vs -1 ******************** FAIL: ScudoStandalone-Unit-GwpAsanTorture :: ./ScudoUnitTest-aarch64-Test/74/171 (4530 of 5799) ******************** TEST 'ScudoStandalone-Unit-GwpAsanTorture :: ./ScudoUnitTest-aarch64-Test/74/171' FAILED ******************** Script(shard): -- GTEST_OUTPUT=json:/var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test-ScudoStandalone-Unit-GwpAsanTorture-190-74-171.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=171 GTEST_SHARD_INDEX=74 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test -- Script: -- /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt_build/lib/scudo/standalone/tests/./ScudoUnitTest-aarch64-Test --gtest_filter=ScudoMapTest.Zeroing -- /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123: Failure Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:133: Failure Expected: (-1) != (munlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123 Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /var/tmp/portage/llvm-runtimes/compiler-rt-sanitizers-22.0.0.9999/work/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:133 Expected: (-1) != (munlock(Data, MemMap.getCapacity())), actual: -1 vs -1 ******************** 

This is on top of d9dfe75. Neoverse-N1. glibc 2.41.

@madanial0
Copy link
Contributor

The new test also fails on ppc64le starting with https://lab.llvm.org/buildbot/#/builders/145/builds/10835

/home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-rhel-test/clang-ppc64le-rhel/build/runtimes/runtimes-bins/compiler-rt/lib/scudo/standalone/tests/./ScudoUnitTest-powerpc64le-Test --gtest_filter=ScudoMapTest.Zeroing -- /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-rhel-test/clang-ppc64le-rhel/llvm-project/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123: Failure Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-rhel-test/clang-ppc64le-rhel/llvm-project/compiler-rt/lib/scudo/standalone/tests/map_test.cpp:123 Expected: (-1) != (mlock(Data, MemMap.getCapacity())), actual: -1 vs -1 
@ZarkoT
Copy link
Contributor

ZarkoT commented Nov 17, 2025

@cferris1000 Do you mind having a look at the ppc64le failures, possibly reverting until you can investigate further?
https://lab.llvm.org/buildbot/#/builders/95/builds/19135

@cferris1000
Copy link
Contributor Author

This looks like mlock/munlock are not supported everywhere, so I'll fix the test right now.

@cferris1000
Copy link
Contributor Author

I believe this should pull request should fix this:

#168448

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment