static_cast<const PassManagerBuilderWrapper &>(Builder);
const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts();
bool Recover = CGOpts.SanitizeRecover.has(SanitizerKind::HWAddress);
- PM.add(
- createHWAddressSanitizerLegacyPassPass(/*CompileKernel*/ false, Recover));
+ PM.add(createHWAddressSanitizerLegacyPassPass(
+ /*CompileKernel*/ false, Recover,
+ /*DisableOptimization*/ CGOpts.OptimizationLevel == 0));
}
static void addKernelHWAddressSanitizerPasses(const PassManagerBuilder &Builder,
- legacy::PassManagerBase &PM) {
+ legacy::PassManagerBase &PM) {
+ const PassManagerBuilderWrapper &BuilderWrapper =
+ static_cast<const PassManagerBuilderWrapper &>(Builder);
+ const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts();
PM.add(createHWAddressSanitizerLegacyPassPass(
- /*CompileKernel*/ true, /*Recover*/ true));
+ /*CompileKernel*/ true, /*Recover*/ true,
+ /*DisableOptimization*/ CGOpts.OptimizationLevel == 0));
}
static void addGeneralOptsForMemorySanitizer(const PassManagerBuilder &Builder,
auto HWASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
if (LangOpts.Sanitize.has(Mask)) {
bool Recover = CodeGenOpts.SanitizeRecover.has(Mask);
- MPM.addPass(HWAddressSanitizerPass(CompileKernel, Recover));
+ MPM.addPass(HWAddressSanitizerPass(
+ CompileKernel, Recover,
+ /*DisableOptimization=*/CodeGenOpts.OptimizationLevel == 0));
}
};
HWASanPass(SanitizerKind::HWAddress, false);
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/StackSafetyAnalysis.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
cl::desc("instrument stack (allocas)"),
cl::Hidden, cl::init(true));
+static cl::opt<bool>
+ ClUseStackSafety("hwasan-use-stack-safety", cl::Hidden, cl::init(true),
+ cl::Hidden, cl::desc("Use Stack Safety analysis results"),
+ cl::Optional);
+
static cl::opt<bool> ClUARRetagToZero(
"hwasan-uar-retag-to-zero",
cl::desc("Clear alloca tags before returning from the function to allow "
#endif
}
+bool shouldUseStackSafetyAnalysis(const Triple &TargetTriple,
+ bool DisableOptimization) {
+ auto StackSafety = ClUseStackSafety.getNumOccurrences()
+ ? ClUseStackSafety
+ : !DisableOptimization;
+ return shouldInstrumentStack(TargetTriple) && StackSafety;
+// No one should use the option directly.
+#pragma GCC poison ClUseStackSafety
+}
/// An instrumentation pass implementing detection of addressability bugs
/// using tagged pointers.
class HWAddressSanitizer {
public:
- HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover) : M(M) {
+ HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover,
+ const StackSafetyGlobalInfo *SSI)
+ : M(M), SSI(SSI) {
this->Recover = ClRecover.getNumOccurrences() > 0 ? ClRecover : Recover;
this->CompileKernel = ClEnableKhwasan.getNumOccurrences() > 0
? ClEnableKhwasan
initializeModule();
}
+ void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; }
+
bool sanitizeFunction(Function &F);
void initializeModule();
void createHwasanCtorComdat();
private:
LLVMContext *C;
Module &M;
+ const StackSafetyGlobalInfo *SSI;
Triple TargetTriple;
FunctionCallee HWAsanMemmove, HWAsanMemcpy, HWAsanMemset;
FunctionCallee HWAsanHandleVfork;
static char ID;
explicit HWAddressSanitizerLegacyPass(bool CompileKernel = false,
- bool Recover = false)
+ bool Recover = false,
+ bool DisableOptimization = false)
: FunctionPass(ID), CompileKernel(CompileKernel), Recover(Recover) {
initializeHWAddressSanitizerLegacyPassPass(
*PassRegistry::getPassRegistry());
StringRef getPassName() const override { return "HWAddressSanitizer"; }
bool doInitialization(Module &M) override {
- HWASan = std::make_unique<HWAddressSanitizer>(M, CompileKernel, Recover);
+ HWASan = std::make_unique<HWAddressSanitizer>(M, CompileKernel, Recover,
+ /*SSI=*/nullptr);
return true;
}
bool runOnFunction(Function &F) override {
+ if (shouldUseStackSafetyAnalysis(Triple(F.getParent()->getTargetTriple()),
+ DisableOptimization)) {
+ // We cannot call getAnalysis in doInitialization, that would cause a
+ // crash as the required analyses are not initialized yet.
+ HWASan->setSSI(
+ &getAnalysis<StackSafetyGlobalInfoWrapperPass>().getResult());
+ }
return HWASan->sanitizeFunction(F);
}
return false;
}
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ if (!DisableOptimization)
+ AU.addRequired<StackSafetyGlobalInfoWrapperPass>();
+ }
+
private:
std::unique_ptr<HWAddressSanitizer> HWASan;
bool CompileKernel;
bool Recover;
+ bool DisableOptimization;
};
} // end anonymous namespace
"HWAddressSanitizer: detect memory bugs using tagged addressing.", false,
false)
-FunctionPass *llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel,
- bool Recover) {
+FunctionPass *
+llvm::createHWAddressSanitizerLegacyPassPass(bool CompileKernel, bool Recover,
+ bool DisableOptimization) {
assert(!CompileKernel || Recover);
- return new HWAddressSanitizerLegacyPass(CompileKernel, Recover);
+ return new HWAddressSanitizerLegacyPass(CompileKernel, Recover,
+ DisableOptimization);
}
-HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover)
- : CompileKernel(CompileKernel), Recover(Recover) {}
+HWAddressSanitizerPass::HWAddressSanitizerPass(bool CompileKernel, bool Recover,
+ bool DisableOptimization)
+ : CompileKernel(CompileKernel), Recover(Recover),
+ DisableOptimization(DisableOptimization) {}
PreservedAnalyses HWAddressSanitizerPass::run(Module &M,
ModuleAnalysisManager &MAM) {
- HWAddressSanitizer HWASan(M, CompileKernel, Recover);
+ const StackSafetyGlobalInfo *SSI = nullptr;
+ if (shouldUseStackSafetyAnalysis(llvm::Triple(M.getTargetTriple()),
+ DisableOptimization))
+ SSI = &MAM.getResult<StackSafetyGlobalAnalysis>(M);
+ HWAddressSanitizer HWASan(M, CompileKernel, Recover, SSI);
bool Modified = false;
for (Function &F : M)
Modified |= HWASan.sanitizeFunction(F);
// dynamic alloca instrumentation for them as well.
!AI.isUsedWithInAlloca() &&
// swifterror allocas are register promoted by ISel
- !AI.isSwiftError());
+ !AI.isSwiftError()) &&
+ // safe allocas are not interesting
+ !(SSI && SSI->isSafe(AI));
}
bool HWAddressSanitizer::sanitizeFunction(Function &F) {
--- /dev/null
+; RUN: opt -hwasan -hwasan-use-stack-safety=1 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=SAFETY
+; RUN: opt -hwasan -hwasan-use-stack-safety=0 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=NOSAFETY
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+; Check a safe alloca to ensure it does not get a tag.
+define i32 @test_load(i32* %a) sanitize_hwaddress {
+entry:
+ ; NOSAFETY: call {{.*}}__hwasan_generate_tag
+ ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag
+ %buf.sroa.0 = alloca i8, align 4
+ call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0)
+ store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8
+ call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0)
+ ret i32 0
+}
+
+; Check a non-safe alloca to ensure it gets a tag.
+define i32 @test_use(i32* %a) sanitize_hwaddress {
+entry:
+ ; NOSAFETY: call {{.*}}__hwasan_generate_tag
+ ; SAFETY: call {{.*}}__hwasan_generate_tag
+ %buf.sroa.0 = alloca i8, align 4
+ call void @use(i8* nonnull %buf.sroa.0)
+ call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0)
+ store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8
+ call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0)
+ ret i32 0
+}
+
+; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
+
+; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)
+
+declare void @use(i8* nocapture)
+
+!8 = !{!9, !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C/C++ TBAA"}