| // 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 <sanitizer/msan_interface.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #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 <class T> |
| __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 <class T> __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<T>(args, n, __FUNCTION__); |
| va_end(args); |
| } |
| |
| template <class T> __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<T>(args, n, __FUNCTION__); |
| va_end(args); |
| } |
| |
| template <class T> __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<T>(1, args[1]); |
| // CHECK-COUNT-1: ArgShadow fn:test1 n:1 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]] |
| |
| memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize); |
| test1<T>(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<T>(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<T>(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<T>(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<T>(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<T>(); \ |
| 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; |
| } |