From 46c26486fb1186b23aeee64933269ac862b2c70a Mon Sep 17 00:00:00 2001 From: Justin King Date: Fri, 20 Jun 2025 10:54:30 -0700 Subject: [PATCH] rtsan: Support free_sized and free_aligned_sized from C23 Signed-off-by: Justin King --- .../lib/rtsan/rtsan_interceptors_posix.cpp | 44 +++++++++++++++ .../tests/rtsan_test_interceptors_posix.cpp | 53 ++++++++++++++++++- .../TestCases/Linux/free_aligned_sized.c | 4 +- .../TestCases/Linux/free_sized.c | 4 +- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp index 9f9ffd4313810..a9d864e9fe926 100644 --- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp @@ -869,6 +869,48 @@ INTERCEPTOR(void, free, void *ptr) { return REAL(free)(ptr); } +#if SANITIZER_INTERCEPT_FREE_SIZED +INTERCEPTOR(void, free_sized, void *ptr, SIZE_T size) { + if (DlsymAlloc::PointerIsMine(ptr)) + return DlsymAlloc::Free(ptr); + + // According to the C and C++ standard, freeing a nullptr is guaranteed to be + // a no-op (and thus real-time safe). This can be confirmed for looking at + // __libc_free in the glibc source. + if (ptr != nullptr) + __rtsan_notify_intercepted_call("free_sized"); + + if (REAL(free_sized)) + return REAL(free_sized)(ptr, size); + return REAL(free)(ptr); +} +#define RTSAN_MAYBE_INTERCEPT_FREE_SIZED INTERCEPT_FUNCTION(free_sized) +#else +#define RTSAN_MAYBE_INTERCEPT_FREE_SIZED +#endif + +#if SANITIZER_INTERCEPT_FREE_ALIGNED_SIZED +INTERCEPTOR(void, free_aligned_sized, void *ptr, SIZE_T alignment, + SIZE_T size) { + if (DlsymAlloc::PointerIsMine(ptr)) + return DlsymAlloc::Free(ptr); + + // According to the C and C++ standard, freeing a nullptr is guaranteed to be + // a no-op (and thus real-time safe). This can be confirmed for looking at + // __libc_free in the glibc source. + if (ptr != nullptr) + __rtsan_notify_intercepted_call("free_aligned_sized"); + + if (REAL(free_aligned_sized)) + return REAL(free_aligned_sized)(ptr, alignment, size); + return REAL(free)(ptr); +} +#define RTSAN_MAYBE_INTERCEPT_FREE_ALIGNED_SIZED \ + INTERCEPT_FUNCTION(free_aligned_sized) +#else +#define RTSAN_MAYBE_INTERCEPT_FREE_ALIGNED_SIZED +#endif + INTERCEPTOR(void *, malloc, SIZE_T size) { if (DlsymAlloc::Use()) return DlsymAlloc::Allocate(size); @@ -1493,6 +1535,8 @@ INTERCEPTOR(INT_TYPE_SYSCALL, syscall, INT_TYPE_SYSCALL number, ...) { void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(free); + RTSAN_MAYBE_INTERCEPT_FREE_SIZED; + RTSAN_MAYBE_INTERCEPT_FREE_ALIGNED_SIZED; INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(reallocf); diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp index 2ee35555c24de..26a3b252d3b6b 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp @@ -63,6 +63,14 @@ #define MAYBE_APPEND_64(func) func #endif +#if SANITIZER_INTERCEPT_FREE_SIZED +extern "C" void free_sized(void *ptr, size_t size); +#endif + +#if SANITIZER_INTERCEPT_FREE_ALIGNED_SIZED +extern "C" void free_aligned_sized(void *ptr, size_t alignment, size_t size); +#endif + using namespace testing; using namespace rtsan_testing; using namespace std::chrono_literals; @@ -148,7 +156,6 @@ TEST(TestRtsanInterceptors, AlignedAllocDiesWhenRealtime) { } } -// free_sized and free_aligned_sized (both C23) are not yet supported TEST(TestRtsanInterceptors, FreeDiesWhenRealtime) { void *ptr_1 = malloc(1); void *ptr_2 = malloc(1); @@ -160,11 +167,55 @@ TEST(TestRtsanInterceptors, FreeDiesWhenRealtime) { ASSERT_NE(nullptr, ptr_2); } +#if SANITIZER_INTERCEPT_FREE_SIZED +TEST(TestRtsanInterceptors, FreeSizedDiesWhenRealtime) { + void *ptr_1 = malloc(1); + void *ptr_2 = malloc(1); + ExpectRealtimeDeath([ptr_1]() { free_sized(ptr_1, 1); }, "free_sized"); + ExpectNonRealtimeSurvival([ptr_2]() { free_sized(ptr_2, 1); }); + + // Prevent malloc/free pair being optimised out + ASSERT_NE(nullptr, ptr_1); + ASSERT_NE(nullptr, ptr_2); +} +#endif + +#if SANITIZER_INTERCEPT_FREE_ALIGNED_SIZED +TEST(TestRtsanInterceptors, FreeAlignedSizedDiesWhenRealtime) { + if (ALIGNED_ALLOC_AVAILABLE()) { + void *ptr_1 = aligned_alloc(16, 32); + void *ptr_2 = aligned_alloc(16, 32); + ExpectRealtimeDeath([ptr_1]() { free_aligned_sized(ptr_1, 16, 32); }, + "free_aligned_sized"); + ExpectNonRealtimeSurvival([ptr_2]() { free_aligned_sized(ptr_2, 16, 32); }); + + // Prevent malloc/free pair being optimised out + ASSERT_NE(nullptr, ptr_1); + ASSERT_NE(nullptr, ptr_2); + } +} +#endif + TEST(TestRtsanInterceptors, FreeSurvivesWhenRealtimeIfArgumentIsNull) { RealtimeInvoke([]() { free(NULL); }); ExpectNonRealtimeSurvival([]() { free(NULL); }); } +#if SANITIZER_INTERCEPT_FREE_SIZED +TEST(TestRtsanInterceptors, FreeSizedSurvivesWhenRealtimeIfArgumentIsNull) { + RealtimeInvoke([]() { free_sized(NULL, 0); }); + ExpectNonRealtimeSurvival([]() { free_sized(NULL, 0); }); +} +#endif + +#if SANITIZER_INTERCEPT_FREE_ALIGNED_SIZED +TEST(TestRtsanInterceptors, + FreeAlignedSizedSurvivesWhenRealtimeIfArgumentIsNull) { + RealtimeInvoke([]() { free_aligned_sized(NULL, 0, 0); }); + ExpectNonRealtimeSurvival([]() { free_aligned_sized(NULL, 0, 0); }); +} +#endif + TEST(TestRtsanInterceptors, PosixMemalignDiesWhenRealtime) { auto Func = []() { void *ptr; diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/free_aligned_sized.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/free_aligned_sized.c index dc11e7fa5e9b3..15b1a43f596e8 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/free_aligned_sized.c +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/free_aligned_sized.c @@ -1,9 +1,11 @@ // RUN: %clang -std=c23 -O0 %s -o %t && %run %t -// UNSUPPORTED: asan, hwasan, rtsan, ubsan +// UNSUPPORTED: asan, hwasan, ubsan #include #include +extern void *aligned_alloc(size_t alignment, size_t size); + extern void free_aligned_sized(void *p, size_t alignment, size_t size); int main() { diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/free_sized.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/free_sized.c index 7a993d481bc5a..d8ec1e8b364b6 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Linux/free_sized.c +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/free_sized.c @@ -1,11 +1,9 @@ // RUN: %clang -std=c23 -O0 %s -o %t && %run %t -// UNSUPPORTED: asan, hwasan, rtsan, ubsan +// UNSUPPORTED: asan, hwasan, ubsan #include #include -extern void *aligned_alloc(size_t alignment, size_t size); - extern void free_sized(void *p, size_t size); int main() {