[compiler-rt] [scudo] Use setenv instead of putenv in ScudoCombinedTest.ZeroOnDeallocEnabledAndFlag (PR #173423)

Fabio D'Urso via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 29 07:53:23 PST 2025


https://github.com/fabio-d updated https://github.com/llvm/llvm-project/pull/173423

>From f95336507d70e6bc8d0ef4bb8274f6add7a5e7de Mon Sep 17 00:00:00 2001
From: Fabio D'Urso <fdurso at google.com>
Date: Tue, 23 Dec 2025 22:35:21 +0000
Subject: [PATCH 1/2] [scudo] Use setenv instead of putenv in
 ScudoCombinedTest.ZeroOnDeallocEnabledAndFlag

This solves a stack-use-after-scope reported by AddressSanitizer
within the unsetenv call at end of the test, due to the "Options"
buffer, that we allocate on the stack, having already gone out of
scope.

Unlike putenv, which stores the pointer to the passed string
directly in the environment, setenv creates an internal copy.
---
 compiler-rt/lib/scudo/standalone/tests/combined_test.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index e8587ee397e2a..8b62c0e104d6b 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -1122,10 +1122,9 @@ TEST(ScudoCombinedTest, ZeroOnDeallocEnabledAndFlag) {
     for (scudo::uptr FlagValue = 128; FlagValue <= 2048; FlagValue *= 2) {
       // Set the size limit flag via the environment variable.
       char Options[256];
-      snprintf(Options, sizeof(Options),
-               "SCUDO_OPTIONS=zero_on_dealloc_max_size=%ld",
+      snprintf(Options, sizeof(Options), "zero_on_dealloc_max_size=%ld",
                static_cast<long>(FlagValue));
-      putenv(Options);
+      setenv("SCUDO_OPTIONS", Options, 1);
 
       // Creates an allocator, configured from the environment.
       using AllocatorT = TestAllocator<TestZeroOnDeallocConfig>;

>From bcf87003bcca0ca74c7a8a5cde12e0a5ce63ac5d Mon Sep 17 00:00:00 2001
From: Fabio D'Urso <fdurso at google.com>
Date: Mon, 29 Dec 2025 15:46:34 +0000
Subject: [PATCH 2/2] ScopedScudoOptions RAII wrapper

---
 .../scudo/standalone/tests/combined_test.cpp  | 69 ++++++++++---------
 1 file changed, 37 insertions(+), 32 deletions(-)

diff --git a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 8b62c0e104d6b..01fadcf3425d6 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -152,6 +152,15 @@ void TestAllocator<Config>::operator delete(void *ptr) {
   TestAllocatorStorage::release(ptr);
 }
 
+class ScopedScudoOptions {
+public:
+  explicit ScopedScudoOptions(const char *Options) {
+    setenv("SCUDO_OPTIONS", Options, 1);
+  }
+
+  ~ScopedScudoOptions() { unsetenv("SCUDO_OPTIONS"); }
+};
+
 template <class TypeParam> struct ScudoCombinedTest : public Test {
   ScudoCombinedTest() { Allocator = std::make_unique<AllocatorT>(); }
   ~ScudoCombinedTest() { Allocator->releaseToOS(scudo::ReleaseToOS::Force); }
@@ -1118,41 +1127,37 @@ struct TestNoZeroOnDeallocConfig : public TestZeroOnDeallocConfig {
 };
 
 TEST(ScudoCombinedTest, ZeroOnDeallocEnabledAndFlag) {
-  ([]() { // Cleanup on exit scope.
-    for (scudo::uptr FlagValue = 128; FlagValue <= 2048; FlagValue *= 2) {
-      // Set the size limit flag via the environment variable.
-      char Options[256];
-      snprintf(Options, sizeof(Options), "zero_on_dealloc_max_size=%ld",
-               static_cast<long>(FlagValue));
-      setenv("SCUDO_OPTIONS", Options, 1);
-
-      // Creates an allocator, configured from the environment.
-      using AllocatorT = TestAllocator<TestZeroOnDeallocConfig>;
-      auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
-
-      for (scudo::uptr AllocatedSize : {FlagValue / 2, FlagValue}) {
-        // Allocates and sets the memory.
-        void *P = Allocator->allocate(AllocatedSize, Origin);
-        ASSERT_NE(P, nullptr);
-        memset(P, 'B', AllocatedSize);
-
-        char *Begin =
-            reinterpret_cast<char *>(Allocator->getBlockBeginTestOnly(P));
-        char *End = reinterpret_cast<char *>(P) + AllocatedSize;
-        // Deallocates and eventually clears the memory.
-        Allocator->deallocate(P, Origin);
-
-        if (End - Begin <= static_cast<long>(FlagValue)) {
-          // Verifies the memory was cleared, including the header.
-          for (char *T = Begin; T < End; ++T) {
-            ASSERT_EQ(*T, 0);
-          }
+  for (scudo::uptr FlagValue = 128; FlagValue <= 2048; FlagValue *= 2) {
+    // Set the size limit flag via the environment variable.
+    char OptionsStr[256];
+    snprintf(OptionsStr, sizeof(OptionsStr), "zero_on_dealloc_max_size=%ld",
+             static_cast<long>(FlagValue));
+    ScopedScudoOptions Options(OptionsStr);
+
+    // Creates an allocator, configured from the environment.
+    using AllocatorT = TestAllocator<TestZeroOnDeallocConfig>;
+    auto Allocator = std::unique_ptr<AllocatorT>(new AllocatorT());
+
+    for (scudo::uptr AllocatedSize : {FlagValue / 2, FlagValue}) {
+      // Allocates and sets the memory.
+      void *P = Allocator->allocate(AllocatedSize, Origin);
+      ASSERT_NE(P, nullptr);
+      memset(P, 'B', AllocatedSize);
+
+      char *Begin =
+          reinterpret_cast<char *>(Allocator->getBlockBeginTestOnly(P));
+      char *End = reinterpret_cast<char *>(P) + AllocatedSize;
+      // Deallocates and eventually clears the memory.
+      Allocator->deallocate(P, Origin);
+
+      if (End - Begin <= static_cast<long>(FlagValue)) {
+        // Verifies the memory was cleared, including the header.
+        for (char *T = Begin; T < End; ++T) {
+          ASSERT_EQ(*T, 0);
         }
       }
     }
-  })();
-  // Clear the scudo flag option.
-  unsetenv("SCUDO_OPTIONS");
+  }
 }
 #endif
 



More information about the llvm-commits mailing list