Index: include/sanitizer/msan_interface.h =================================================================== --- include/sanitizer/msan_interface.h +++ include/sanitizer/msan_interface.h @@ -42,6 +42,9 @@ contents). */ void __msan_unpoison_string(const volatile char *a); + /* Make first n parameters of the next function call fully initialized. */ + void __msan_unpoison_param(size_t n); + /* Make memory region fully uninitialized (without changing its contents). This is a legacy interface that does not update origin information. Use __msan_allocated_memory() instead. */ Index: lib/msan/msan_interceptors.cc =================================================================== --- lib/msan/msan_interceptors.cc +++ lib/msan/msan_interceptors.cc @@ -1543,6 +1543,8 @@ SetShadow(a, size, __msan::flags()->poison_stack_with_zeroes ? 0 : -1); } +void __msan_unpoison_param(uptr n) { UnpoisonParam(n); } + void __msan_clear_and_unpoison(void *a, uptr size) { REAL(memset)(a, 0, size); SetShadow(a, size, 0); Index: lib/msan/msan_interface_internal.h =================================================================== --- lib/msan/msan_interface_internal.h +++ lib/msan/msan_interface_internal.h @@ -69,6 +69,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_unpoison_string(const char *s); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_unpoison_param(uptr n); +SANITIZER_INTERFACE_ATTRIBUTE void __msan_clear_and_unpoison(void *a, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void* __msan_memcpy(void *dst, const void *src, uptr size); Index: test/msan/unpoison_param.cc =================================================================== --- test/msan/unpoison_param.cc +++ test/msan/unpoison_param.cc @@ -0,0 +1,51 @@ +// Tests that __msan_unpoison_param() works as specified. To prevent MSan +// instrumentation from modifying parameter shadow before each call to foo(), we +// compile main() without MSan. + +// RUN: %clangxx_msan -fno-sanitize=memory -c %s -o %t-main.o +// RUN: %clangxx_msan %t-main.o %s -o %t +// RUN: %run %t + +#include +#include + +#if __has_feature(memory_sanitizer) + +__attribute__((noinline)) int bar(int a, int b) { + volatile int zero = 0; + return zero; +} + +int foo(int a, int b, int unpoisoned_params) { + if (unpoisoned_params == 0) { + assert(__msan_test_shadow(&a, sizeof(a)) == 0); + assert(__msan_test_shadow(&b, sizeof(b)) == 0); + } else if (unpoisoned_params == 1) { + assert(__msan_test_shadow(&a, sizeof(a)) == -1); + assert(__msan_test_shadow(&b, sizeof(b)) == 0); + } else if (unpoisoned_params == 2) { + assert(__msan_test_shadow(&a, sizeof(a)) == -1); + assert(__msan_test_shadow(&b, sizeof(b)) == -1); + } + + // Poisons parameter shadow in TLS so that the next call from uninstrumented + // main has params 1 and 2 poisoned no matter what. + int x, y; + return bar(x, y); +} + +#else + +int foo(int, int, int); + +int main() { + foo(0, 0, 2); // Poison parameters for next call. + foo(0, 0, 0); // Check that both params are poisoned. + __msan_unpoison_param(1); + foo(0, 0, 1); // Check that only first param is unpoisoned. + __msan_unpoison_param(2); + foo(0, 0, 2); // Check that first and second params are unpoisoned. + return 0; +} + +#endif