Skip to content

[profile] Fix bounds checks in profile merging #118782

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 2 commits into from
Dec 6, 2024

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Dec 5, 2024

These bounds checks work on the result of the pointer addition -- but the pointer addition already asserts that no overflow may occur, so the checks are optimized away after #118472. Avoid this by performing the addition in a way that permits overflow.

@nikic nikic requested a review from vitalybuka December 5, 2024 10:33
@llvmbot llvmbot added compiler-rt PGO Profile Guided Optimizations labels Dec 5, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2024

@llvm/pr-subscribers-pgo

Author: Nikita Popov (nikic)

Changes

These bounds checks work on the result of the pointer addition -- but the pointer addition already asserts that no overflow may occur, so the checks are optimized away after #118472. Avoid this by performing the addition in a way that permits overflow.


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

1 Files Affected:

  • (modified) compiler-rt/lib/profile/InstrProfilingMerge.c (+12-5)
diff --git a/compiler-rt/lib/profile/InstrProfilingMerge.c b/compiler-rt/lib/profile/InstrProfilingMerge.c
index 7cf1679811eb07..77fc69110c73ea 100644
--- a/compiler-rt/lib/profile/InstrProfilingMerge.c
+++ b/compiler-rt/lib/profile/InstrProfilingMerge.c
@@ -127,6 +127,12 @@ getDistanceFromCounterToValueProf(const __llvm_profile_header *const Header) {
          PaddingBytesAfterVNamesSize;
 }
 
+// Add offset to pointer without assuming that the addition does not overflow.
+// This allows performing bounds checks by checking the result of the addition.
+static const char *ptr_add_with_overflow(const char *p, size_t offset) {
+  return (const char *)((uintptr_t)p + offset);
+}
+
 COMPILER_RT_VISIBILITY
 int __llvm_profile_merge_from_buffer(const char *ProfileData,
                                      uint64_t ProfileSize) {
@@ -154,9 +160,10 @@ int __llvm_profile_merge_from_buffer(const char *ProfileData,
   SrcCountersStart = (char *)SrcDataEnd;
   SrcCountersEnd = SrcCountersStart +
                    Header->NumCounters * __llvm_profile_counter_entry_size();
-  SrcBitmapStart = SrcCountersEnd + __llvm_profile_get_num_padding_bytes(
-                                        SrcCountersEnd - SrcCountersStart);
-  SrcNameStart = SrcBitmapStart + Header->NumBitmapBytes;
+  SrcBitmapStart = ptr_add_with_overflow(
+      SrcCountersEnd,
+      __llvm_profile_get_num_padding_bytes(SrcCountersEnd - SrcCountersStart));
+  SrcNameStart = ptr_add_with_overflow(SrcBitmapStart, Header->NumBitmapBytes);
   SrcValueProfDataStart =
       SrcNameStart + getDistanceFromCounterToValueProf(Header);
   if (SrcNameStart < SrcCountersStart || SrcNameStart < SrcBitmapStart)
@@ -200,8 +207,8 @@ int __llvm_profile_merge_from_buffer(const char *ProfileData,
     // CountersDelta computes the offset into the in-buffer counter section.
     //
     // On WIN64, CountersDelta is truncated as well, so no need for signext.
-    char *SrcCounters =
-        SrcCountersStart + ((uintptr_t)SrcData->CounterPtr - CountersDelta);
+    char *SrcCounters = ptr_add_with_overflow(
+        SrcCountersStart, (uintptr_t)SrcData->CounterPtr - CountersDelta);
     // CountersDelta needs to be decreased as we advance to the next data
     // record.
     CountersDelta -= sizeof(*SrcData);

Copy link
Contributor

@chapuni chapuni left a comment

Choose a reason for hiding this comment

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

Looks good. Please wait for another opinion.

For bound-free arithmetic, shall we cast to uintptr_t, ultimately?

nikic added 2 commits December 5, 2024 14:37
These bounds checks work on the result of the pointer addition --
but the pointer addition already asserts that no overflow may
occur, so the checks are optimized away after llvm#118472. Avoid this
by performing the addition in a way that permits overflow.
@nikic nikic force-pushed the profile-bounds-check branch from 44d55b7 to b6d1b81 Compare December 5, 2024 13:40
@nikic
Copy link
Contributor Author

nikic commented Dec 5, 2024

For bound-free arithmetic, shall we cast to uintptr_t, ultimately?

I'm not sure I get what you mean here -- are you suggesting we should do everything in uintptr rather than const char and then only cast to a pointer type when doing accesses?

@vitalybuka
Copy link
Collaborator

I will apply both patches and validate in about 1h

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

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

The patch fixes the crash caused by #118472

@ellishg
Copy link
Contributor

ellishg commented Dec 5, 2024

For bound-free arithmetic, shall we cast to uintptr_t, ultimately?

I'm not sure I get what you mean here -- are you suggesting we should do everything in uintptr rather than const char and then only cast to a pointer type when doing accesses?

I agree with @chapuni, it seems cleaner to make the type of SrcBitmapStart (and similar variables) uintptr_t in the first place.

@nikic nikic merged commit 3eed847 into llvm:main Dec 6, 2024
7 checks passed
@nikic nikic deleted the profile-bounds-check branch December 6, 2024 09:09
nikic added a commit to nikic/llvm-project that referenced this pull request Dec 6, 2024
Based on the feedback from llvm#118782, this switches most of the
pointer arithmetic in __llvm_profile_merge_from_buffer to work
on uintptr_t instead of const char *, only casting back to a
pointer when performing actual accesses.

This ensures that all the arithmetic is performed without any
assumptions about pointer overflow.
@nikic
Copy link
Contributor Author

nikic commented Dec 6, 2024

I've created a followup to switch most of the arithmetic to uintptr_t here: #118944

nikic added a commit that referenced this pull request Dec 6, 2024
The profile runtime test failure this caused has been addressed in:
#118782

-----

Unsigned icmp of gep nuw folds to unsigned icmp of offsets. Unsigned
icmp of gep nusw nuw folds to unsigned samesign icmp of offsets.

Proofs: https://alive2.llvm.org/ce/z/VEwQY8
nikic added a commit that referenced this pull request Dec 9, 2024
Based on the feedback from #118782, this switches most of the pointer
arithmetic in __llvm_profile_merge_from_buffer to work on uintptr_t
instead of const char *, only casting back to a pointer when performing
actual accesses.

This ensures that all the arithmetic is performed without any
assumptions about pointer overflow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler-rt PGO Profile Guided Optimizations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants