Skip to content

Fix: Distinguish CFI Metadata Checks in MergeFunctions Pass #65963

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 1 commit into from
Sep 23, 2023

Conversation

oskarwirga
Copy link
Contributor

This diff fixes an issue in the MergeFunctions pass where two different Control Flow Integrity (CFI) metadata checks were incorrectly considered identical. These merges would lead to runtime violations down the line as two separate objects contained a single destructor which itself contained checks for only one of the objects.

Here I update the comparison logic to take into account the metadata at llvm.type.test checks. Now, only truly identical checks will be considered for merging, thus preserving the integrity of each check.

Previous discussion: https://reviews.llvm.org/D154119

@oskarwirga oskarwirga requested a review from a team as a code owner September 11, 2023 13:26
@oskarwirga
Copy link
Contributor Author

CC: @dexonsmith

Copy link
Collaborator

@dexonsmith dexonsmith left a comment

Choose a reason for hiding this comment

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

This an improvement on https://reviews.llvm.org/D154119, in that it doesn't break function sorting.

However:

  • It looks the performance is quadratic in the size of the function. There should be a more efficient way to do this. Especially since you have prior knowledge that the functions have sorted as equal.
  • It adds a new "compare"-named and -behaving function which violates strict-weak order (since it will return both A<B and B<A). It shouldn't over-promise that it can compare < when it only decides "yes or no" for equivalency.

A few other comments inline.

@dexonsmith
Copy link
Collaborator

CC: @dexonsmith

Can you also pull in the other reviewers from the Phabricator patch? (And if none of them did significant work on MergeFunctions, perhaps look through Git history to find someone who did...)

@dtcxzyw dtcxzyw requested review from nikic and smeenai September 11, 2023 16:21
@oskarwirga
Copy link
Contributor Author

CC: @dexonsmith

Can you also pull in the other reviewers from the Phabricator patch? (And if none of them did significant work on MergeFunctions, perhaps look through Git history to find someone who did...)

Apparently I need write access to be able to add reviewers, I can't add them on my own :(

@dexonsmith
Copy link
Collaborator

Before giving more low-level feedback, I'm a bit worried this is change is too broad.

This diff fixes an issue in the MergeFunctions pass where two different Control Flow Integrity (CFI) metadata checks were incorrectly considered identical. These merges would lead to runtime violations down the line as two separate objects contained a single destructor which itself contained checks for only one of the objects.

Can you clarify whether functions should be excluded from merging if the CFI metadata referenced by llvm.type.test matches exactly, or only if they are different? (What if they have different identities (distinct) but are structurally equivalent?)

Also, why are you restricting all metadata-as-value arguments, instead of just llvm.type.test intrinsics?

@oskarwirga
Copy link
Contributor Author

Again, thank you for taking the time to review this change!

Can you clarify whether functions should be excluded from merging if the CFI metadata referenced by llvm.type.test matches exactly, or only if they are different? (What if they have different identities (distinct) but are structurally equivalent?)

If they have different identities but are structurally the same, as in the test I created:

!10 = !{i64 16, !11}
!11 = distinct !{}
!21 = !{i64 16, !22}
!22 = distinct !{}

Then CFI will create distinct checks for each of these and they are NOT equal and NOT valid to be merged. I am not the author so I can't explain the exact specifics, but IIUC when a function is internal to the module, CFI will use empty, distinct metadata as placeholders for the final checks.

Also, why are you restricting all metadata-as-value arguments, instead of just llvm.type.test intrinsics?

@nikic had suggested on my first draft that "This doesn't looks like a problem specific to the type.test intrinsic. Other calls may have metadata operands as well." and so I made the change more broad to potentially fix other possible bad merges.

@dexonsmith
Copy link
Collaborator

Can you clarify whether functions should be excluded from merging if the CFI metadata referenced by llvm.type.test matches exactly, or only if they are different? (What if they have different identities (distinct) but are structurally equivalent?)

If they have different identities but are structurally the same, as in the test I created:

!10 = !{i64 16, !11}
!11 = distinct !{}
!21 = !{i64 16, !22}
!22 = distinct !{}

Then CFI will create distinct checks for each of these and they are NOT equal and NOT valid to be merged. I am not the author so I can't explain the exact specifics, but IIUC when a function is internal to the module, CFI will use empty, distinct metadata as placeholders for the final checks.

You only answered the parenthesized question :).

For the first question, I had a look at the documentation. In the examples, there are no distinct nodes. Looks like global types that come up like this do not use distinct, and are candidates for merging. distinct must come up for function-local types. This is likely why merging isn't valid.

(Just to double-check: is llvm.type.test being (correctly) rejected for function merging for global types (not distinct) when it's not structurally equal? (I.e., pointing at two different global types.) I'm assuming "yes" below.)


Perhaps we don't need to compare functions at all; instead, we can filter out functions as candidates for merging if they have (non-debug info) intrinsics that reference distinct metadata. You're never comparing metadata. You're just checking it.

