Instrument LLVM 22 IR for AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), or ThreadSanitizer (TSan). Covers enabling sanitizers via TargetMachine/PassBuilder, adding manual shadow-memory checks, inserting UBSan runtime calls, and CMake setup.
68
81%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Use this skill when you want to add memory-safety, undefined-behavior, or data-race detection to your language runtime or compiler output.
| Approach | Use when |
|---|---|
| Pass pipeline sanitizers (ASan/TSan/MSan/UBSan) | Your compiler drives the full pipeline; easiest — just enable the pass |
| Manual IR instrumentation | You only want to check specific operations (custom UBSan-style checks) |
| Link against sanitizer runtime | Your output must be compatible with Clang's sanitizer runtimes |
The cleanest approach for a compiler that owns its optimization pipeline:
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
llvm::PassBuilder PB;
llvm::ModuleAnalysisManager MAM;
llvm::CGSCCAnalysisManager CGAM;
llvm::FunctionAnalysisManager FAM;
llvm::LoopAnalysisManager LAM;
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
llvm::ModulePassManager MPM;
// --- AddressSanitizer ---
llvm::AddressSanitizerOptions AsanOpts;
// Instrument module-level globals
MPM.addPass(llvm::ModuleAddressSanitizerPass(AsanOpts,
/*UseGlobalGC=*/false, /*UseOdrIndicator=*/true,
llvm::AsanDtorKind::Global));
// Per-function instrumentation
llvm::FunctionPassManager FPM;
FPM.addPass(llvm::AddressSanitizerPass(AsanOpts));
MPM.addPass(llvm::createModuleToFunctionPassAdaptor(std::move(FPM)));
// --- ThreadSanitizer ---
// MPM.addPass(llvm::ModuleThreadSanitizerPass());
// FPM.addPass(llvm::ThreadSanitizerPass());
MPM.run(*M, MAM);Then link the sanitizer runtime at link time:
clang output.o -fsanitize=address -o prog
# or link manually: -lasan// Disable sanitizer for a specific function (e.g., runtime internals):
F->addFnAttr(llvm::Attribute::SanitizeAddress); // opt-in to ASan
F->addFnAttr(llvm::Attribute::DisableSanitizerInstrumentation); // opt-out
// Equivalently, the LLVM IR attributes:
// define void @foo() sanitize_address { ... }
// define void @bar() no_sanitize_address { ... }Insert explicit checks for undefined behavior your language defines (e.g., integer overflow, null dereference, out-of-bounds):
// --- Null pointer check before a load ---
llvm::BasicBlock *OkBB = llvm::BasicBlock::Create(Ctx, "ptr.ok", F);
llvm::BasicBlock *TrapBB = llvm::BasicBlock::Create(Ctx, "ptr.null", F);
llvm::Value *IsNull = B.CreateIsNull(Ptr, "is.null");
B.CreateCondBr(IsNull, TrapBB, OkBB);
// Trap block: call the runtime handler or llvm.trap
B.SetInsertPoint(TrapBB);
llvm::Function *TrapFn = llvm::Intrinsic::getOrInsertDeclaration(
M, llvm::Intrinsic::trap);
B.CreateCall(TrapFn);
B.CreateUnreachable(); // terminates the block
B.SetInsertPoint(OkBB);
// ... safe to load here ...// --- Signed integer overflow check (add) ---
// Use llvm.sadd.with.overflow.*
llvm::Function *SAddOvf = llvm::Intrinsic::getOrInsertDeclaration(
M, llvm::Intrinsic::sadd_with_overflow, {B.getInt32Ty()});
llvm::Value *Result = B.CreateCall(SAddOvf, {LHS, RHS});
llvm::Value *Sum = B.CreateExtractValue(Result, 0, "sum");
llvm::Value *Overflowed = B.CreateExtractValue(Result, 1, "ovf");
B.CreateCondBr(Overflowed, TrapBB, OkBB);llvm.ubsantrap for UBSan-style abortsLLVM 22 provides a dedicated UBSan trap intrinsic that carries an error kind:
// llvm.ubsantrap(i8 kind) — kind maps to UBSan error codes
// Common kinds: 0=add-overflow, 1=sub-overflow, 2=mul-overflow,
// 11=null-ptr-deref, 23=bounds
llvm::Function *UBTrap = llvm::Intrinsic::getOrInsertDeclaration(
M, llvm::Intrinsic::ubsantrap);
B.SetInsertPoint(TrapBB);
B.CreateCall(UBTrap, {B.getInt8(11)}); // 11 = null-pointer-deref
B.CreateUnreachable();# If using Clang as the final linker, just pass sanitizer flags:
target_link_options(MyCompiler PRIVATE -fsanitize=address)
target_compile_options(MyCompiler PRIVATE -fsanitize=address)
# To compile the sanitized output with Clang:
# clang -fsanitize=address -O1 output.ll -o prog# Run ASan-instrumented binary
ASAN_OPTIONS=detect_leaks=1 ./prog
# Check what instrumentation was inserted (dump IR after passes)
opt -passes="asan-module,asan" -S output.ll -o output.asan.ll
cat output.asan.ll | grep "__asan"unreachable after llvm.trap / llvm.ubsantrap calls — two terminators in a block is a verifier error.verifyModule(*M, &errs())).a251bde
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.