[llvm] 8cc0259 - [ORC] Clear stale ElemToPendingSN entries in WaitingOnGraph. (#169747)

via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 26 17:03:08 PST 2025


Author: Lang Hames
Date: 2025-11-27T12:03:03+11:00
New Revision: 8cc02597f119674b7592e4db5dabf476c97ac8fa

URL: https://github.com/llvm/llvm-project/commit/8cc02597f119674b7592e4db5dabf476c97ac8fa
DIFF: https://github.com/llvm/llvm-project/commit/8cc02597f119674b7592e4db5dabf476c97ac8fa.diff

LOG: [ORC] Clear stale ElemToPendingSN entries in WaitingOnGraph. (#169747)

WaitingOnGraph::processReadyOrFailed was not clearing stale entries from
the ElemToPendingSN map. If symbols were removed from the
ExecutionSession and then re-added this could lead to dependencies on
the stale entries, triggering a use-after-free bug.

https://github.com/llvm/llvm-project/issues/169135

Added: 
    

Modified: 
    llvm/include/llvm/ExecutionEngine/Orc/WaitingOnGraph.h
    llvm/unittests/ExecutionEngine/Orc/WaitingOnGraphTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/WaitingOnGraph.h b/llvm/include/llvm/ExecutionEngine/Orc/WaitingOnGraph.h
index 0b46c7fb1f445..93412d9d22f8c 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/WaitingOnGraph.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/WaitingOnGraph.h
@@ -338,9 +338,9 @@ template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
     // incorporate NewSNs.
     std::vector<std::unique_ptr<SuperNode>> ReadyNodes, FailedNodes;
     processReadyOrFailed(ModifiedPendingSNs, ReadyNodes, FailedNodes,
-                         SuperNodeDeps, ElemToPendingSN, FailedSNs);
+                         SuperNodeDeps, FailedSNs, &ElemToPendingSN);
     processReadyOrFailed(NewSNs, ReadyNodes, FailedNodes, SuperNodeDeps,
-                         ElemToNewSN, FailedSNs);
+                         FailedSNs, nullptr);
 
     CoalesceToPendingSNs.coalesce(ModifiedPendingSNs, ElemToPendingSN);
     CoalesceToPendingSNs.coalesce(NewSNs, ElemToPendingSN);
@@ -591,8 +591,11 @@ template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
                             std::vector<std::unique_ptr<SuperNode>> &Ready,
                             std::vector<std::unique_ptr<SuperNode>> &Failed,
                             SuperNodeDepsMap &SuperNodeDeps,
-                            ElemToSuperNodeMap &ElemToSNs,
-                            const std::vector<SuperNode *> &FailedSNs) {
+                            const std::vector<SuperNode *> &FailedSNs,
+                            ElemToSuperNodeMap *ElemToSNs) {
+
+    SmallVector<SuperNode *> ToRemoveFromElemToSNs;
+
     for (size_t I = 0; I != SNs.size();) {
       auto &SN = SNs[I];
 
@@ -609,6 +612,8 @@ template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
       bool SNReady = SN->Deps.empty();
 
       if (SNReady || SNFailed) {
+        if (ElemToSNs)
+          ToRemoveFromElemToSNs.push_back(SN.get());
         auto &NodeList = SNFailed ? Failed : Ready;
         NodeList.push_back(std::move(SN));
         std::swap(SN, SNs.back());
@@ -616,6 +621,15 @@ template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
       } else
         ++I;
     }
+
+    // Update ElemToSNs (if passed) to remove elements pointing at SN.
+    for (auto *SN : ToRemoveFromElemToSNs) {
+      for (auto &[Container, Elems] : SN->defs()) {
+        auto &Row = (*ElemToSNs)[Container];
+        for (auto &Elem : Elems)
+          Row.erase(Elem);
+      }
+    }
   }
 
   std::vector<std::unique_ptr<SuperNode>> PendingSNs;

diff  --git a/llvm/unittests/ExecutionEngine/Orc/WaitingOnGraphTest.cpp b/llvm/unittests/ExecutionEngine/Orc/WaitingOnGraphTest.cpp
index 0d4a5212c1f0c..1d550b1cfbc19 100644
--- a/llvm/unittests/ExecutionEngine/Orc/WaitingOnGraphTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/WaitingOnGraphTest.cpp
@@ -532,6 +532,52 @@ TEST_F(WaitingOnGraphTest, Emit_ZigZag) {
   EXPECT_TRUE(PendingSNs.empty());
 }
 
+TEST_F(WaitingOnGraphTest, Emit_ReEmit) {
+  // Test for the bug in https://github.com/llvm/llvm-project/issues/169135,
+  // which was caused by stale entries in the ElemsToPendingSNs map.
+  //
+  // To trigger the bug we need to:
+  // 1. Create a SuperNode with an unmet dependence, causing it to be added to
+  //    ElemsToPendingSNs.
+  // 2. Cause that SuperNode to become ready (bug left stale entries in map)
+  // 3. Remove the node from the Ready map (this is equivalent to removal of a
+  //    symbol in an ORC session, and allows new SuperNodes to depend on the
+  //    stale entry).
+  // 4. Add a new node that references the previously emitted/removed SuperNode
+  //    This triggers access of the stale entry, and should error out in
+  //    sanitizer builds.
+
+  SuperNodeBuilder B;
+
+  // 1. Create SuperNode with unmet dependence.
+  ContainerElementsMap Defs0({{0, {0}}});
+  ContainerElementsMap Deps0({{0, {1}}});
+  B.add(Defs0, Deps0);
+  emit(TestGraph::simplify(B.takeSuperNodes()));
+
+  EXPECT_TRUE(Ready.empty());
+
+  // 2. Cause previous SuperNode to become ready.
+  ContainerElementsMap Defs1({{0, {1}}});
+  B.add(Defs1, ContainerElementsMap());
+  emit(TestGraph::simplify(B.takeSuperNodes()));
+
+  // Check that both nodes have become ready.
+  EXPECT_EQ(Ready, merge(Defs0, Defs1));
+
+  // 3. Erase Ready nodes to simulate removal from the graph.
+  Ready.clear();
+
+  // 4. Emit a new dependence on the original def.
+  ContainerElementsMap Defs2({{0, {2}}});
+  ContainerElementsMap Deps2({{0, {0}}});
+  B.add(Defs2, Deps2);
+  auto ER = emit(TestGraph::simplify(B.takeSuperNodes()));
+
+  // We expect the new dependence to remain pending.
+  EXPECT_TRUE(ER.Ready.empty());
+}
+
 TEST_F(WaitingOnGraphTest, Fail_Empty) {
   // Check that failing an empty set is a no-op.
   auto FR = G.fail(ContainerElementsMap());


        


More information about the llvm-commits mailing list