I suggest:

  • Move the helper function to MergeFunctions.cpp and make it a top-level static function (local to the file)
  • Rename it to hasDistinctMetadataIntrinsic
  • Use it as a filter to skip over / reject a function

(Or, is there already a place early where functions are rejected as candidates for merging? If so, maybe this logic should be moved there...)

I think you should use logic something like this (I looked at BasicBlock.h to find how to skip debug info):

lang=c++
for (const BasicBlock &BB : ...) {
  for (const Instruction &I : BB.instructionsWithoutDebug()) {
    if (!isa<IntrinsicInst>(I))
      continue; // Only intrinsics can reference metadata.

    // Iterate through operands, and return true if you find a distinct metadata operand.
  }
}
return false;

@oskarwirga
Copy link
Contributor Author

(Just to double-check: is llvm.type.test being (correctly) rejected for function merging for global types (not distinct) when it's not structurally equal? (I.e., pointing at two different global types.) I'm assuming "yes" below.)

Yes, because the typeid metadata are different.

Perhaps we don't need to compare functions at all; instead, we can filter out functions as candidates for merging if they have (non-debug info) intrinsics that reference distinct metadata. You're never comparing metadata. You're just checking it.

I like this idea, will put up a new patch.

@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from 401ea8b to 779eb8e Compare September 21, 2023 01:32
@oskarwirga
Copy link
Contributor Author

It looks so much better now, thank you very much @dexonsmith!

Copy link
Collaborator

@dexonsmith dexonsmith left a comment

Choose a reason for hiding this comment

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

Nice, LGTM! I have some minor feedback inline.

@@ -404,7 +430,7 @@ bool MergeFunctions::runOnModule(Module &M) {
// If the hash value matches the previous value or the next one, we must
// consider merging it. Otherwise it is dropped and never considered again.
if ((I != S && std::prev(I)->first == I->first) ||
(std::next(I) != IE && std::next(I)->first == I->first) ) {
(std::next(I) != IE && std::next(I)->first == I->first)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: you should revert the whitespace fix-up on this line since it's not related to the patch anymore and makes it hard to see what actually changed. (Feel free to commit it separately / before / after as an NFC change if you want to clean it up (no need for review, IMO), as long as it's not squashed with this change.)

Copy link
Collaborator

Choose a reason for hiding this comment

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

The GitHub pull request interface requires "squashing", so if you don't have commit access to push this separately, it'd be better just to leave it out. Sorry for the churn!

@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from 779eb8e to 6c80916 Compare September 21, 2023 15:41
@oskarwirga
Copy link
Contributor Author

Thanks for teaching me so much in just 1 diff!

@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from 6c80916 to e037714 Compare September 21, 2023 18:14
@oskarwirga
Copy link
Contributor Author

Thank you @nikic for catching that, growing pains of using Github over phabricator :)

@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from e037714 to c13a50b Compare September 22, 2023 00:56
@oskarwirga
Copy link
Contributor Author

oskarwirga commented Sep 22, 2023

Looking good! I don't have merge access so once it gets to @nikic and @dexonsmith's standards I would appreciate a merge :)

Copy link
Collaborator

@dexonsmith dexonsmith left a comment

Choose a reason for hiding this comment

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

A few more style nits; after that, LGTM again, but let's wait for @nikic to take another look (thanks for catching the missing isDistinct() check...)

@@ -404,7 +430,7 @@ bool MergeFunctions::runOnModule(Module &M) {
// If the hash value matches the previous value or the next one, we must
// consider merging it. Otherwise it is dropped and never considered again.
if ((I != S && std::prev(I)->first == I->first) ||
(std::next(I) != IE && std::next(I)->first == I->first) ) {
(std::next(I) != IE && std::next(I)->first == I->first)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The GitHub pull request interface requires "squashing", so if you don't have commit access to push this separately, it'd be better just to leave it out. Sorry for the churn!

@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from c13a50b to 2d038ae Compare September 22, 2023 01:55
@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from 2d038ae to ca4fc4b Compare September 22, 2023 16:52
@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from ca4fc4b to c80c820 Compare September 22, 2023 17:05
@oskarwirga oskarwirga force-pushed the distinguish-distinct-md branch from c80c820 to 09ef2fd Compare September 22, 2023 17:28
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

LGTM

@oskarwirga
Copy link
Contributor Author

@nikic @dexonsmith Again thank you for your feedback on this! I've yet to have write access so I'll ask one of you to merge this. :)

@nikic nikic merged commit e06fc2b into llvm:main Sep 23, 2023
Guzhu-AMD pushed a commit to GPUOpen-Drivers/llvm-project that referenced this pull request Sep 28, 2023
Local branch amd-gfx 05e8466 Merged main:658b655191e9 into amd-gfx:d68837a57682
Remote branch main e06fc2b Fix: Distinguish CFI Metadata Checks in MergeFunctions Pass (llvm#65963)
@oskarwirga oskarwirga deleted the distinguish-distinct-md branch December 28, 2023 18:06
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.

4 participants