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

Reply via email to