https://github.com/devsw-prayas updated https://github.com/llvm/llvm-project/pull/207529
>From 0ecfdd2290a069a0fd92ac5c366672b13e580789 Mon Sep 17 00:00:00 2001 From: Prayas <[email protected]> Date: Fri, 17 Jul 2026 04:45:02 +0000 Subject: [PATCH] Remove UBSan uninitialized-read check, add MSan-based replacement The original UBSan implementation (Sanitizers.def entry, SanitizerHandler.h check, ubsan_checks.inc, ubsan_handlers.h/.cpp) is removed. In its place, detection is folded into MemorySanitizer, per thurstond's review suggestion on PR #207529 -- mirroring the existing -fsanitize-memory-param-retval precedent instead of adding a new standalone UBSan check. New MSan-side pieces: MD_msan_check_uninit_read metadata kind, CheckLocalUninitReads option threaded through MemorySanitizerOptions, a visitLoadInst hook calling insertCheckShadowOf, and driver flag -fsanitize-memory-local-address-never-taken plumbed through Options.td/CodeGenOptions.def/BackendUtil.cpp/SanitizerArgs. CGExprScalar.cpp's UninitLocalVarVisitor is rebuilt to attach check metadata after load emission instead of emitting a UBSan handler call before it. VisitBinaryOperator and VisitGCCAsmStmt erasure guards are dropped as redundant: MSan's own shadow propagation is already path-sensitive, and MSan already zeroes shadow for asm output operands. VisitUnaryOperator (address-taken exclusion) is kept -- that's a DR338/C11 scope boundary, not a defensive patch. Adds compiler-rt/test/msan/local_uninit_read.c covering positive, negative, path-sensitive, and language-gate cases. 878/940 check-ubsan, 344/352 check-msan, no regressions. --- clang/include/clang/Basic/CodeGenOptions.def | 3 + clang/include/clang/Driver/SanitizerArgs.h | 1 + clang/include/clang/Options/Options.td | 7 ++ clang/lib/CodeGen/BackendUtil.cpp | 6 +- clang/lib/CodeGen/CGExprScalar.cpp | 69 ++++++++++- clang/lib/CodeGen/CodeGenFunction.h | 5 + clang/lib/Driver/SanitizerArgs.cpp | 9 ++ compiler-rt/test/msan/local_uninit_read.c | 107 ++++++++++++++++++ llvm/include/llvm/IR/FixedMetadataKinds.def | 1 + llvm/include/llvm/IR/Instruction.h | 4 + .../Instrumentation/MemorySanitizer.h | 4 +- llvm/lib/IR/Metadata.cpp | 5 + .../Instrumentation/MemorySanitizer.cpp | 25 +++- 13 files changed, 239 insertions(+), 7 deletions(-) create mode 100644 compiler-rt/test/msan/local_uninit_read.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 7e54e75752f39..b4edb5bb3ad7b 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -283,6 +283,9 @@ ENUM_CODEGENOPT(SanitizeAddressDtor, AsanDtorKind, 2, CODEGENOPT(SanitizeMemoryParamRetval, 1, 0, Benign) ///< Enable detection of uninitialized ///< parameters and return values ///< in MemorySanitizer +CODEGENOPT(SanitizeMemoryLocalAddressNeverTaken, 1, 0, Benign) ///< Enable detection of + ///< uninitialized reads of + ///< locals never address-taken CODEGENOPT(SanitizeMemoryUseAfterDtor, 1, 0, Benign) ///< Enable use-after-delete detection ///< in MemorySanitizer CODEGENOPT(SanitizeCfiCrossDso, 1, 0, Benign) ///< Enable cross-dso support in CFI. diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index 6a01b3e36d44c..b5fca73f74451 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -44,6 +44,7 @@ class SanitizerArgs { int MsanTrackOrigins = 0; bool MsanUseAfterDtor = true; bool MsanParamRetval = true; + bool MsanCheckLocalUninitReads = false; bool CfiCrossDso = false; bool CfiICallGeneralizePointers = false; bool CfiICallNormalizeIntegers = false; diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 4974209b8db30..034dcfa9c5fb1 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2744,6 +2744,13 @@ defm sanitize_memory_param_retval PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, NegFlag<SetFalse, [], [ClangOption], "Disable">, BothFlags<[], [ClangOption], " detection of uninitialized parameters and return values">>; +defm sanitize_memory_local_address_never_taken + : BoolFOption<"sanitize-memory-local-address-never-taken", + CodeGenOpts<"SanitizeMemoryLocalAddressNeverTaken">, + DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption], " detection of reads of uninitialized local variables whose address is never taken">>; //// Note: This flag was introduced when it was necessary to distinguish between // ABI for correct codegen. This is no longer needed, but the flag is // not removed since targeting either ABI will behave the same. diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 2b755fa916e55..125724bd99eaf 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -714,8 +714,10 @@ static void addSanitizers(const Triple &TargetTriple, int TrackOrigins = CodeGenOpts.SanitizeMemoryTrackOrigins; bool Recover = CodeGenOpts.SanitizeRecover.has(Mask); - MemorySanitizerOptions options(TrackOrigins, Recover, CompileKernel, - CodeGenOpts.SanitizeMemoryParamRetval); + MemorySanitizerOptions options( + TrackOrigins, Recover, CompileKernel, + CodeGenOpts.SanitizeMemoryParamRetval, + CodeGenOpts.SanitizeMemoryLocalAddressNeverTaken); MPM.addPass(MemorySanitizerPass(options)); if (Level != OptimizationLevel::O0) { // MemorySanitizer inserts complex instrumentation that mostly follows diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 6f7b7e7d6840e..57f9c583c2afe 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -29,6 +29,7 @@ #include "clang/AST/MatrixUtils.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/DiagnosticTrap.h" @@ -294,6 +295,51 @@ static bool CanElideOverflowCheck(ASTContext &Ctx, const BinOpInfo &Op) { (2 * Ctx.getTypeSize(RHSTy)) < PromotedSize; } +/// Walks a C function body once, collecting local scalar VarDecls that are +/// never assigned and never have their address taken. These are candidates +/// for MSan's local uninitialized-read check (ISO C 6.3.2.1p2 indeterminate +/// value; the "address never taken" gate is spec-correct since DR338/C11, +/// not C23-specific). Scoped to C only — C++ lambdas, captures, and +/// aggregates are out of scope. +class UninitLocalVarVisitor + : public RecursiveASTVisitor<UninitLocalVarVisitor> { + llvm::DenseMap<const VarDecl *, bool> &Candidates; + +public: + UninitLocalVarVisitor(llvm::DenseMap<const VarDecl *, bool> &Candidates) + : Candidates(Candidates) {} + + bool VisitVarDecl(VarDecl *VD) { + if (VD->isLocalVarDecl() && !VD->isStaticLocal() && !VD->hasInit() && + VD->getType()->isScalarType()) + Candidates[VD] = true; + return true; + } + + // Deliberately no VisitBinaryOperator override to erase candidates on + // assignment. Under MSan, attaching the check to more loads is safe: + // if a path actually initializes the variable, MSan's own shadow + // tracking (genuinely path-sensitive at runtime, via phi-node shadow + // merging) reflects that and the check silently passes. Erasing on + // any assignment, anywhere in the function, made sense for the prior + // UBSan-based design (an unconditional trap with no runtime awareness + // of which path was taken) but is unnecessarily conservative here -- + // it would produce false negatives on variables that are correctly + // initialized on some paths and read uninitialized on others. + // Verified empirically: no regressions on check-msan/check-ubsan, and + // confirmed correct runtime behavior on both branches of a genuinely + // conditional assignment (warns on the uninitialized path, silent on + // the initialized one). + + bool VisitUnaryOperator(UnaryOperator *UO) { + if (UO->getOpcode() == UO_AddrOf) + if (auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) + if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) + Candidates.erase(VD); + return true; + } +}; + class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, Value*> { CodeGenFunction &CGF; @@ -608,7 +654,28 @@ class ScalarExprEmitter Value *VisitDeclRefExpr(DeclRefExpr *E) { if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) return CGF.emitScalarConstant(Constant, E); - return EmitLoadOfLValue(E); + + Value *V = EmitLoadOfLValue(E); + + if (CGF.SanOpts.has(SanitizerKind::Memory) && + CGF.CGM.getCodeGenOpts().SanitizeMemoryLocalAddressNeverTaken && + !CGF.getLangOpts().CPlusPlus) { + if (auto *VD = dyn_cast<VarDecl>(E->getDecl())) { + if (!CGF.UninitReadAnalysisDone) { + if (auto *FD = dyn_cast<FunctionDecl>(CGF.CurFuncDecl)) + if (const Stmt *Body = FD->getBody()) + UninitLocalVarVisitor(CGF.UninitReadCandidates) + .TraverseStmt(const_cast<Stmt *>(Body)); + CGF.UninitReadAnalysisDone = true; + } + if (CGF.UninitReadCandidates.count(VD)) { + if (auto *LI = dyn_cast<llvm::LoadInst>(V)) + LI->setUninitReadCheckMetadata(); + } + } + } + + return V; } Value *VisitObjCSelectorExpr(ObjCSelectorExpr *E) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e7c24f1f36f1e..643c5a415c35c 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -591,6 +591,11 @@ class CodeGenFunction : public CodeGenTypeCache { ~SanitizerScope(); }; + /// Cache for MSan local-uninitialized-read analysis. + /// Populated lazily on first scalar read in a function body. + bool UninitReadAnalysisDone = false; + llvm::DenseMap<const VarDecl *, bool> UninitReadCandidates; + /// In C++, whether we are code generating a thunk. This controls whether we /// should emit cleanups. bool CurFuncIsThunk = false; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index e813efc89073d..96fded1bd1c69 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -902,14 +902,20 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, MsanParamRetval = Args.hasFlag( options::OPT_fsanitize_memory_param_retval, options::OPT_fno_sanitize_memory_param_retval, MsanParamRetval); + MsanCheckLocalUninitReads = + Args.hasFlag(options::OPT_fsanitize_memory_local_address_never_taken, + options::OPT_fno_sanitize_memory_local_address_never_taken, + MsanCheckLocalUninitReads); } else if (AllAddedKinds & SanitizerKind::KernelMemory) { MsanUseAfterDtor = false; MsanParamRetval = Args.hasFlag( options::OPT_fsanitize_memory_param_retval, options::OPT_fno_sanitize_memory_param_retval, MsanParamRetval); + MsanCheckLocalUninitReads = false; } else { MsanUseAfterDtor = false; MsanParamRetval = false; + MsanCheckLocalUninitReads = false; } if (AllAddedKinds & SanitizerKind::MemTag) { @@ -1535,6 +1541,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, if (!MsanParamRetval) CmdArgs.push_back("-fno-sanitize-memory-param-retval"); + if (MsanCheckLocalUninitReads) + CmdArgs.push_back("-fsanitize-memory-local-address-never-taken"); + // FIXME: Pass these parameters as function attributes, not as -llvm flags. if (!TsanMemoryAccess) { CmdArgs.push_back("-mllvm"); diff --git a/compiler-rt/test/msan/local_uninit_read.c b/compiler-rt/test/msan/local_uninit_read.c new file mode 100644 index 0000000000000..484e7d5b964e4 --- /dev/null +++ b/compiler-rt/test/msan/local_uninit_read.c @@ -0,0 +1,107 @@ +// Tests the opt-in MSan local-uninitialized-read check +// (-fsanitize-memory-local-address-never-taken): flags reads of local +// scalar variables whose address is never taken, for values that are +// not always initialized before the read (ISO C 6.3.2.1p2 indeterminate +// value; the "address never taken" gate has been part of this rule +// since DR338/C11, not C23-specific). +// +// The canonical positive case below is a *dead load* (read, then +// discarded, no branch/return/store on the value) precisely because +// that pattern reaches none of MSan's other checked sinks on its own -- +// unlike `return x;`, which vanilla -fsanitize=memory already flags via +// its own return-value shadow tracking, independent of this feature. +// Using `return x;` here would not actually test this code path; this +// was confirmed empirically before writing this test. +// +// Positive: dead load of a never-initialized scalar -> warns. +// RUN: %clang_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=WARN +// +// Negative: same dead load, but flag not passed -> vanilla MSan misses it. +// RUN: %clang_msan -O0 -Wno-unused-value %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty +// +// Negative: address taken -> not flagged even though never initialized. +// RUN: %clang_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value -DADDR_TAKEN %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty +// +// Negative: initialized via GCC asm output operand -> not flagged. +// RUN: %clang_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value -DASM_INIT %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty +// +// Negative: aggregate type (struct) -> out of scope (scalars only), +// not flagged either with or without the feature. +// RUN: %clang_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value -DAGGREGATE %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty +// +// Path-sensitive: conditionally-assigned scalar, dead load. Warns only +// when the uninitialized path is actually taken at runtime (no extra +// argv), silent when the initializing path was taken (extra argv +// present). This works via MSan's own runtime shadow propagation +// (genuinely path-sensitive via phi-node shadow merging at branch +// joins) -- UninitLocalVarVisitor's own AST-level candidate detection +// is a coarse whole-function existence pre-filter, not itself path +// sensitive; it does not erase a candidate merely because it is +// assigned somewhere in the function, and relies on MSan's shadow to +// correctly reflect whether that assignment actually dominated this +// particular execution. +// +// Note: deliberately not testing __builtin_unreachable() as a +// substitute for the "always initialized" path here. Reaching a branch +// marked unreachable at runtime is undefined behavior independent of +// this feature; any apparent pass/fail from doing so is a coincidence +// of code layout, not attributable to this check, and was confirmed +// unreliable before this test was written. +// RUN: %clang_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value -DCONDITIONAL %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=WARN +// RUN: %run %t extra_arg 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty +// +// Negative: C++ excluded entirely by the language gate +// (!CGF.getLangOpts().CPlusPlus in VisitDeclRefExpr). +// RUN: %clangxx_msan -fsanitize-memory-local-address-never-taken -O0 -Wno-unused-value -x c++ %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=SILENT --allow-empty + +#if defined(ADDR_TAKEN) +int main() { + int x; + int *p = &x; + (void)p; + x; + return 0; +} +#elif defined(ASM_INIT) +int main() { + int x; +#if defined(__x86_64__) || defined(__i386__) + __asm__("movl $0, %0" : "=r"(x)); +#else + x = 0; +#endif + x; + return 0; +} +#elif defined(AGGREGATE) +struct S { int a; }; +int main() { + struct S s; + s.a; + return 0; +} +#elif defined(CONDITIONAL) +int main(int argc, char **argv) { + int x; + if (argc > 1) + x = 5; + x; + return 0; +} +#else +int main() { + int x; + x; + return 0; +} +#endif + +// WARN: WARNING: MemorySanitizer: use-of-uninitialized-value +// SILENT-NOT: MemorySanitizer diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index cb6ebfb5c0fb5..8d23aadf0d2c7 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -66,4 +66,5 @@ LLVM_FIXED_MD_KIND(MD_unique_id, "guid", 51) LLVM_FIXED_MD_KIND(MD_mem_cache_hint, "mem.cache_hint", 52) LLVM_FIXED_MD_KIND(MD_block_uniformity_profile, "block.uniformity.profile", 53) LLVM_FIXED_MD_KIND(MD_callgraph, "callgraph", 54) +LLVM_FIXED_MD_KIND(MD_msan_check_uninit_read, "msan.check-uninit-read", 55) diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h index 86843b339117f..6c8b5276c89a2 100644 --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -534,6 +534,10 @@ class Instruction : public User, /// Sets the nosanitize metadata on this instruction. LLVM_ABI void setNoSanitizeMetadata(); + /// Marks this load as needing an MSan uninitialized-read check + /// (locals whose address is never taken). + LLVM_ABI void setUninitReadCheckMetadata(); + /// Retrieve total raw weight values of a branch. /// Returns true on success with profile total weights filled in. /// Returns false if no metadata was found. diff --git a/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h index b42f2e21aa52f..60d738f5c2ac5 100644 --- a/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h +++ b/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h @@ -27,11 +27,13 @@ struct MemorySanitizerOptions { MemorySanitizerOptions(int TrackOrigins, bool Recover, bool Kernel) : MemorySanitizerOptions(TrackOrigins, Recover, Kernel, false) {} LLVM_ABI MemorySanitizerOptions(int TrackOrigins, bool Recover, bool Kernel, - bool EagerChecks); + bool EagerChecks, + bool CheckLocalUninitReads = false); bool Kernel; int TrackOrigins; bool Recover; bool EagerChecks; + bool CheckLocalUninitReads; }; /// A module pass for msan instrumentation. diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp index 491c788fc4445..076734ac67c2c 100644 --- a/llvm/lib/IR/Metadata.cpp +++ b/llvm/lib/IR/Metadata.cpp @@ -1862,6 +1862,11 @@ void Instruction::setNoSanitizeMetadata() { llvm::MDNode::get(getContext(), {})); } +void Instruction::setUninitReadCheckMetadata() { + setMetadata(llvm::LLVMContext::MD_msan_check_uninit_read, + llvm::MDNode::get(getContext(), {})); +} + void Instruction::getAllMetadataImpl( SmallVectorImpl<std::pair<unsigned, MDNode *>> &Result) const { Result.clear(); diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index cdce2e039154f..25ffe4eb0d998 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -343,6 +343,12 @@ static cl::opt<bool> ClEagerChecks( cl::desc("check arguments and return values at function call boundaries"), cl::Hidden, cl::init(false)); +static cl::opt<bool> ClCheckLocalUninitReads( + "msan-check-local-uninit-reads", + cl::desc("check reads of local variables whose address is never " + "taken for uninitialized values"), + cl::Hidden, cl::init(false)); + static cl::opt<bool> ClDumpStrictInstructions( "msan-dump-strict-instructions", cl::desc("print out instructions with default strict semantics i.e.," @@ -616,7 +622,8 @@ class MemorySanitizer { public: MemorySanitizer(Module &M, MemorySanitizerOptions Options) : CompileKernel(Options.Kernel), TrackOrigins(Options.TrackOrigins), - Recover(Options.Recover), EagerChecks(Options.EagerChecks) { + Recover(Options.Recover), EagerChecks(Options.EagerChecks), + CheckLocalUninitReads(Options.CheckLocalUninitReads) { initializeModule(M); } @@ -654,6 +661,7 @@ class MemorySanitizer { int TrackOrigins; bool Recover; bool EagerChecks; + bool CheckLocalUninitReads; Triple TargetTriple; LLVMContext *C; @@ -778,11 +786,14 @@ template <class T> T getOptOrDefault(const cl::opt<T> &Opt, T Default) { } // end anonymous namespace MemorySanitizerOptions::MemorySanitizerOptions(int TO, bool R, bool K, - bool EagerChecks) + bool EagerChecks, + bool CheckLocalUninitReads) : Kernel(getOptOrDefault(ClEnableKmsan, K)), TrackOrigins(getOptOrDefault(ClTrackOrigins, Kernel ? 2 : TO)), Recover(getOptOrDefault(ClKeepGoing, Kernel || R)), - EagerChecks(getOptOrDefault(ClEagerChecks, EagerChecks)) {} + EagerChecks(getOptOrDefault(ClEagerChecks, EagerChecks)), + CheckLocalUninitReads( + getOptOrDefault(ClCheckLocalUninitReads, CheckLocalUninitReads)) {} PreservedAnalyses MemorySanitizerPass::run(Module &M, ModuleAnalysisManager &AM) { @@ -2371,6 +2382,10 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> { void visitLoadInst(LoadInst &I) { assert(I.getType()->isSized() && "Load type must have size"); assert(!I.getMetadata(LLVMContext::MD_nosanitize)); + // Captured before any shadow/origin instructions are spliced in after I, + // so this stays a valid anchor for a check that depends on I's own + // shadow (which NextNodeIRBuilder inserts *after* I, not before). + Instruction *UninitReadCheckAnchor = I.getNextNode(); NextNodeIRBuilder IRB(&I); Type *ShadowTy = getShadowTy(&I); Value *Addr = I.getPointerOperand(); @@ -2388,6 +2403,10 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> { if (ClCheckAccessAddress) insertCheckShadowOf(I.getPointerOperand(), &I); + if (MS.CheckLocalUninitReads && + I.getMetadata(LLVMContext::MD_msan_check_uninit_read)) + insertCheckShadowOf(&I, UninitReadCheckAnchor); + if (I.isAtomic()) I.setOrdering(addAcquireOrdering(I.getOrdering())); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
