[llvm] [LTO] Fix a crash with thin LTO caching and asm output (PR #138203)
Alexey Karyakin via llvm-commits
llvm-commits at lists.llvm.org
Thu May 1 14:37:21 PDT 2025
https://github.com/quic-akaryaki created https://github.com/llvm/llvm-project/pull/138203
The commit function of CacheFile deletes the underlying raw stream. Some output streamers may hold a pointer to it, which in this case will outlive the stream object. In particular, MCAsmStreamer keeps the pointer to the raw stream though formatted_raw_stream, which calls flush() in its destructor. If commit is called before MCAsmStreamer destruction, it will access freed memory.
Fixes: #138194.
>From a37da1a162d15d00a1f99435666ede9306bfe71f Mon Sep 17 00:00:00 2001
From: Alexey Karyakin <akaryaki at quicinc.com>
Date: Thu, 1 May 2025 14:28:20 -0700
Subject: [PATCH] [LTO] Fix a crash with thin LTO caching and asm output
The commit function of CacheFile deletes the underlying raw stream.
Some output streamers may hold a pointer to it, which in this case will
outlive the stream object. In particular, MCAsmStreamer keeps the
pointer to the raw stream though formatted_raw_stream, which calls
flush() in its destructor. If commit is called before MCAsmStreamer
destruction, it will access freed memory.
---
llvm/lib/LTO/LTOBackend.cpp | 48 +++++++++++++++++++++----------------
1 file changed, 27 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index dd9197efa7718..905e841291cf8 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -439,27 +439,33 @@ static void codegen(const Config &Conf, TargetMachine *TM,
std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
TM->Options.ObjectFilenameForDebug = Stream->ObjectPathName;
- legacy::PassManager CodeGenPasses;
- TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
- CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
- // No need to make index available if the module is empty.
- // In theory these passes should not use the index for an empty
- // module, however, this guards against doing any unnecessary summary-based
- // analysis in the case of a ThinLTO build where this might be an empty
- // regular LTO combined module, with a large combined index from ThinLTO.
- if (!isEmptyModule(Mod))
- CodeGenPasses.add(
- createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
- if (Conf.PreCodeGenPassesHook)
- Conf.PreCodeGenPassesHook(CodeGenPasses);
- if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
- DwoOut ? &DwoOut->os() : nullptr,
- Conf.CGFileType))
- report_fatal_error("Failed to setup codegen");
- CodeGenPasses.run(Mod);
-
- if (DwoOut)
- DwoOut->keep();
+ // Create the LTO pipeline in its own scope so it gets deleted before
+ // Stream->commit() is called. The commit function of CacheFile deletes
+ // the raw stream, which is too early as streamers (e.g. MCAsmStreamer)
+ // keep the pointer and may use it until their destruction. See #138194.
+ {
+ legacy::PassManager CodeGenPasses;
+ TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
+ CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
+ // No need to make index available if the module is empty.
+ // In theory these passes should not use the index for an empty
+ // module, however, this guards against doing any unnecessary summary-based
+ // analysis in the case of a ThinLTO build where this might be an empty
+ // regular LTO combined module, with a large combined index from ThinLTO.
+ if (!isEmptyModule(Mod))
+ CodeGenPasses.add(
+ createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
+ if (Conf.PreCodeGenPassesHook)
+ Conf.PreCodeGenPassesHook(CodeGenPasses);
+ if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
+ DwoOut ? &DwoOut->os() : nullptr,
+ Conf.CGFileType))
+ report_fatal_error("Failed to setup codegen");
+ CodeGenPasses.run(Mod);
+
+ if (DwoOut)
+ DwoOut->keep();
+ }
if (Error Err = Stream->commit())
report_fatal_error(std::move(Err));
More information about the llvm-commits
mailing list