Skip to content

Commit 3957141

Browse files
committed
[asan] Add support for dead code stripping
Implement a new way global variables are registered making ASan global buffer overflow compatible with -dead-strip linker option. We disable the new registration mode by default. This will allow us to catch global variable overflows in ASan SWBs and unblock kernel sanitizer development, while still not exposing all of the users to the new mode this late in the development cycle. rdar://problem/15424799
1 parent 118fdd6 commit 3957141

File tree

3 files changed

+155
-12
lines changed

3 files changed

+155
-12
lines changed

lib/Transforms/Instrumentation/AddressSanitizer.cpp

Lines changed: 117 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ static const char *const kAsanReportErrorTemplate = "__asan_report_";
8989
static const char *const kAsanRegisterGlobalsName = "__asan_register_globals";
9090
static const char *const kAsanUnregisterGlobalsName =
9191
"__asan_unregister_globals";
92+
static const char *const kAsanRegisterImageGlobalsName =
93+
"__asan_register_image_globals";
94+
static const char *const kAsanUnregisterImageGlobalsName =
95+
"__asan_unregister_image_globals";
9296
static const char *const kAsanPoisonGlobalsName = "__asan_before_dynamic_init";
9397
static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init";
9498
static const char *const kAsanInitName = "__asan_init";
@@ -106,6 +110,8 @@ static const char *const kAsanPoisonStackMemoryName =
106110
"__asan_poison_stack_memory";
107111
static const char *const kAsanUnpoisonStackMemoryName =
108112
"__asan_unpoison_stack_memory";
113+
static const char *const kAsanGlobalsRegisteredFlagName =
114+
"__asan_globals_registered";
109115

110116
static const char *const kAsanOptionDetectUAR =
111117
"__asan_option_detect_stack_use_after_return";
@@ -226,6 +232,13 @@ static cl::opt<uint32_t> ClForceExperiment(
226232
cl::desc("Force optimization experiment (for testing)"), cl::Hidden,
227233
cl::init(0));
228234

235+
static cl::opt<bool>
236+
ClUseMachOGlobalsSection("asan-globals-live-support",
237+
cl::desc("Use linker features to support dead "
238+
"code stripping of globals "
239+
"(Mach-O only)"),
240+
cl::Hidden, cl::init(false));
241+
229242
// Debug flags.
230243
static cl::opt<int> ClDebug("asan-debug", cl::desc("debug"), cl::Hidden,
231244
cl::init(0));
@@ -520,6 +533,7 @@ class AddressSanitizerModule : public ModulePass {
520533

521534
bool InstrumentGlobals(IRBuilder<> &IRB, Module &M);
522535
bool ShouldInstrumentGlobal(GlobalVariable *G);
536+
bool ShouldUseMachOGlobalsSection() const;
523537
void poisonOneInitializer(Function &GlobalInit, GlobalValue *ModuleName);
524538
void createInitializerPoisonCalls(Module &M, GlobalValue *ModuleName);
525539
size_t MinRedzoneSizeForGlobal() const {
@@ -537,6 +551,8 @@ class AddressSanitizerModule : public ModulePass {
537551
Function *AsanUnpoisonGlobals;
538552
Function *AsanRegisterGlobals;
539553
Function *AsanUnregisterGlobals;
554+
Function *AsanRegisterImageGlobals;
555+
Function *AsanUnregisterImageGlobals;
540556
};
541557

542558
// Stack poisoning does not play well with exception handling.
@@ -1272,15 +1288,37 @@ bool AddressSanitizerModule::ShouldInstrumentGlobal(GlobalVariable *G) {
12721288
return true;
12731289
}
12741290

1291+
// On Mach-O platforms, we emit global metadata in a separate section of the
1292+
// binary in order to allow the linker to properly dead strip. This is only
1293+
// supported on recent versions of ld64.
1294+
bool AddressSanitizerModule::ShouldUseMachOGlobalsSection() const {
1295+
if (!ClUseMachOGlobalsSection)
1296+
return false;
1297+
1298+
if (!TargetTriple.isOSBinFormatMachO())
1299+
return false;
1300+
1301+
if (TargetTriple.isMacOSX() && !TargetTriple.isMacOSXVersionLT(10, 11))
1302+
return true;
1303+
if (TargetTriple.isiOS() /* or tvOS */ && !TargetTriple.isOSVersionLT(9))
1304+
return true;
1305+
if (TargetTriple.isWatchOS() && !TargetTriple.isOSVersionLT(2))
1306+
return true;
1307+
1308+
return false;
1309+
}
1310+
12751311
void AddressSanitizerModule::initializeCallbacks(Module &M) {
12761312
IRBuilder<> IRB(*C);
1313+
12771314
// Declare our poisoning and unpoisoning functions.
12781315
AsanPoisonGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
12791316
kAsanPoisonGlobalsName, IRB.getVoidTy(), IntptrTy, nullptr));
12801317
AsanPoisonGlobals->setLinkage(Function::ExternalLinkage);
12811318
AsanUnpoisonGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
12821319
kAsanUnpoisonGlobalsName, IRB.getVoidTy(), nullptr));
12831320
AsanUnpoisonGlobals->setLinkage(Function::ExternalLinkage);
1321+
12841322
// Declare functions that register/unregister globals.
12851323
AsanRegisterGlobals = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
12861324
kAsanRegisterGlobalsName, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr));
@@ -1289,6 +1327,18 @@ void AddressSanitizerModule::initializeCallbacks(Module &M) {
12891327
M.getOrInsertFunction(kAsanUnregisterGlobalsName, IRB.getVoidTy(),
12901328
IntptrTy, IntptrTy, nullptr));
12911329
AsanUnregisterGlobals->setLinkage(Function::ExternalLinkage);
1330+
1331+
// Declare the functions that find globals in a shared object and then invoke
1332+
// the (un)register function on them.
1333+
AsanRegisterImageGlobals = checkSanitizerInterfaceFunction(
1334+
M.getOrInsertFunction(kAsanRegisterImageGlobalsName,
1335+
IRB.getVoidTy(), IntptrTy, nullptr));
1336+
AsanRegisterImageGlobals->setLinkage(Function::ExternalLinkage);
1337+
1338+
AsanUnregisterImageGlobals = checkSanitizerInterfaceFunction(
1339+
M.getOrInsertFunction(kAsanUnregisterImageGlobalsName,
1340+
IRB.getVoidTy(), IntptrTy, nullptr));
1341+
AsanUnregisterImageGlobals->setLinkage(Function::ExternalLinkage);
12921342
}
12931343

