Skip to content

[TySan] Intercept malloc_size on Apple platforms. #122133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 9, 2025

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Jan 8, 2025

After #120563 malloc_size also needs intercepting on Apple platforms, otherwise all type-sanitized binaries crash on startup with an objc error:
realized class 0x12345 has corrupt data pointer: malloc_size(0x567) = 0

@llvmbot
Copy link
Member

llvmbot commented Jan 8, 2025

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

Author: Florian Hahn (fhahn)

Changes

After #120563 malloc_size also needs intercepting on Apple platforms, otherwise all type-sanitized binaries crash on startup with an objc error:
realized class 0x12345 has corrupt data pointer: malloc_size(0x567) = 0


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

2 Files Affected:

  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_allocator_dlsym.h (+8-4)
  • (modified) compiler-rt/lib/tysan/tysan_interceptors.cpp (+8)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_dlsym.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_dlsym.h
index b360478a058a54..5465258e6a022d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_dlsym.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_dlsym.h
@@ -37,7 +37,7 @@ struct DlSymAllocator {
     void *ptr = InternalAlloc(size_in_bytes, nullptr, align);
     CHECK(internal_allocator()->FromPrimary(ptr));
     Details::OnAllocate(ptr,
-                        internal_allocator()->GetActuallyAllocatedSize(ptr));
+                        Size(ptr));
     return ptr;
   }
 
@@ -45,12 +45,12 @@ struct DlSymAllocator {
     void *ptr = InternalCalloc(nmemb, size);
     CHECK(internal_allocator()->FromPrimary(ptr));
     Details::OnAllocate(ptr,
-                        internal_allocator()->GetActuallyAllocatedSize(ptr));
+                        Size(ptr));
     return ptr;
   }
 
   static void Free(void *ptr) {
-    uptr size = internal_allocator()->GetActuallyAllocatedSize(ptr);
+    uptr size = Size(ptr);
     Details::OnFree(ptr, size);
     InternalFree(ptr);
   }
@@ -63,7 +63,7 @@ struct DlSymAllocator {
       Free(ptr);
       return nullptr;
     }
-    uptr size = internal_allocator()->GetActuallyAllocatedSize(ptr);
+    uptr size = Size(ptr);
     uptr memcpy_size = Min(new_size, size);
     void *new_ptr = Allocate(new_size);
     if (new_ptr)
@@ -77,6 +77,10 @@ struct DlSymAllocator {
     return Realloc(ptr, count * size);
   }
 
+  static uptr Size(void *ptr) {
+    return internal_allocator()->GetActuallyAllocatedSize(ptr);
+  }
+
   static void OnAllocate(const void *ptr, uptr size) {}
   static void OnFree(const void *ptr, uptr size) {}
 };
diff --git a/compiler-rt/lib/tysan/tysan_interceptors.cpp b/compiler-rt/lib/tysan/tysan_interceptors.cpp
index 08b1010a48ecf0..4a89f0746230fe 100644
--- a/compiler-rt/lib/tysan/tysan_interceptors.cpp
+++ b/compiler-rt/lib/tysan/tysan_interceptors.cpp
@@ -108,6 +108,14 @@ INTERCEPTOR(void *, malloc, uptr size) {
   return res;
 }
 
+#if SANITIZER_APPLE
+INTERCEPTOR(uptr , malloc_size, void *ptr) {
+  if (DlsymAlloc::Use())
+    return DlsymAlloc::Size(ptr);
+  return REAL(malloc_size)(ptr);
+}
+#endif
+
 INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
   if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
     return DlsymAlloc::Realloc(ptr, size);

Copy link

github-actions bot commented Jan 8, 2025

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

@@ -108,6 +108,14 @@ INTERCEPTOR(void *, malloc, uptr size) {
return res;
}

#if SANITIZER_APPLE
Copy link
Contributor

Choose a reason for hiding this comment

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

drive by question: how do we decide between TYSAN_INTERCEPT___STRDUP and SANITIZER_APPLE style conditions?

@@ -108,6 +108,14 @@ INTERCEPTOR(void *, malloc, uptr size) {
return res;
}

