[compiler-rt] [scudo] Mitigate commitbase exploit (PR #75295)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 13 00:14:45 PST 2023
https://github.com/SIELA1915 updated https://github.com/llvm/llvm-project/pull/75295
>From 1ff75e4ad549ccce28a7c0a83370827681b4405d 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/combined_test.cpp | 4 +
.../scudo/standalone/tests/secondary_test.cpp | 4 +
4 files changed, 163 insertions(+)
diff --git a/compiler-rt/lib/scudo/standalone/allocator_config.h b/compiler-rt/lib/scudo/standalone/allocator_config.h
index 3c6aa3acb0e45b..02d0b4e0e5d7ba 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 f52a4188bcf3a6..d7173aebbf0496 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/combined_test.cpp b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
index 3dbd93cacefd68..721e524b07b824 100644
--- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp
@@ -205,6 +205,8 @@ struct TestConditionVariableConfig {
#endif
struct Secondary {
+ static const bool VerifyInUseAddresses = true;
+ static const scudo::u32 InUseBlocksSize = 1000U;
template <typename Config>
using CacheT = scudo::MapAllocatorNoCache<Config>;
};
@@ -678,6 +680,8 @@ struct DeathConfig {
using PrimaryT = scudo::SizeClassAllocator64<Config>;
struct Secondary {
+ static const bool VerifyInUseAddresses = true;
+ static const scudo::u32 InUseBlocksSize = 1000U;
template <typename Config>
using CacheT = scudo::MapAllocatorNoCache<Config>;
};
diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
index 18d2e187fa3ce2..a5485b0e117707 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