// Check that shadow of retrieved value from va_list matches the shadow of passed value. // Without -fno-sanitize-memory-param-retval we can't even pass poisoned values. // RUN: %clangxx_msan -fno-sanitize-memory-param-retval -fsanitize-memory-track-origins=0 -O3 %s -o %t // FIXME: The rest is likely still broken. // XFAIL: target={{(loongarch64|mips|powerpc64).*}} #include #include #include #include #include #ifdef DEBUG_VARARG_SHADOW_TEST __attribute__((noinline, no_sanitize("memory"))) void printb(const void *p, size_t n, int line, int align) { fprintf(stderr, "\n%p at line %d: \n", p, line); for (int i = 0; i < n;) { fprintf(stderr, "%p: ", (void *)(((uint8_t *)p) + i)); for (int j = 0; j < align; ++i, ++j) fprintf(stderr, "%02x ", ((uint8_t *)p)[i]); fprintf(stderr, "\n"); } } struct my_va_list { # ifdef __ARM_ARCH_ISA_A64 void *stack; void *gr_top; void *vr_top; int gr_offs; int vr_offs; # else unsigned int gp_offset; unsigned int fp_offset; void *overflow_arg_area; void *reg_save_area; # endif }; __attribute__((noinline, no_sanitize("memory"))) void printva(const void *p, int line) { my_va_list *pp = (my_va_list *)p; # ifdef __ARM_ARCH_ISA_A64 fprintf(stderr, "\nva %p at line %d: stack : %p\n gr_top: %p\n vr_top: %p\n gr_offs: " "%d\n " "vr_offs: %d\n", p, line, pp->stack, pp->gr_top, pp->vr_top, pp->gr_offs, pp->vr_offs); printb((char *)pp->gr_top + pp->gr_offs, -pp->gr_offs, __LINE__, 8); printb((char *)pp->vr_top + pp->vr_offs, -pp->vr_offs, __LINE__, 16); printb((char *)pp->stack, 256, __LINE__, 8); # else fprintf(stderr, "\nva %p at line %d:\n gp_offset: %u\n fp_offset: %u\n " "overflow_arg_area: %p\n reg_save_area: %p\n\n", p, line, pp->gp_offset, pp->fp_offset, pp->overflow_arg_area, pp->reg_save_area); printb((char *)pp->reg_save_area + pp->gp_offset, pp->fp_offset - pp->gp_offset, __LINE__, 8); printb((char *)pp->reg_save_area + pp->fp_offset, 128, __LINE__, 16); printb((char *)pp->overflow_arg_area, 256, __LINE__, 8); # endif } __attribute__((noinline, no_sanitize("memory"))) void printtls(int line) { uint8_t tmp[kMsanParamTlsSize]; for (int i = 0; i < kMsanParamTlsSize; ++i) tmp[i] = __msan_va_arg_tls[i]; fprintf(stderr, "\nTLS at line %d: ", line); for (int i = 0; i < kMsanParamTlsSize;) { fprintf(stderr, "\n"); for (int j = 0; j < 16; ++i, ++j) fprintf(stderr, "%02x ", tmp[i]); } fprintf(stderr, "\n"); } #endif // DEBUG_VARARG_SHADOW_TEST const int kMsanParamTlsSize = 800; extern "C" __thread uint8_t __msan_va_arg_tls[]; struct IntInt { int a; int b; }; struct Int64Int64 { int64_t a; int64_t b; }; struct DoubleDouble { double a; double b; }; struct Double4 { double a[4]; }; struct DoubleFloat { double a; float b; }; struct LongDouble2 { long double a[2]; }; struct LongDouble4 { long double a[4]; }; template __attribute__((noinline)) void print_shadow(va_list &args, int n, const char *function) { for (int i = 0; i < n; i++) { // 1-based to make it different from clean shadow. fprintf(stderr, "\nArgShadow fn:%s n:%d i:%02x ", function, n, i + 1); T arg_int = va_arg(args, T); if (__msan_test_shadow(&arg_int, sizeof(arg_int))) fprintf(stderr, "fake[clean] %02x", i + 1); else __msan_dump_shadow(&arg_int, sizeof(arg_int)); #ifdef DEBUG_VARARG_SHADOW_TEST printb(&arg_int, sizeof(arg_int), __LINE__, 16); #endif } } template __attribute__((noinline)) void test1(int n, ...) { #ifdef DEBUG_VARARG_SHADOW_TEST printtls(__LINE__); #endif va_list args; va_start(args, n); #ifdef DEBUG_VARARG_SHADOW_TEST printva(&args, __LINE__); #endif print_shadow(args, n, __FUNCTION__); va_end(args); } template __attribute__((noinline)) void test2(T t, int n, ...) { #ifdef DEBUG_VARARG_SHADOW_TEST printtls(__LINE__); #endif va_list args; va_start(args, n); #ifdef DEBUG_VARARG_SHADOW_TEST printva(&args, __LINE__); #endif print_shadow(args, n, __FUNCTION__); va_end(args); } template __attribute__((noinline)) void test() { // Array of values we will pass into variadic functions. static T args[32] = {}; // Poison values making the fist byte of the item shadow match the index. // E.g. item 3 should be poisoned as '03 ff ff ff'. memset(args, 0xff, sizeof(args)); __msan_poison(args, sizeof(args)); for (int i = 0; i < 32; ++i) { char *first = (char *)(&args[i]); *first = char(*(int *)(first)&i); } #ifdef DEBUG_VARARG_SHADOW_TEST __msan_print_shadow(args, sizeof(args)); #endif // Now we will check that index, printed like 'i:03' will match // '0x123abc[0x123abc] 03 ff ff ff' memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test1(1, args[1]); // CHECK-COUNT-1: ArgShadow fn:test1 n:1 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test1(4, args[1], args[2], args[3], args[4]); // CHECK-COUNT-4: ArgShadow fn:test1 n:4 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test1(20, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], args[20]); // CHECK-COUNT-20: ArgShadow fn:test1 n:20 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test2(args[31], 1, args[1]); // CHECK-COUNT-1: ArgShadow fn:test2 n:1 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test2(args[31], 4, args[1], args[2], args[3], args[4]); // CHECK-COUNT-4: ArgShadow fn:test2 n:4 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); test2(args[31], 20, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], args[20]); // CHECK-COUNT-20: ArgShadow fn:test2 n:20 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] } int main(int argc, char *argv[]) { #define TEST(T...) \ if (argc == 2 && strcmp(argv[1], #T) == 0) { \ test(); \ return 0; \ } TEST(char); // RUN: %run %t char 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(int); // RUN: %run %t int 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(void*); // RUN: %run %t "void*" 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(float); // RUN: %run %t float 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(double); // RUN: %run %t double 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(long double); // RUN: %run %t "long double" 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(IntInt); // RUN: %run %t IntInt 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(Int64Int64); // RUN: %run %t Int64Int64 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(DoubleDouble); // RUN: %run %t DoubleDouble 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(Double4); // RUN: %run %t Double4 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(DoubleFloat); // RUN: %run %t DoubleFloat 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(LongDouble2); // RUN: %run %t LongDouble2 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" TEST(LongDouble4); // RUN: %run %t LongDouble4 2>&1 | FileCheck %s --implicit-check-not="ArgShadow" return 1; }