[TEST] Add ASan/LeakSanitizer harness and fix memory bugs it found - #7831
[TEST] Add ASan/LeakSanitizer harness and fix memory bugs it found#7831Niz13 wants to merge 4 commits into
Conversation
Implements the host-side memory-leak check requested in issue #5029. - scripts/test-triton.sh gains `--asan`: runs the host-side ctest and lit suites under LeakSanitizer. Sets allow_user_poisoning=0 to neutralize the false use-after-poison from linking instrumented Triton against the non-instrumented prebuilt LLVM, guards that triton-opt is ASan-instrumented, and wires in the suppressions file. - scripts/asan/lsan.supp suppresses only benign LLVM/MLIR globals (never anything under third_party/intel). Fixes found by the harness: - StrideVersioning erased a loop from inside a pre-order walk(), so the walker dereferenced the freed op (heap-use-after-free). Switch to collect-then-mutate: gather candidate loops during the walk, version them afterwards. - Six Analysis unit-test fixtures returned a raw ModuleOp from ModuleOp::create() and never freed it. Own each module in a SmallVector<OwningOpRef<ModuleOp>> member declared after the MLIRContext. Host-side ctest (453) and lit (446) run clean under LeakSanitizer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review feedback: instead of a SmallVector<OwningOpRef<ModuleOp>> member managing module lifetimes, the buildModule()/createModule() fixture helpers now return OwningOpRef<ModuleOp> so each test owns its module locally (as in OptimizePartitionWarps.cpp). Applied across all six Analysis fixtures. Still leak-free under LeakSanitizer (118 host-side tests pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| TEST_F(AliasAnalysisTest, TwoLoadsSameArgDifferentOffsets) { | ||
| auto module = createModule(); | ||
| OwningOpRef<ModuleOp> moduleRef = createModule(); | ||
| ModuleOp module = *moduleRef; |
There was a problem hiding this comment.
Let's use module directly from OwningOpRef without unpacking into a separate variable. Here and elsewhere.
This way should be safer.
Otherwise LGTM!
There was a problem hiding this comment.
Done, but please take a look at DPAS.h, It had to be changed for this to be uniform across.
if you prefer not to change, I can make exceptions for when it doesn't work.
There was a problem hiding this comment.
Will const ModuleOp &mod work?
There was a problem hiding this comment.
Pull request overview
Adds an AddressSanitizer/LeakSanitizer testing mode to the repo’s test runner and fixes memory-safety issues uncovered by sanitizer runs (including a walk-time erase UAF in a Triton/MLIR transform and MLIR ModuleOp ownership leaks in Intel backend unit tests).
Changes:
- Add
scripts/test-triton.sh --asanto run host-onlyctest+litunder ASan/LSan with a suppression file and instrumentation guardrails. - Fix a heap-use-after-free in
TritonIntelStrideVersioningby collecting candidatescf.forloops first and versioning them after the walk. - Fix
ModuleOp::create()ownership leaks in multiple Analysis unit tests by returning/holdingOwningOpRef<ModuleOp>.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| third_party/intel/unittest/Analysis/TemporalReuseAnalysisTest.cpp | Switch module construction to OwningOpRef<ModuleOp> to prevent leaked ModuleOps in test fixtures. |
| third_party/intel/unittest/Analysis/StrideInfoTest.cpp | Same OwningOpRef<ModuleOp> lifetime ownership fix for stride-analysis unit tests. |
| third_party/intel/unittest/Analysis/SpatialReuseAnalysisTest.cpp | Same OwningOpRef<ModuleOp> lifetime ownership fix for spatial-reuse unit tests. |
| third_party/intel/unittest/Analysis/ReuseAnalysisTest.cpp | Same OwningOpRef<ModuleOp> lifetime ownership fix for reuse-analysis unit tests. |
| third_party/intel/unittest/Analysis/DPASAnalysisTest.cpp | Same OwningOpRef<ModuleOp> lifetime ownership fix for DPAS-analysis unit tests. |
| third_party/intel/unittest/Analysis/AliasAnalysisTest.cpp | Same OwningOpRef<ModuleOp> lifetime ownership fix for alias-analysis unit tests. |
| third_party/intel/lib/Dialect/Triton/Transforms/StrideVersioning.cpp | Avoid IR mutation during a pre-order walk by collecting loops first, then versioning, preventing walk iterator UAF. |
| scripts/test-triton.sh | Add --asan option to run only host-side unit + LIT tests under ASan/LSan with env configuration and instrumentation checks. |
| scripts/asan/lsan.supp | Add LSan suppression list for known benign LLVM/MLIR process-lifetime allocations. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
What's the plan to prevent regressions going forward? Without regular execution, new code could reintroduce leaks/UAFs and we wouldn't catch them until someone manually runs |
@Niz13 we may create new issue/pr as follow-up to incorporate new feature in CI runs |
… variable Address review feedback: name the OwningOpRef `module` and dereference it as `*module` at use sites, rather than unpacking into a second `ModuleOp` variable. Applied across all six Analysis test fixtures. Since OwningOpRef::operator* yields an rvalue ModuleOp, relax DPASAnalysisFactory::createDPASAnalysis to take ModuleOp by value (it never mutates the module); the handle is lightweight and existing lvalue callers still bind. Also compose ASAN_OPTIONS/LSAN_OPTIONS so the harness-critical settings are appended last (last-wins parser) and avoid a stray trailing colon when the inherited variable is empty. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the host-side memory-leak check requested in #5029.
Harness
scripts/test-triton.sh --asanruns the host-side C++ (ctest) and MLIRlitsuites under LeakSanitizer. SetsASAN_OPTIONS=allow_user_poisoning=0to neutralize a false use-after-poison caused by linking ASan-instrumented Triton against the non-instrumented prebuilt LLVM (does not weaken leak/redzone detection), and guards thattriton-optis actually ASan-instrumented.scripts/asan/lsan.suppsuppresses only benign LLVM/MLIR globals — nothing underthird_party/intel.Bugs found & fixed
StrideVersioning— the pass calledforOp.erase()inside a pre-orderwalk(), so the walker then dereferenced the freed op. Fixed with collect-then-mutate: gather candidate loops during the walk, version them afterwards (safe — only non-nested top-level loops are candidates).ModuleOpleaks inAnalysisunit-test fixtures —ModuleOp::create()returned raw and was never freed. Fixed by owning modules in aSmallVector<OwningOpRef<ModuleOp>>member declared after theMLIRContext.Result (under LeakSanitizer)
ctestlit🤖 Generated with Claude Code