[compiler-rt] [scudo] Mitigate commitbase exploit (PR #75295)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 13 00:05:16 PST 2023


https://github.com/SIELA1915 updated https://github.com/llvm/llvm-project/pull/75295

>From 1407cd477784b805d2e25368215d3780ae8ca4e1 Mon Sep 17 00:00:00 2001
From: Elias Boschung <siela1915 at gmail.com>
Date: Wed, 13 Dec 2023 08:13:59 +0100
Subject: [PATCH] [scudo] verify free'd secondary was allocated before

---
 .../lib/scudo/standalone/allocator_config.h   |   8 +
 compiler-rt/lib/scudo/standalone/secondary.h  | 147 ++++++++++++++++++
 .../scudo/standalone/tests/secondary_test.cpp |   4 +
 3 files changed, 159 insertions(+)

diff --git a/compiler-rt/lib/scudo/standalone/allocator_config.h b/compiler-rt/lib/scudo/standalone/allocator_config.h
index 3c6aa3acb0e45..02d0b4e0e5d7b 100644
--- a/compiler-rt/lib/scudo/standalone/allocator_config.h
+++ b/compiler-rt/lib/scudo/standalone/allocator_config.h
@@ -153,6 +153,8 @@ struct DefaultConfig {
       static const s32 MinReleaseToOsIntervalMs = INT32_MIN;
       static const s32 MaxReleaseToOsIntervalMs = INT32_MAX;
     };
+    static const bool VerifyInUseAddresses = true;
+    static const u32 InUseBlocksSize = 1000U;
     template <typename Config> using CacheT = MapAllocatorCache<Config>;
   };
 
@@ -198,6 +200,8 @@ struct AndroidConfig {
       static const s32 MinReleaseToOsIntervalMs = 0;
       static const s32 MaxReleaseToOsIntervalMs = 1000;
     };
+    static const bool VerifyInUseAddresses = true;
+    static const u32 InUseBlocksSize = 1000U;
     template <typename Config> using CacheT = MapAllocatorCache<Config>;
   };
 
@@ -230,6 +234,8 @@ struct FuchsiaConfig {
   template <typename Config> using PrimaryT = SizeClassAllocator64<Config>;
 
   struct Secondary {
+    static const bool VerifyInUseAddresses = true;
+    static const u32 InUseBlocksSize = 1000U;
     template <typename Config> using CacheT = MapAllocatorNoCache<Config>;
   };
   template <typename Config> using SecondaryT = MapAllocator<Config>;
@@ -254,6 +260,8 @@ struct TrustyConfig {
   template <typename Config> using PrimaryT = SizeClassAllocator64<Config>;
 
   struct Secondary {
+    static const bool VerifyInUseAddresses = true;
+    static const u32 InUseBlocksSize = 1000U;
     template <typename Config> using CacheT = MapAllocatorNoCache<Config>;
   };
 
diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h
index f52a4188bcf3a..d7173aebbf049 100644
--- a/compiler-rt/lib/scudo/standalone/secondary.h
+++ b/compiler-rt/lib/scudo/standalone/secondary.h
@@ -11,6 +11,7 @@
 
 #include "chunk.h"
 #include "common.h"
+#include "internal_defs.h"
 #include "list.h"
 #include "mem_map.h"
 #include "memtag.h"
@@ -476,6 +477,23 @@ template <typename Config> class MapAllocator {
     DCHECK_EQ(FreedBytes, 0U);
     Cache.init(ReleaseToOsInterval);
     Stats.init();
+
+    if (Config::Secondary::VerifyInUseAddresses) {
+      ReservedMemoryT InUseReserved;
+      InUseReserved.create(
+          0U, sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1),
+          "scudo:secondary_integrity");
+      DCHECK_NE(InUseReserved.getBase(), 0U);
+
+      InUseAddresses = InUseReserved.dispatch(
+          InUseReserved.getBase(),
+          sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1));
+      CHECK(InUseAddresses.isAllocated());
+      InUseAddresses.setMemoryPermission(
+          InUseAddresses.getBase(),
+          sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1), 0);
+    }
+
     if (LIKELY(S))
       S->link(&Stats);
   }
@@ -544,6 +562,8 @@ template <typename Config> class MapAllocator {
   u32 NumberOfAllocs GUARDED_BY(Mutex) = 0;
   u32 NumberOfFrees GUARDED_BY(Mutex) = 0;
   LocalStats Stats GUARDED_BY(Mutex);
+
+  MemMapT InUseAddresses GUARDED_BY(Mutex) = {};
 };
 
 // As with the Primary, the size passed to this function includes any desired
