Skip to content

[mlir][bufferization] Fix crash with copy-before-write + bufferize-function-boundaries#186446

Merged
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-163052
Mar 17, 2026
Merged

[mlir][bufferization] Fix crash with copy-before-write + bufferize-function-boundaries#186446
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-163052

Conversation

@joker-eph

Copy link
Copy Markdown
Contributor

When copy-before-write=1 is combined with bufferize-function-boundaries=1, bufferizeOp creates a plain AnalysisState (not OneShotAnalysisState) and passes it to insertTensorCopies. Walking CallOps during conflict resolution called getCalledFunction(callOp, state), which unconditionally cast the AnalysisState to OneShotAnalysisState via static_cast, causing UB and a stack overflow crash.

Fix by guarding the cast with isa<OneShotAnalysisState>() so that when the state is a plain AnalysisState, the function falls through to building a fresh SymbolTableCollection — the same safe fallback already present.

Fixes #163052

Assisted-by: Claude Code

…nction-boundaries

When `copy-before-write=1` is combined with `bufferize-function-boundaries=1`,
`bufferizeOp` creates a plain `AnalysisState` (not `OneShotAnalysisState`) and
passes it to `insertTensorCopies`. Walking `CallOp`s during conflict resolution
called `getCalledFunction(callOp, state)`, which unconditionally cast the
`AnalysisState` to `OneShotAnalysisState` via `static_cast`, causing UB and a
stack overflow crash.

Fix by guarding the cast with `isa<OneShotAnalysisState>()` so that when the
state is a plain `AnalysisState`, the function falls through to building a
fresh `SymbolTableCollection` — the same safe fallback already present.

Fixes llvm#163052

Assisted-by: Claude Code
@joker-eph joker-eph removed the request for review from matthias-springer March 13, 2026 16:38
@llvmbot llvmbot added mlir mlir:bufferization Bufferization infrastructure labels Mar 13, 2026
@llvmbot

llvmbot commented Mar 13, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mlir-bufferization

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

Changes

When copy-before-write=1 is combined with bufferize-function-boundaries=1, bufferizeOp creates a plain AnalysisState (not OneShotAnalysisState) and passes it to insertTensorCopies. Walking CallOps during conflict resolution called getCalledFunction(callOp, state), which unconditionally cast the AnalysisState to OneShotAnalysisState via static_cast, causing UB and a stack overflow crash.

Fix by guarding the cast with isa&lt;OneShotAnalysisState&gt;() so that when the state is a plain AnalysisState, the function falls through to building a fresh SymbolTableCollection — the same safe fallback already present.

Fixes #163052

Assisted-by: Claude Code


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

2 Files Affected:

  • (modified) mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp (+8-6)
  • (added) mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-call-copy-before-write.mlir (+16)
diff --git a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
index e43ab54a048b9..3aaa38272935d 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.cpp
@@ -101,12 +101,14 @@ static FuncOp getCalledFunction(CallOpInterface callOp,
 /// Return the FuncOp called by `callOp`.
 static FuncOp getCalledFunction(CallOpInterface callOp,
                                 const AnalysisState &state) {
-  auto &oneShotAnalysisState = static_cast<const OneShotAnalysisState &>(state);
-
-  if (auto *funcAnalysisState =
-          oneShotAnalysisState.getExtension<FuncAnalysisState>()) {
-    // Use the cached symbol tables.
-    return getCalledFunction(callOp, funcAnalysisState->symbolTables);
+  if (isa<OneShotAnalysisState>(state)) {
+    auto &oneShotAnalysisState =
+        static_cast<const OneShotAnalysisState &>(state);
+    if (auto *funcAnalysisState =
+            oneShotAnalysisState.getExtension<FuncAnalysisState>()) {
+      // Use the cached symbol tables.
+      return getCalledFunction(callOp, funcAnalysisState->symbolTables);
+    }
   }
 
   SymbolTableCollection symbolTables;
diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-call-copy-before-write.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-call-copy-before-write.mlir
new file mode 100644
index 0000000000000..7addca2c9d6a5
--- /dev/null
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-call-copy-before-write.mlir
@@ -0,0 +1,16 @@
+// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 copy-before-write=1" | FileCheck %s
+
+// Regression test for https://github.com/llvm/llvm-project/issues/163052
+// copy-before-write=1 + bufferize-function-boundaries=1 with a call to a
+// private (declaration-only) function used to crash with a stack overflow due
+// to an invalid cast of AnalysisState to OneShotAnalysisState inside
+// getCalledFunction().
+
+// CHECK-LABEL: func.func private @callee(memref<64xf32
+// CHECK-LABEL: func.func @caller
+// CHECK:         call @callee
+func.func private @callee(tensor<64xf32>)
+func.func @caller(%A : tensor<64xf32>) {
+  call @callee(%A) : (tensor<64xf32>) -> ()
+  return
+}

@joker-eph

Copy link
Copy Markdown
Contributor Author

Ping @matthias-springer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mlir:bufferization Bufferization infrastructure mlir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[mlir] [llvm] stack overflow in llvm/include/llvm/ADT/DenseMap.h

3 participants