This is an archive of the discontinued LLVM Phabricator instance.

[IR] Allow Value::replaceUsesWithIf() to process constants
ClosedPublic

Authored by rampitec on May 24 2021, 2:59 PM.

Details

Summary

The change is currently NFC, but exploited by the depending D102954.
Code to handle constants is borrowed from the general implementation
of Value::doRAUW().

Diff Detail

Event Timeline

rampitec created this revision.May 24 2021, 2:59 PM
rampitec requested review of this revision.May 24 2021, 2:59 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 24 2021, 2:59 PM
Herald added a subscriber: wdng. · View Herald Transcript
This revision is now accepted and ready to land.May 25 2021, 1:39 AM
efriedma added inline comments.
llvm/lib/IR/Value.cpp
543

I'm suspicious of the way you're iterating over the use list here; incrementing early isn't enough to avoid a use-after-free, I think. The incremented UI might refer to the same constant.

doRAUW repeatedly accesses *use_begin() to avoid issues.

rampitec added inline comments.Jun 28 2021, 12:10 PM
llvm/lib/IR/Value.cpp
543

I do not think this is currently exploitable as the only caller with constants convertConstantExprsToInstructions() guards it, but this may be true in general. Since not all uses are going to be replaced I cannot do the same thing as doRAUW. Do you think this will resolve the potential issue?

@@ -527,22 +527,26 @@ void Value::replaceNonMetadataUsesWith(Value *New) {

 void Value::replaceUsesWithIf(Value *New,
                               llvm::function_ref<bool(Use &U)> ShouldReplace) {
   assert(New && "Value::replaceUsesWithIf(<null>) is invalid!");
   assert(New->getType() == getType() &&
          "replaceUses of value with new value of different type!");

+  SmallPtrSet<Constant *, 8> Visited;
   for (use_iterator UI = use_begin(), E = use_end(); UI != E;) {
     Use &U = *UI;
     ++UI;
+    auto *C = dyn_cast<Constant>(U.getUser());
+    if (C && !Visited.insert(C).second)
+      continue;
     if (!ShouldReplace(U))
       continue;
     // Must handle Constants specially, we cannot call replaceUsesOfWith on a
     // constant because they are uniqued.
-    if (auto *C = dyn_cast<Constant>(U.getUser())) {
+    if (C) {
       if (!isa<GlobalValue>(C)) {
         C->handleOperandChange(this, New);
         continue;
       }
     }
     U.set(New);
   }
efriedma added inline comments.Jun 28 2021, 1:10 PM
llvm/lib/IR/Value.cpp
543

I don't think that's quite right. The problem is ensuring that UI points to something valid when we execute the ++UI. Adding a "continue" after that doesn't really help.

The most straightforward solution I can think of is to dodge the issue by not calling handleOperandChange with an active use_iterator. Instead, we collect all the constant users into a vector of TrackingVH, then iterate over that.

Also, minor related issue: handleOperandChange updates all the uses in a given Constant, not just the one passed to ShouldReplace.

rampitec marked an inline comment as done.Jun 28 2021, 1:50 PM
rampitec added inline comments.
llvm/lib/IR/Value.cpp
543

Thanks! A good idea, here is the patch: D105061
For the handleOperandChange() I have left a FIXME for now in the same review. This is a very good point, although not essential for the current use.