#if SANITIZER_APPLE
INTERCEPTOR(uptr, malloc_size, void *ptr) {
if (DlsymAlloc::Use())
Copy link
Contributor

@fmayer fmayer Jan 8, 2025

Choose a reason for hiding this comment

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

Why don't we need DlSymAlloc::PointerIsMine here but we do in realloc? (undo approval)

Copy link
Contributor

@delcypher delcypher left a comment

Choose a reason for hiding this comment

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

Glad you figured out the problem. I think the implementation of malloc_size interceptor needs to be a little different to avoid a potential bug.

@@ -108,6 +108,14 @@ INTERCEPTOR(void *, malloc, uptr size) {
return res;
}

#if SANITIZER_APPLE
INTERCEPTOR(uptr, malloc_size, void *ptr) {
if (DlsymAlloc::Use())
Copy link
Contributor

@delcypher delcypher Jan 8, 2025

Choose a reason for hiding this comment

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

A less fragile implementation here would be ask the DlsymAlloc allocator if it owns this allocation and if so request the size from it, otherwise call the system malloc_size. That way the malloc_size interceptor always reports the right things no matter which allocator actually allocated the memory.

The current implementation means that if malloc_size is called after TYSan init on an allocation that was allocated during TySan init then we'll call the system malloc_size which will fail because the system allocator didn't allocator the memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I think that should be taken care of by checking PointerIsMine (as mentioned by @fmayer ). Update the code.

@@ -110,7 +110,7 @@ INTERCEPTOR(void *, malloc, uptr size) {

#if SANITIZER_APPLE
INTERCEPTOR(uptr, malloc_size, void *ptr) {
if (DlsymAlloc::Use())
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as for realloc: please document the state transitions we support.

Specifically: could it be that DlSymAlloc::Use is true but NOT DlsymAlloc::PointerIsMine(ptr) (e.g. because the pointer was created before DlSymAlloc was enabled? That would be incorrect then I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

For realloc, during TySan init realloc(0, size) can make DlSymAlloc::Use is true but DlsymAlloc::PointerIsMine(ptr) is false.

Copy link
Contributor

Choose a reason for hiding this comment

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

So do we need DlsymAlloc::Use() here? To me DlsymAlloc::PointerIsMine(ptr) sounds like what we actually care about?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep that should do the trick, done ,thanks

Copy link
Contributor

@Enna1 Enna1 left a comment

Choose a reason for hiding this comment

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

Thanks for the fix!

fhahn added 4 commits January 9, 2025 11:45
On Apple platforms, malloc_size also needs intercepting with
DlSymAllocator, otherwise all type-sanitized binaries crash on startup
with an objc error:
   realized class 0x12345 has corrupt data pointer: malloc_size(0x567) = 0
@fhahn fhahn force-pushed the tysan-intercept-malloc-size branch from 8c8f9d9 to 5688643 Compare January 9, 2025 11:54
Copy link
Contributor

@delcypher delcypher left a comment

Choose a reason for hiding this comment

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

I have a minor nit. Other than that LGTM.

@@ -77,6 +75,10 @@ struct DlSymAllocator {
return Realloc(ptr, count * size);
}

static uptr Size(void *ptr) {
Copy link
Contributor

@delcypher delcypher Jan 9, 2025

Choose a reason for hiding this comment

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

Nit: I'd call this GetSize() rather than Size() because other methods have verbs in their name (e.g. Allocate) but Size doesn't contain a verb but GetSize() does.

It's not really that important so feel free to ignore this suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, thanks!

@seanm
Copy link

seanm commented Jan 9, 2025

I've download this patch locally, and can confirm it fixes my issue. Now 94% of cmake's unit tests pass, which is the kind of fraction one might expect using a new stress testing tool. Now to actually take a look at some...

@seanm
Copy link

seanm commented Jan 9, 2025

I've download this patch locally, and can confirm it fixes my issue. Now 94% of cmake's unit tests pass, which is the kind of fraction one might expect using a new stress testing tool. Now to actually take a look at some...

Actually, looks like most (all?) are some weird linking issue:

dyld[82529]: Symbol not found: __ZNSt13exception_ptr31__from_native_exception_pointerEPv
  Referenced from: <9FF30C7F-ED6B-3C65-AE51-8D211D47EBA4> /Users/sean/external/cmake-bin/Tests/CMakeLib/CMakeLibTests
  Expected in:     <C4E10D69-6F13-32B1-831D-82DA32D7DB88> /usr/lib/libc++.1.dylib

@fhahn
Copy link
Contributor Author

fhahn commented Jan 9, 2025

I've download this patch locally, and can confirm it fixes my issue. Now 94% of cmake's unit tests pass, which is the kind of fraction one might expect using a new stress testing tool. Now to actually take a look at some...

Actually, looks like most (all?) are some weird linking issue:

dyld[82529]: Symbol not found: __ZNSt13exception_ptr31__from_native_exception_pointerEPv
  Referenced from: <9FF30C7F-ED6B-3C65-AE51-8D211D47EBA4> /Users/sean/external/cmake-bin/Tests/CMakeLib/CMakeLibTests
  Expected in:     <C4E10D69-6F13-32B1-831D-82DA32D7DB88> /usr/lib/libc++.1.dylib

@seanm Thanks for checking! Not sure what is going on here exactly, I've not seen this locally myself. Could you file an issue with steps to reproduce?

@seanm
Copy link

seanm commented Jan 9, 2025

@seanm Thanks for checking! Not sure what is going on here exactly, I've not seen this locally myself. Could you file an issue with steps to reproduce?

OK I'll wait until this is merged so as to be using a more proper build of clang.

@fhahn fhahn merged commit e8c8543 into llvm:main Jan 9, 2025
5 of 6 checks passed
@fhahn fhahn deleted the tysan-intercept-malloc-size branch January 9, 2025 20:55
github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 10, 2025
After llvm/llvm-project#120563 malloc_size also
needs intercepting on Apple platforms, otherwise all type-sanitized
binaries crash on startup with an objc error:
realized class 0x12345 has corrupt data pointer: malloc_size(0x567) = 0

PR: llvm/llvm-project#122133
BaiXilin pushed a commit to BaiXilin/llvm-fix-vnni-instr-types that referenced this pull request Jan 12, 2025
After llvm#120563 malloc_size also
needs intercepting on Apple platforms, otherwise all type-sanitized
binaries crash on startup with an objc error:
realized class 0x12345 has corrupt data pointer: malloc_size(0x567) = 0

PR: llvm#122133
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants