https://github.com/vgvassilev created 
https://github.com/llvm/llvm-project/pull/196894

__clang_Interpreter_SetValueNoAlloc receives the runtime value through a 
varargs ABI: the JIT-emitted caller copies real bits into the varargs slot, and 
the runtime callee reads them via va_arg and stores them in the user-provided 
Value. MSan tracks parameter shadow through TLS, but the JIT does not emit MSan 
TLS shadow setup for its calls, so the varargs source reads as uninitialized.

The end result is correct bits with an uninit shadow on the stored Value; 
downstream reads (V.getLongDouble(), V.convertTo<T>()) then report 
use-of-uninitialized-value. The bits are real -- only the shadow is wrong.

Unpoison the written Value before returning, gated on 
__has_feature(memory_sanitizer) so non-MSan builds compile out to nothing.

>From 4c7cada1f283971401f7b34f6f2a6cfcb7d5aeed Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <[email protected]>
Date: Mon, 11 May 2026 07:18:19 +0000
Subject: [PATCH] [clang-repl] Unpoison Value in SetValueNoAlloc to clear MSan
 varargs taint.

__clang_Interpreter_SetValueNoAlloc receives the runtime value
through a varargs ABI: the JIT-emitted caller copies real bits into
the varargs slot, and the runtime callee reads them via va_arg and
stores them in the user-provided Value. MSan tracks parameter
shadow through TLS, but the JIT does not emit MSan TLS shadow
setup for its calls, so the varargs source reads as uninitialized.

The end result is correct bits with an uninit shadow on the stored
Value; downstream reads (V.getLongDouble(), V.convertTo<T>()) then
report use-of-uninitialized-value. The bits are real -- only the
shadow is wrong.

Unpoison the written Value before returning, gated on
__has_feature(memory_sanitizer) so non-MSan builds compile out
to nothing.
---
 .../Interpreter/InterpreterValuePrinter.cpp   | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp 
b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 1754e7812469a..2a00a86a2838e 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -31,6 +31,21 @@
 #include <sstream>
 #include <string>
 
+// MSan cannot track value shadow across the JIT/native varargs boundary in
+// __clang_Interpreter_SetValueNoAlloc: the JIT-emitted caller does not set
+// up MSan TLS shadow, so writes via va_arg propagate uninit shadow into the
+// resulting Value. Unpoison the written Value before returning.
+#if defined(__has_feature)
+#  if __has_feature(memory_sanitizer)
+#    include <sanitizer/msan_interface.h>
+#    define CLANG_INTERP_MSAN_UNPOISON(p, n) __msan_unpoison((p), (n))
+#  else
+#    define CLANG_INTERP_MSAN_UNPOISON(p, n) ((void)0)
+#  endif
+#else
+#  define CLANG_INTERP_MSAN_UNPOISON(p, n) ((void)0)
+#endif
+
 #define DEBUG_TYPE "interp-value"
 
 using namespace clang;
@@ -651,8 +666,10 @@ __clang_Interpreter_SetValueNoAlloc(void *This, void 
*OutVal, void *OpaqueType,
   Value &VRef = *(Value *)OutVal;
   Interpreter *I = static_cast<Interpreter *>(This);
   VRef = Value(I, OpaqueType);
-  if (VRef.isVoid())
+  if (VRef.isVoid()) {
+    CLANG_INTERP_MSAN_UNPOISON(OutVal, sizeof(Value));
     return;
+  }
 
   va_list args;
   va_start(args, /*last named param*/ OpaqueType);
@@ -721,6 +738,7 @@ __clang_Interpreter_SetValueNoAlloc(void *This, void 
*OutVal, void *OpaqueType,
     }
   }
   va_end(args);
+  CLANG_INTERP_MSAN_UNPOISON(OutVal, sizeof(Value));
 }
 }
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to