Index: projects/compiler-rt/lib/asan/asan_interface_internal.h =================================================================== --- projects/compiler-rt/lib/asan/asan_interface_internal.h +++ projects/compiler-rt/lib/asan/asan_interface_internal.h @@ -33,6 +33,14 @@ SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); #define __asan_init __asan_init_v3 + // This structure is used to describe the source location of a place where + // global was defined. + struct __asan_global_source_location { + const char *filename; + int line_no; + int column_no; + }; + // This structure describes an instrumented global variable. struct __asan_global { uptr beg; // The address of the global. @@ -42,6 +50,8 @@ const char *module_name; // Module name as a C string. This pointer is a // unique identifier of a module. uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. + __asan_global_source_location *location; // Source location of a global, + // or NULL if it is unknown. }; // These two functions should be called by the instrumented code. Index: projects/compiler-rt/lib/asan/asan_report.cc =================================================================== --- projects/compiler-rt/lib/asan/asan_report.cc +++ projects/compiler-rt/lib/asan/asan_report.cc @@ -212,6 +212,26 @@ (char *)g.beg); } +static const char *GlobalFilename(const __asan_global &g) { + const char *res = g.module_name; + // Prefer the filename from source location, if is available. + if (g.location) + res = g.location->filename; + CHECK(res); + return res; +} + +static void PrintGlobalLocation(InternalScopedString *str, + const __asan_global &g) { + str->append("%s", GlobalFilename(g)); + if (!g.location) + return; + if (g.location->line_no) + str->append(":%d", g.location->line_no); + if (g.location->column_no) + str->append(":%d", g.location->column_no); +} + bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, const __asan_global &g) { static const uptr kMinimalDistanceFromAnotherGlobal = 64; @@ -232,8 +252,10 @@ // Can it happen? str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); } - str.append(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", - MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); + str.append(" of global variable '%s' defined in '", + MaybeDemangleGlobalName(g.name)); + PrintGlobalLocation(&str, g); + str.append("' (0x%zx) of size %zu\n", g.beg, g.size); str.append("%s", d.EndLocation()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); @@ -741,11 +763,14 @@ Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); Printf("%s", d.EndWarning()); - Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1->module_name); - Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2->module_name); + InternalScopedString g1_loc(256), g2_loc(256); + PrintGlobalLocation(&g1_loc, *g1); + PrintGlobalLocation(&g2_loc, *g2); + Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1_loc.data()); + Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2_loc.data()); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=detect_odr_violation=0\n"); - ReportErrorSummary("odr-violation", g1->module_name, 0, g1->name); + ReportErrorSummary("odr-violation", g1_loc.data(), 0, g1->name); } // ----------------------- CheckForInvalidPointerPair ----------- {{{1 Index: projects/compiler-rt/test/asan/TestCases/global-location.cc =================================================================== --- /dev/null +++ projects/compiler-rt/test/asan/TestCases/global-location.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %run %t g 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=GLOB +// RUN: not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CLASS_STATIC +// RUN: not %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=FUNC_STATIC +// RUN: not %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=LITERAL + +// CHECK: AddressSanitizer: global-buffer-overflow + +#include + +class C { + public: + static int array[10]; +}; + +int global[10]; +// GLOB: 0x{{.*}} is located 4 bytes to the right of global variable 'global' defined in '{{.*}}/global-location.cc:[[@LINE-1]]:5' {{.*}} of size 40 +int C::array[10]; +// CLASS_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'C::array' defined in '{{.*}}/global-location.cc:[[@LINE-1]]:8' {{.*}} of size 40 + +int main(int argc, char **argv) { + int one = argc - 1; + switch (argv[1][0]) { + case 'g': return global[one * 11]; + case 'c': return C::array[one * 11]; + case 'f': + static int array[10]; + // FUNC_STATIC: 0x{{.*}} is located 4 bytes to the right of global variable 'main::array' defined in '{{.*}}/global-location.cc:[[@LINE-1]]:16' {{.*}} of size 40 + memset(array, 0, 10); + return array[one * 11]; + case 'l': + const char *str = "0123456789"; + // LITERAL: 0x{{.*}} is located 0 bytes to the right of global variable {{.*}} defined in '{{.*}}/global-location.cc:[[@LINE-1]]:23' {{.*}} of size 11 + return str[one * 11]; + } + return 0; +} + +// CHECK: SUMMARY: AddressSanitizer: global-buffer-overflow Index: tools/clang/lib/CodeGen/CGDecl.cpp =================================================================== --- tools/clang/lib/CodeGen/CGDecl.cpp +++ tools/clang/lib/CodeGen/CGDecl.cpp @@ -345,6 +345,8 @@ DMEntry = castedAddr; CGM.setStaticLocalDeclAddress(&D, castedAddr); + CGM.reportGlobalToASan(var, D.getLocation()); + // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); if (DI && Index: tools/clang/lib/CodeGen/CodeGenModule.h =================================================================== --- tools/clang/lib/CodeGen/CodeGenModule.h +++ tools/clang/lib/CodeGen/CodeGenModule.h @@ -1001,6 +1001,9 @@ const SanitizerOptions &getSanOpts() const { return SanOpts; } + void reportGlobalToASan(llvm::GlobalVariable *GV, SourceLocation Loc, + bool IsDynInit = false); + void addDeferredVTable(const CXXRecordDecl *RD) { DeferredVTables.push_back(RD); } Index: tools/clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- tools/clang/lib/CodeGen/CodeGenModule.cpp +++ tools/clang/lib/CodeGen/CodeGenModule.cpp @@ -1929,16 +1929,7 @@ if (NeedsGlobalCtor || NeedsGlobalDtor) EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); - // If we are compiling with ASan, add metadata indicating dynamically - // initialized (and not blacklisted) globals. - if (SanOpts.Address && NeedsGlobalCtor && - !SanitizerBlacklist->isIn(*GV, "init")) { - llvm::NamedMDNode *DynamicInitializers = TheModule.getOrInsertNamedMetadata( - "llvm.asan.dynamically_initialized_globals"); - llvm::Value *GlobalToAdd[] = { GV }; - llvm::MDNode *ThisGlobal = llvm::MDNode::get(VMContext, GlobalToAdd); - DynamicInitializers->addOperand(ThisGlobal); - } + reportGlobalToASan(GV, D->getLocation(), NeedsGlobalCtor); // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) @@ -1946,6 +1937,51 @@ DI->EmitGlobalVariable(GV, D); } +void CodeGenModule::reportGlobalToASan(llvm::GlobalVariable *GV, + SourceLocation Loc, bool IsDynInit) { + if (!SanOpts.Address) + return; + IsDynInit &= !SanitizerBlacklist->isIn(*GV, "init"); + bool IsBlacklisted = SanitizerBlacklist->isIn(*GV); + + llvm::LLVMContext &LLVMCtx = TheModule.getContext(); + + llvm::GlobalVariable *LocDescr = nullptr; + if (!IsBlacklisted) { + // Don't generate source location if a global is blacklisted - it won't + // be instrumented anyway. + PresumedLoc PLoc = Context.getSourceManager().getPresumedLoc(Loc); + if (PLoc.isValid()) { + llvm::Constant *LocData[] = { + GetAddrOfConstantCString(PLoc.getFilename()), + llvm::ConstantInt::get(llvm::Type::getInt32Ty(LLVMCtx), PLoc.getLine()), + llvm::ConstantInt::get(llvm::Type::getInt32Ty(LLVMCtx), + PLoc.getColumn()), + }; + auto LocStruct = llvm::ConstantStruct::getAnon(LocData); + LocDescr = new llvm::GlobalVariable(TheModule, LocStruct->getType(), true, + llvm::GlobalValue::PrivateLinkage, + LocStruct, ".asan_loc_descr"); + LocDescr->setUnnamedAddr(true); + // Add LocDescr to llvm.compiler.used, so that it won't be removed by + // the optimizer before the ASan instrumentation pass. + addCompilerUsedGlobal(LocDescr); + } + } + + llvm::Value *GlobalMetadata[] = { + GV, + LocDescr, + llvm::ConstantInt::get(llvm::Type::getInt1Ty(LLVMCtx), IsDynInit), + llvm::ConstantInt::get(llvm::Type::getInt1Ty(LLVMCtx), IsBlacklisted) + }; + + llvm::MDNode *ThisGlobal = llvm::MDNode::get(VMContext, GlobalMetadata); + llvm::NamedMDNode *AsanGlobals = + TheModule.getOrInsertNamedMetadata("llvm.asan.globals"); + AsanGlobals->addOperand(ThisGlobal); +} + static bool isVarDeclStrongDefinition(const VarDecl *D, bool NoCommon) { // Don't give variables common linkage if -fno-common was specified unless it // was overridden by a NoCommon attribute. @@ -2750,6 +2786,8 @@ auto GV = GenerateStringLiteral(C, LT, *this, GlobalVariableName, Alignment); if (Entry) Entry->setValue(GV); + + reportGlobalToASan(GV, S->getStrTokenLoc(0)); return GV; } Index: tools/clang/test/CodeGen/asan-globals.cpp =================================================================== --- /dev/null +++ tools/clang/test/CodeGen/asan-globals.cpp @@ -0,0 +1,23 @@ +// RUN: echo "global:*blacklisted_global*" > %t.blacklist +// RUN: %clang_cc1 -fsanitize=address -fsanitize-blacklist=%t.blacklist -emit-llvm -o - %s | FileCheck %s +// REQUIRES: shell + +int global; +// CHECK: [[GLOBAL_LOC:@.asan_loc_descr[0-9]*]] = private unnamed_addr constant {{.*}} i32 [[@LINE-1]], i32 5 +int dyn_init_global = global; +// CHECK: [[DYN_INIT_LOC:@.asan_loc_descr[0-9]*]] = {{.*}} i32 [[@LINE-1]], i32 5 +int blacklisted_global; + +void func() { + static int static_var = 0; + // CHECK: [[STATIC_LOC:@.asan_loc_descr[0-9]*]] = {{.*}} i32 [[@LINE-1]], i32 14 + const char *literal = "Hello, world!"; + // CHECK: [[LITERAL_LOC:@.asan_loc_descr[0-9]*]] = {{.*}} i32 [[@LINE-1]], i32 25 +} + +// CHECK: !llvm.asan.globals = !{![[GLOBAL:[0-9]+]], ![[DYN_INIT_GLOBAL:[0-9]+]], ![[BLACKLISTED_GLOBAL:[0-9]+]], ![[STATIC_VAR:[0-9]+]], ![[LITERAL:[0-9]+]]} +// CHECK: ![[GLOBAL]] = metadata !{{{.*}} [[GLOBAL_LOC]], i1 false, i1 false} +// CHECK: ![[DYN_INIT_GLOBAL]] = metadata !{{{.*}} [[DYN_INIT_LOC]], i1 true, i1 false} +// CHECK: ![[BLACKLISTED_GLOBAL]] = metadata !{{{.*}}, null, i1 false, i1 true} +// CHECK: ![[STATIC_VAR]] = metadata !{{{.*}} [[STATIC_LOC]], i1 false, i1 false} +// CHECK: ![[LITERAL]] = metadata !{{{.*}} [[LITERAL_LOC]], i1 false, i1 false} Index: tools/clang/test/CodeGen/sanitize-init-order.cpp =================================================================== --- tools/clang/test/CodeGen/sanitize-init-order.cpp +++ tools/clang/test/CodeGen/sanitize-init-order.cpp @@ -27,7 +27,12 @@ // Check that ASan init-order checking ignores structs with trivial default // constructor. -// CHECK: !llvm.asan.dynamically_initialized_globals = !{[[GLOB:![0-9]+]]} -// CHECK: [[GLOB]] = metadata !{%struct.PODWithCtorAndDtor - -// BLACKLIST-NOT: llvm.asan.dynamically_initialized_globals +// CHECK: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]]]} +// CHECK: ![[GLOB_1]] = metadata !{%struct.PODStruct* {{.*}}, i1 false, i1 false} +// CHECK: ![[GLOB_2]] = metadata !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false} +// CHECK: ![[GLOB_3]] = metadata !{%struct.PODWithCtorAndDtor* {{.*}}, i1 true, i1 false} + +// BLACKLIST: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]]]} +// BLACKLIST: ![[GLOB_1]] = metadata !{%struct.PODStruct* {{.*}}, i1 false, i1 false} +// BLACKLIST: ![[GLOB_2]] = metadata !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false} +// BLACKLIST: ![[GLOB_3]] = metadata !{%struct.PODWithCtorAndDtor* {{.*}}, i1 false, i1 false}