12941344
// This function replaces all global variables with new variables that have
@@ -1397,28 +1447,84 @@ bool AddressSanitizerModule::InstrumentGlobals(IRBuilder<> &IRB, Module &M) {
13971447
DEBUG(dbgs() << "NEW GLOBAL: " << *NewGlobal << "\n");
13981448
}
13991449

1400-
ArrayType *ArrayOfGlobalStructTy = ArrayType::get(GlobalStructTy, n);
1401-
GlobalVariable *AllGlobals = new GlobalVariable(
1402-
M, ArrayOfGlobalStructTy, false, GlobalVariable::InternalLinkage,
1403-
ConstantArray::get(ArrayOfGlobalStructTy, Initializers), "");
1450+
1451+
GlobalVariable *AllGlobals = nullptr;
1452+
GlobalVariable *RegisteredFlag = nullptr;
1453+
1454+
// On recent Mach-O platforms, we emit the global metadata in a way that
1455+
// allows the linker to properly strip dead globals.
1456+
if (ShouldUseMachOGlobalsSection()) {
1457+
// RegisteredFlag serves two purposes. First, we can pass it to dladdr()
1458+
// to look up the loaded image that contains it. Second, we can store in it
1459+
// whether registration has already occurred, to prevent duplicate
1460+
// registration.
1461+
//
1462+
// Common linkage allows us to coalesce needles defined in each object
1463+
// file so that there's only one per shared library.
1464+
RegisteredFlag = new GlobalVariable(
1465+
M, IntptrTy, false, GlobalVariable::CommonLinkage,
1466+
ConstantInt::get(IntptrTy, 0), kAsanGlobalsRegisteredFlagName);
1467+
1468+
// We also emit a structure which binds the liveness of the global
1469+
// variable to the metadata struct.
1470+
StructType *LivenessTy = StructType::get(IntptrTy, IntptrTy, nullptr);
1471+
1472+
for (size_t i = 0; i < n; i++) {
1473+
GlobalVariable *Metadata = new GlobalVariable(
1474+
M, GlobalStructTy, false, GlobalVariable::InternalLinkage,
1475+
Initializers[i], "");
1476+
Metadata->setSection("__DATA,__asan_globals,regular");
1477+
Metadata->setAlignment(1); // don't leave padding in between
1478+
1479+
auto LivenessBinder = ConstantStruct::get(LivenessTy,
1480+
Initializers[i]->getAggregateElement(0u),
1481+
ConstantExpr::getPointerCast(Metadata, IntptrTy),
1482+
nullptr);
1483+
GlobalVariable *Liveness = new GlobalVariable(
1484+
M, LivenessTy, false, GlobalVariable::InternalLinkage,
1485+
LivenessBinder, "");
1486+
Liveness->setSection("__DATA,__asan_liveness,regular,live_support");
1487+
}
1488+
} else {
1489+
// On all other platfoms, we just emit an array of global metadata
1490+
// structures.
1491+
ArrayType *ArrayOfGlobalStructTy = ArrayType::get(GlobalStructTy, n);
1492+
AllGlobals = new GlobalVariable(
1493+
M, ArrayOfGlobalStructTy, false, GlobalVariable::InternalLinkage,
1494+
ConstantArray::get(ArrayOfGlobalStructTy, Initializers), "");
1495+
}
14041496

14051497
// Create calls for poisoning before initializers run and unpoisoning after.
14061498
if (HasDynamicallyInitializedGlobals)
14071499
createInitializerPoisonCalls(M, ModuleName);
1408-
IRB.CreateCall(AsanRegisterGlobals,
1409-
{IRB.CreatePointerCast(AllGlobals, IntptrTy),
1410-
ConstantInt::get(IntptrTy, n)});
14111500

