Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -475,6 +475,7 @@ void instrumentMemIntrinsic(MemIntrinsic *MI); Value *memToShadow(Value *Shadow, IRBuilder<> &IRB); bool runOnFunction(Function &F) override; + void maybeMarkCallNoInline(CallInst *CI, const TargetLibraryInfo *TLI); bool maybeInsertAsanInitAtFunctionEntry(Function &F); void markEscapedLocalAllocas(Function &F); bool doInitialization(Module &M) override; @@ -1751,6 +1752,8 @@ bool IsWrite; unsigned Alignment; uint64_t TypeSize; + const TargetLibraryInfo *TLI = + &getAnalysis().getTLI(); // Fill the set of memory operations to instrument. for (auto &BB : F) { @@ -1779,6 +1782,9 @@ TempsToInstrument.clear(); if (CS.doesNotReturn()) NoReturnCalls.push_back(CS.getInstruction()); } + if (CallInst *CI = dyn_cast(&Inst)) { + maybeMarkCallNoInline(CI, TLI); + } continue; } ToInstrument.push_back(&Inst); @@ -1791,8 +1797,6 @@ CompileKernel || (ClInstrumentationWithCallsThreshold >= 0 && ToInstrument.size() > (unsigned)ClInstrumentationWithCallsThreshold); - const TargetLibraryInfo *TLI = - &getAnalysis().getTLI(); const DataLayout &DL = F.getParent()->getDataLayout(); ObjectSizeOffsetVisitor ObjSizeVis(DL, TLI, F.getContext(), /*RoundToAlign=*/true); @@ -1833,6 +1837,31 @@ return res; } +// CodeGen has special handling for some string functions that may replace +// them with target-specific intrinsics. Since that'd skip our interceptors, +// and thus make us miss some memory accesses, we mark affected calls +// as NoBuiltin, which will disable optimization in CodeGen. +void AddressSanitizer::maybeMarkCallNoInline(CallInst *CI, + const TargetLibraryInfo *TLI) { + Function *F = CI->getCalledFunction(); + LibFunc::Func Func; + if (!F || F->hasLocalLinkage() || !F->hasName() || + !TLI->getLibFunc(F->getName(), Func)) + return; + switch (Func) { + default: break; + case LibFunc::memcmp: + case LibFunc::memchr: + case LibFunc::strcpy: + case LibFunc::stpcpy: + case LibFunc::strcmp: + case LibFunc::strlen: + case LibFunc::strnlen: + CI->addAttribute(AttributeSet::FunctionIndex, Attribute::NoBuiltin); + break; + } +} + // Workaround for bug 11395: we don't want to instrument stack in functions // with large assembly blobs (32-bit only), otherwise reg alloc may crash. // FIXME: remove once the bug 11395 is fixed. Index: test/Instrumentation/AddressSanitizer/str-nobuiltin.ll =================================================================== --- /dev/null +++ test/Instrumentation/AddressSanitizer/str-nobuiltin.ll @@ -0,0 +1,33 @@ +; Test marking string functions as nobuiltin in address sanitizer. +; +; RUN: opt < %s -asan -asan-module -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" +target triple = "x86_64-unknown-linux-gnu" + +declare i8* @memchr(i8* %a, i32 %b, i64 %c) +declare i32 @memcmp(i8* %a, i8* %b, i64 %c) +declare i32 @strcmp(i8* %a, i8* %b) +declare i8* @strcpy(i8* %a, i8* %b) +declare i8* @stpcpy(i8* %a, i8* %b) +declare i64 @strlen(i8* %a) +declare i64 @strnlen(i8* %a, i64 %b) + +; CHECK: call{{.*}}@memchr{{.*}} #[[ATTR:[0-9]+]] +; CHECK: call{{.*}}@memcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcmp{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@stpcpy{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strlen{{.*}} #[[ATTR]] +; CHECK: call{{.*}}@strnlen{{.*}} #[[ATTR]] +; attributes #[[ATTR]] = { nobuiltin } + +define void @f1(i8* %a, i8* %b) nounwind uwtable sanitize_address { + tail call i8* @memchr(i8* %a, i32 1, i64 12) + tail call i32 @memcmp(i8* %a, i8* %b, i64 12) + tail call i32 @strcmp(i8* %a, i8* %b) + tail call i8* @strcpy(i8* %a, i8* %b) + tail call i8* @stpcpy(i8* %a, i8* %b) + tail call i64 @strlen(i8* %a) + tail call i64 @strnlen(i8* %a, i64 12) + ret void +}