Skip to content

Commit 1a4b9b6

Browse files
committed
[asan] Ensure __asan_register_elf_globals is called in COMDAT asan.module_ctor (llvm#67745)
On ELF platforms, when there is no global variable, COMDAT asan.module_ctor is created with no `__asan_register_elf_globals` calls. If this COMDAT is the prevailing copy selected by the linker, the linkage unit will have no `__asan_register_elf_globals` call: the redzone will not be poisoned and ODR violation checker will not work (llvm#67677). This behavior is benign for -fno-sanitize-address-globals-dead-stripping because asan.module_ctor functions that call `__asan_register_globals` (`InstrumentGlobalsWithMetadataArray`) do not use COMDAT. To fix llvm#67677: * Use COMDAT for -fsanitize-address-globals-dead-stripping on ELF platforms. * Call `__asan_register_elf_globals` even if there is no global variable. Alternatively, when there is no global variable, asan.module_ctor is not COMDAT and does not call `__asan_register_elf_globals`. However, the asan.module_ctor function cannot be eliminated by the linker. Tested the following script. Only ELF -fsanitize-address-globals-dead-stripping has changed behaviors. ``` echo > a.cc # no global variable, empty uniqueModuleId echo 'void f() {}' > b.cc # with global variable, with uniqueModuleId echo 'int g;' > c.cc # with global variable for t in x86_64-linux-gnu arm64-apple-macosx x86_64-windows-msvc; do for gc in -f{,no-}sanitize-address-globals-dead-stripping; do for f in a.cc b.cc c.cc; do echo /tmp/Rel/bin/clang -S --target=$t -fsanitize=address $gc $f -o - /tmp/Rel/bin/clang -S --target=$t -fsanitize=address $gc $f -o - | sed -n '/asan.module_ctor/,/ret/p' done done done ```
1 parent cc9ba56 commit 1a4b9b6

File tree

3 files changed

+45
-29
lines changed

3 files changed

+45
-29
lines changed

llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ class ModuleAddressSanitizer {
819819
private:
820820
void initializeCallbacks(Module &M);
821821

822-
bool InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat);
822+
void InstrumentGlobals(IRBuilder<> &IRB, Module &M, bool *CtorComdat);
823823
void InstrumentGlobalsCOFF(IRBuilder<> &IRB, Module &M,
824824
ArrayRef<GlobalVariable *> ExtendedGlobals,
825825
ArrayRef<Constant *> MetadataInitializers);
@@ -2237,7 +2237,7 @@ void ModuleAddressSanitizer::InstrumentGlobalsELF(
22372237

22382238
// We also need to unregister globals at the end, e.g., when a shared library
22392239
// gets closed.
2240-
if (DestructorKind != AsanDtorKind::None) {
2240+
if (DestructorKind != AsanDtorKind::None && !MetadataGlobals.empty()) {
22412241
IRBuilder<> IrbDtor(CreateAsanModuleDtor(M));
22422242
IrbDtor.CreateCall(AsanUnregisterElfGlobals,
22432243
{IRB.CreatePointerCast(RegisteredFlag, IntptrTy),
@@ -2343,10 +2343,8 @@ void ModuleAddressSanitizer::InstrumentGlobalsWithMetadataArray(
23432343
// redzones and inserts this function into llvm.global_ctors.
23442344
// Sets *CtorComdat to true if the global registration code emitted into the
23452345
// asan constructor is comdat-compatible.
2346-
bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M,
2346+
void ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M,
23472347
bool *CtorComdat) {
2348-
*CtorComdat = false;
2349-
23502348
// Build set of globals that are aliased by some GA, where
23512349
// getExcludedAliasedGlobal(GA) returns the relevant GlobalVariable.
23522350
SmallPtrSet<const GlobalVariable *, 16> AliasedGlobalExclusions;
@@ -2364,11 +2362,6 @@ bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M,
23642362
}
23652363

23662364
size_t n = GlobalsToChange.size();
2367-
if (n == 0) {
2368-
*CtorComdat = true;
2369-
return false;
2370-
}
2371-
23722365
auto &DL = M.getDataLayout();
23732366

23742367
// A global is described by a structure
@@ -2391,8 +2384,11 @@ bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M,
23912384

23922385
// We shouldn't merge same module names, as this string serves as unique
23932386
// module ID in runtime.
2394-
GlobalVariable *ModuleName = createPrivateGlobalForString(
2395-
M, M.getModuleIdentifier(), /*AllowMerging*/ false, kAsanGenPrefix);
2387+
GlobalVariable *ModuleName =
2388+
n != 0
2389+
? createPrivateGlobalForString(M, M.getModuleIdentifier(),
2390+
/*AllowMerging*/ false, kAsanGenPrefix)
2391+
: nullptr;
23962392

23972393
for (size_t i = 0; i < n; i++) {
23982394
GlobalVariable *G = GlobalsToChange[i];
@@ -2517,27 +2513,34 @@ bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M,
25172513
}
25182514
appendToCompilerUsed(M, ArrayRef<GlobalValue *>(GlobalsToAddToUsedList));
25192515

2520-
std::string ELFUniqueModuleId =
2521-
(UseGlobalsGC && TargetTriple.isOSBinFormatELF()) ? getUniqueModuleId(&M)
2522-
: "";
2523-
2524-
if (!ELFUniqueModuleId.empty()) {
2525-
InstrumentGlobalsELF(IRB, M, NewGlobals, Initializers, ELFUniqueModuleId);
2516+
if (UseGlobalsGC && TargetTriple.isOSBinFormatELF()) {
2517+
// Use COMDAT and register globals even if n == 0 to ensure that (a) the
2518+
// linkage unit will only have one module constructor, and (b) the register
2519+
// function will be called. The module destructor is not created when n ==
2520+
// 0.
25262521
*CtorComdat = true;
2527-
} else if (UseGlobalsGC && TargetTriple.isOSBinFormatCOFF()) {
2528-
InstrumentGlobalsCOFF(IRB, M, NewGlobals, Initializers);
2529-
} else if (UseGlobalsGC && ShouldUseMachOGlobalsSection()) {
2530-
InstrumentGlobalsMachO(IRB, M, NewGlobals, Initializers);
2522+
InstrumentGlobalsELF(IRB, M, NewGlobals, Initializers,
2523+
getUniqueModuleId(&M));
2524+
} else if (n == 0) {
2525+
// When UseGlobalsGC is false, COMDAT can still be used if n == 0, because
2526+
// all compile units will have identical module constructor/destructor.
2527+
*CtorComdat = TargetTriple.isOSBinFormatELF();
25312528
} else {
2532-
InstrumentGlobalsWithMetadataArray(IRB, M, NewGlobals, Initializers);
2529+
*CtorComdat = false;
2530+
if (UseGlobalsGC && TargetTriple.isOSBinFormatCOFF()) {
2531+
InstrumentGlobalsCOFF(IRB, M, NewGlobals, Initializers);
2532+
} else if (UseGlobalsGC && ShouldUseMachOGlobalsSection()) {
2533+
InstrumentGlobalsMachO(IRB, M, NewGlobals, Initializers);
2534+
} else {
2535+
InstrumentGlobalsWithMetadataArray(IRB, M, NewGlobals, Initializers);
2536+
}
25332537
}
25342538

25352539
// Create calls for poisoning before initializers run and unpoisoning after.
25362540
if (HasDynamicallyInitializedGlobals)
25372541
createInitializerPoisonCalls(M, ModuleName);
25382542

25392543
LLVM_DEBUG(dbgs() << M);
2540-
return true;
25412544
}
25422545

25432546
uint64_t

llvm/test/Instrumentation/AddressSanitizer/basic.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ define void @test_swifterror_3() sanitize_address {
210210

211211
;; ctor/dtor have the nounwind attribute. See uwtable.ll, they additionally have
212212
;; the uwtable attribute with the module flag "uwtable".
213-
; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]] {{(comdat )?}}{
213+
; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]] comdat {
214214
; CHECK: call void @__asan_init()
215+
;; __asan_register_elf_globals is called even if this module does not contain global variables.
216+
; CHECK: call void @__asan_register_elf_globals(i64 ptrtoint (ptr @___asan_globals_registered to i64), i64 ptrtoint (ptr @__start_asan_globals to i64), i64 ptrtoint (ptr @__stop_asan_globals to i64))
215217

216218
; CHECK: attributes #[[#ATTR]] = { nounwind }
217219

llvm/test/Instrumentation/AddressSanitizer/global_metadata_array.ll

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
; RUN: opt < %s -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefix=CHECK %s
2-
; RUN: opt < %s -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-apple-macosx10.11.0 -S | FileCheck --check-prefix=CHECK %s
3-
; RUN: opt < %s -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-pc-windows-msvc19.0.24215 -S | FileCheck --check-prefix=CHECK %s
4-
; RUN: opt < %s -passes=asan -asan-globals-live-support=0 -asan-mapping-scale=5 -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefixes=CHECK,CHECK-S5 %s
1+
; RUN: rm -rf %t && split-file %s %t && cd %t
2+
; RUN: opt < a.ll -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefix=CHECK %s
3+
; RUN: opt < a.ll -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-apple-macosx10.11.0 -S | FileCheck --check-prefix=CHECK %s
4+
; RUN: opt < a.ll -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-pc-windows-msvc19.0.24215 -S | FileCheck --check-prefix=CHECK %s
5+
; RUN: opt < a.ll -passes=asan -asan-globals-live-support=0 -asan-mapping-scale=5 -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefixes=CHECK,CHECK-S5 %s
56

7+
; RUN: opt < empty.ll -passes=asan -asan-globals-live-support=0 -mtriple=x86_64-unknown-linux-gnu -S | FileCheck --check-prefix=ELF-NOGC %s
8+
9+
;--- a.ll
610
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
711

812
; Globals:
@@ -59,3 +63,10 @@ attributes #1 = { nounwind sanitize_address "less-precise-fpmad"="false" "frame-
5963
!7 = !{!"/tmp/asan-globals.cpp", i32 7, i32 5}
6064
!8 = !{!"/tmp/asan-globals.cpp", i32 12, i32 14}
6165
!9 = !{!"/tmp/asan-globals.cpp", i32 14, i32 25}
66+
67+
;; In the presence of instrumented global variables, asan.module_ctor do not use comdat.
68+
; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]] {
69+
70+
; ELF-NOGC: define internal void @asan.module_ctor() #[[#ATTR:]] comdat {
71+
72+
;--- empty.ll

0 commit comments

Comments
 (0)