Skip to content

Commit eac7466

Browse files
[LTO] Fix a crash with thin LTO caching and asm output (#138203)
The `CacheStream::commit()` function (defined in Caching.cpp) deletes the underlying raw stream. Some output streamers may hold a pointer to it, which then will outlive the stream object. In particular, MCAsmStreamer keeps the pointer to the raw stream though a separate `formatted_raw_stream` object, which buffers data and there is no path to explicitly flush this data. Before this change, the buffered data was flushed during the MCAsmStreamer destructor. After #136121, this happened after the `commit()` function is called. Therefore, it caused a crash because the `formatted_raw_stream` object tries to write the buffered data into a deleted raw stream. Even if we don't delete the stream to avoid the crash, it would be too late as the output stream cannot accept data after commit(). Fixes: #138194.
1 parent ee47454 commit eac7466

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

llvm/lib/LTO/LTOBackend.cpp

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -439,27 +439,33 @@ static void codegen(const Config &Conf, TargetMachine *TM,
439439
std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
440440
TM->Options.ObjectFilenameForDebug = Stream->ObjectPathName;
441441

442-
legacy::PassManager CodeGenPasses;
443-
TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
444-
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
445-
// No need to make index available if the module is empty.
446-
// In theory these passes should not use the index for an empty
447-
// module, however, this guards against doing any unnecessary summary-based
448-
// analysis in the case of a ThinLTO build where this might be an empty
449-
// regular LTO combined module, with a large combined index from ThinLTO.
450-
if (!isEmptyModule(Mod))
451-
CodeGenPasses.add(
452-
createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
453-
if (Conf.PreCodeGenPassesHook)
454-
Conf.PreCodeGenPassesHook(CodeGenPasses);
455-
if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
456-
DwoOut ? &DwoOut->os() : nullptr,
457-
Conf.CGFileType))
458-
report_fatal_error("Failed to setup codegen");
459-
CodeGenPasses.run(Mod);
460-
461-
if (DwoOut)
462-
DwoOut->keep();
442+
// Create the codegen pipeline in its own scope so it gets deleted before
443+
// Stream->commit() is called. The commit function of CacheStream deletes
444+
// the raw stream, which is too early as streamers (e.g. MCAsmStreamer)
445+
// keep the pointer and may use it until their destruction. See #138194.
446+
{
447+
legacy::PassManager CodeGenPasses;
448+
TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
449+
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
450+
// No need to make index available if the module is empty.
451+
// In theory these passes should not use the index for an empty
452+
// module, however, this guards against doing any unnecessary summary-based
453+
// analysis in the case of a ThinLTO build where this might be an empty
454+
// regular LTO combined module, with a large combined index from ThinLTO.
455+
if (!isEmptyModule(Mod))
456+
CodeGenPasses.add(
457+
createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
458+
if (Conf.PreCodeGenPassesHook)
459+
Conf.PreCodeGenPassesHook(CodeGenPasses);
460+
if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
461+
DwoOut ? &DwoOut->os() : nullptr,
462+
Conf.CGFileType))
463+
report_fatal_error("Failed to setup codegen");
464+
CodeGenPasses.run(Mod);
465+
466+
if (DwoOut)
467+
DwoOut->keep();
468+
}
463469

464470
if (Error Err = Stream->commit())
465471
report_fatal_error(std::move(Err));
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
;; This test runs thin LTO with cache only to look for memory errors, either
2+
;; as crashes or sanitizer errors. MCAsmStreamer has specific assumptions about
3+
;; the lifetime of the output stream that are easy to overlook (see #138194).
4+
5+
; RUN: rm -rf %t.cache
6+
; RUN: opt -module-hash -module-summary -thinlto-bc %s -o %t1.bc
7+
; RUN: ld.lld --thinlto-cache-dir=%t.cache --lto-emit-asm %t1.bc
8+
9+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
10+
target triple = "x86_64-unknown-linux-gnu"
11+
12+
define void @globalfunc() {
13+
entry:
14+
ret void
15+
}

0 commit comments

Comments
 (0)