@@ -588,6 +608,54 @@ void *MapAllocator<Config>::allocate(const Options &Options, uptr Size,
                BlockEnd - PtrInt);
       {
         ScopedLock L(Mutex);
+
+        if (Config::Secondary::VerifyInUseAddresses) {
+          void **IntegrityList =
+              reinterpret_cast<void **>(InUseAddresses.getBase());
+          bool isFull = true;
+          bool reachedEnd = false;
+
+          while (!reachedEnd) {
+            for (u32 I = 0; I < Config::Secondary::InUseBlocksSize; I++) {
+              if (IntegrityList[I] == nullptr) {
+                isFull = false;
+                IntegrityList[I] = Ptr;
+                break;
+              }
+            }
+            if (isFull &&
+                IntegrityList[Config::Secondary::InUseBlocksSize] != nullptr) {
+              IntegrityList = static_cast<void **>(
+                  IntegrityList[Config::Secondary::InUseBlocksSize]);
+            } else {
+              reachedEnd = true;
+            }
+          }
+
+          if (isFull) {
+            ReservedMemoryT InUseReserved;
+            InUseReserved.create(
+                0U, sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1),
+                "scudo:secondary_integrity");
+            DCHECK_NE(InUseReserved.getBase(), 0U);
+
+            MemMapT NewAddresses = InUseReserved.dispatch(
+                InUseReserved.getBase(),
+                sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1));
+            CHECK(NewAddresses.isAllocated());
+            NewAddresses.setMemoryPermission(
+                NewAddresses.getBase(),
+                sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1), 0);
+
+            IntegrityList[Config::Secondary::InUseBlocksSize] =
+                reinterpret_cast<void *>(NewAddresses.getBase());
+
+            IntegrityList = static_cast<void **>(
+                IntegrityList[Config::Secondary::InUseBlocksSize]);
+            IntegrityList[0] = Ptr;
+          }
+        }
+
         InUseBlocks.push_back(H);
         AllocatedBytes += H->CommitSize;
         FragmentedBytes += H->MemMap.getCapacity() - H->CommitSize;
@@ -662,6 +730,56 @@ void *MapAllocator<Config>::allocate(const Options &Options, uptr Size,
     *BlockEndPtr = CommitBase + CommitSize;
   {
     ScopedLock L(Mutex);
+
+    if (Config::Secondary::VerifyInUseAddresses) {
+      void **IntegrityList =
+          reinterpret_cast<void **>(InUseAddresses.getBase());
+      bool isFull = true;
+      bool reachedEnd = false;
+
+      while (!reachedEnd) {
+        for (u32 I = 0; I < Config::Secondary::InUseBlocksSize; I++) {
+          if (IntegrityList[I] == nullptr) {
+            isFull = false;
+            IntegrityList[I] = reinterpret_cast<void *>(
+                HeaderPos + LargeBlock::getHeaderSize());
+            break;
+          }
+        }
+        if (isFull &&
+            IntegrityList[Config::Secondary::InUseBlocksSize] != nullptr) {
+          IntegrityList = static_cast<void **>(
+              IntegrityList[Config::Secondary::InUseBlocksSize]);
+        } else {
+          reachedEnd = true;
+        }
+      }
+
+      if (isFull) {
+        ReservedMemoryT InUseReserved;
+        InUseReserved.create(
+            0U, sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1),
+            "scudo:secondary_integrity");
+        DCHECK_NE(InUseReserved.getBase(), 0U);
+
+        MemMapT NewAddresses = InUseReserved.dispatch(
+            InUseReserved.getBase(),
+            sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1));
+        CHECK(NewAddresses.isAllocated());
+        NewAddresses.setMemoryPermission(
+            NewAddresses.getBase(),
+            sizeof(void *) * (Config::Secondary::InUseBlocksSize + 1), 0);
+
+        IntegrityList[Config::Secondary::InUseBlocksSize] =
+            reinterpret_cast<void *>(NewAddresses.getBase());
+
+        IntegrityList = static_cast<void **>(
+            IntegrityList[Config::Secondary::InUseBlocksSize]);
+        IntegrityList[0] =
+            reinterpret_cast<void *>(HeaderPos + LargeBlock::getHeaderSize());
+      }
+    }
+
     InUseBlocks.push_back(H);
     AllocatedBytes += CommitSize;
     FragmentedBytes += H->MemMap.getCapacity() - CommitSize;
@@ -681,6 +799,35 @@ void MapAllocator<Config>::deallocate(const Options &Options, void *Ptr)
   const uptr CommitSize = H->CommitSize;
   {
     ScopedLock L(Mutex);
+
+    if (Config::Secondary::VerifyInUseAddresses) {
+      void **IntegrityList =
+          reinterpret_cast<void **>(InUseAddresses.getBase());
+      bool isValid = false;
+      bool reachedEnd = false;
+
+      while (!reachedEnd) {
+        for (u32 I = 0; I < Config::Secondary::InUseBlocksSize; I++) {
+          if (IntegrityList[I] == Ptr) {
+            isValid = true;
+            IntegrityList[I] = nullptr;
+            break;
+          }
+        }
+        if (!isValid &&
+            IntegrityList[Config::Secondary::InUseBlocksSize] != nullptr) {
+          IntegrityList = static_cast<void **>(
+              IntegrityList[Config::Secondary::InUseBlocksSize]);
+        } else {
+          reachedEnd = true;
+        }
+      }
+
+      if (!isValid) {
+        return;
+      }
+    }
+
     InUseBlocks.remove(H);
     FreedBytes += CommitSize;
     FragmentedBytes -= H->MemMap.getCapacity() - CommitSize;
diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
index 18d2e187fa3ce..a5485b0e11770 100644
--- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
@@ -85,6 +85,8 @@ template <typename Config> static void testSecondaryBasic(void) {
 struct NoCacheConfig {
   static const bool MaySupportMemoryTagging = false;
   struct Secondary {
+    static const bool VerifyInUseAddresses = true;
+    static const scudo::u32 InUseBlocksSize = 1000U;
     template <typename Config>
     using CacheT = scudo::MapAllocatorNoCache<Config>;
   };
@@ -102,6 +104,8 @@ struct TestConfig {
       static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
     };
 
+    static const bool VerifyInUseAddresses = true;
+    static const scudo::u32 InUseBlocksSize = 1000U;
     template <typename Config> using CacheT = scudo::MapAllocatorCache<Config>;
   };
 };



More information about the llvm-commits mailing list