1412-
// We also need to unregister globals at the end, e.g. when a shared library
1501+
// Create a call to register the globals with the runtime.
1502+
if (ShouldUseMachOGlobalsSection()) {
1503+
IRB.CreateCall(AsanRegisterImageGlobals,
1504+
{IRB.CreatePointerCast(RegisteredFlag, IntptrTy)});
1505+
} else {
1506+
IRB.CreateCall(AsanRegisterGlobals,
1507+
{IRB.CreatePointerCast(AllGlobals, IntptrTy),
1508+
ConstantInt::get(IntptrTy, n)});
1509+
}
1510+
1511+
// We also need to unregister globals at the end, e.g., when a shared library
14131512
// gets closed.
14141513
Function *AsanDtorFunction =
14151514
Function::Create(FunctionType::get(Type::getVoidTy(*C), false),
14161515
GlobalValue::InternalLinkage, kAsanModuleDtorName, &M);
14171516
BasicBlock *AsanDtorBB = BasicBlock::Create(*C, "", AsanDtorFunction);
14181517
IRBuilder<> IRB_Dtor(ReturnInst::Create(*C, AsanDtorBB));
1419-
IRB_Dtor.CreateCall(AsanUnregisterGlobals,
1420-
{IRB.CreatePointerCast(AllGlobals, IntptrTy),
1421-
ConstantInt::get(IntptrTy, n)});
1518+
1519+
if (ShouldUseMachOGlobalsSection()) {
1520+
IRB_Dtor.CreateCall(AsanUnregisterImageGlobals,
1521+
{IRB.CreatePointerCast(RegisteredFlag, IntptrTy)});
1522+
} else {
1523+
IRB_Dtor.CreateCall(AsanUnregisterGlobals,
1524+
{IRB.CreatePointerCast(AllGlobals, IntptrTy),
1525+
ConstantInt::get(IntptrTy, n)});
1526+
}
1527+
14221528
appendToGlobalDtors(M, AsanDtorFunction, kAsanCtorAndDtorPriority);
14231529

14241530
DEBUG(dbgs() << M);

test/Instrumentation/AddressSanitizer/global_metadata.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ target triple = "x86_64-unknown-linux-gnu"
2020
; CHECK: [[FILENAME:@__asan_gen_.[0-9]+]] = private unnamed_addr constant [22 x i8] c"/tmp/asan-globals.cpp\00", align 1
2121
; CHECK: [[LOCDESCR:@__asan_gen_.[0-9]+]] = private unnamed_addr constant { [22 x i8]*, i32, i32 } { [22 x i8]* [[FILENAME]], i32 5, i32 5 }
2222

23-
; Check that location decriptors and global names were passed into __asan_register_globals:
23+
; Check that location descriptors and global names were passed into __asan_register_globals:
2424
; CHECK: i64 ptrtoint ([7 x i8]* [[VARNAME]] to i64)
2525
; CHECK: i64 ptrtoint ({ [22 x i8]*, i32, i32 }* [[LOCDESCR]] to i64)
2626

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; Test that global metadata is placed in a separate section on Mach-O platforms,
2+
; allowing dead stripping to be performed, and that the appropriate runtime
3+
; routines are invoked.
4+
5+
; RUN: opt < %s -asan -asan-module -asan-globals-live-support -S | FileCheck %s
6+
7+
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
8+
target triple = "x86_64-apple-macosx10.11.0"
9+
10+
@global = global [1 x i32] zeroinitializer, align 4
11+
12+
!llvm.asan.globals = !{!0}
13+
14+
!0 = !{[1 x i32]* @global, !1, !"global", i1 false, i1 false}
15+
!1 = !{!"test-globals.c", i32 1, i32 5}
16+
17+
18+
; Test that there is the flag global variable:
19+
; CHECK: @__asan_globals_registered = common global i64 0
20+
21+
; Find the metadata for @global:
22+
; CHECK: [[METADATA:@[0-9]+]] = internal global {{.*}} @global {{.*}} section "__DATA,__asan_globals,regular", align 1
23+
24+
; Find the liveness binder for @global and its metadata:
25+
; CHECK: @{{[0-9]+}} = internal global {{.*}} @global {{.*}} [[METADATA]] {{.*}} section "__DATA,__asan_liveness,regular,live_support"
26+
27+
; Test that __asan_register_image_globals is invoked from the constructor:
28+
; CHECK-LABEL: define internal void @asan.module_ctor
29+
; CHECK-NOT: ret
30+
; CHECK: call void @__asan_register_image_globals(i64 ptrtoint (i64* @__asan_globals_registered to i64))
31+
; CHECK: ret
32+
33+
; Test that __asan_unregister_image_globals is invoked from the destructor:
34+
; CHECK-LABEL: define internal void @asan.module_dtor
35+
; CHECK-NOT: ret
36+
; CHECK: call void @__asan_unregister_image_globals(i64 ptrtoint (i64* @__asan_globals_registered to i64))
37+
; CHECK: ret

0 commit comments

Comments
 (0)