258 lines · cpp
1// Check that shadow of retrieved value from va_list matches the shadow of passed value.2 3// Without -fno-sanitize-memory-param-retval we can't even pass poisoned values.4// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -fsanitize-memory-track-origins=0 -O3 %s -o %t5 6// FIXME: The rest is likely still broken.7// XFAIL: target={{(loongarch64|mips|powerpc64).*}}8 9#include <sanitizer/msan_interface.h>10#include <stdarg.h>11#include <stdint.h>12#include <stdio.h>13#include <string.h>14 15#ifdef DEBUG_VARARG_SHADOW_TEST16__attribute__((noinline, no_sanitize("memory"))) void17printb(const void *p, size_t n, int line, int align) {18 fprintf(stderr, "\n%p at line %d: \n", p, line);19 for (int i = 0; i < n;) {20 fprintf(stderr, "%p: ", (void *)(((uint8_t *)p) + i));21 for (int j = 0; j < align; ++i, ++j)22 fprintf(stderr, "%02x ", ((uint8_t *)p)[i]);23 fprintf(stderr, "\n");24 }25}26 27struct my_va_list {28# ifdef __ARM_ARCH_ISA_A6429 void *stack;30 void *gr_top;31 void *vr_top;32 int gr_offs;33 int vr_offs;34# else35 unsigned int gp_offset;36 unsigned int fp_offset;37 void *overflow_arg_area;38 void *reg_save_area;39# endif40};41 42__attribute__((noinline, no_sanitize("memory"))) void printva(const void *p,43 int line) {44 my_va_list *pp = (my_va_list *)p;45# ifdef __ARM_ARCH_ISA_A6446 fprintf(stderr,47 "\nva %p at line %d: stack : %p\n gr_top: %p\n vr_top: %p\n gr_offs: "48 "%d\n "49 "vr_offs: %d\n",50 p, line, pp->stack, pp->gr_top, pp->vr_top, pp->gr_offs, pp->vr_offs);51 52 printb((char *)pp->gr_top + pp->gr_offs, -pp->gr_offs, __LINE__, 8);53 printb((char *)pp->vr_top + pp->vr_offs, -pp->vr_offs, __LINE__, 16);54 printb((char *)pp->stack, 256, __LINE__, 8);55# else56 fprintf(stderr,57 "\nva %p at line %d:\n gp_offset: %u\n fp_offset: %u\n "58 "overflow_arg_area: %p\n reg_save_area: %p\n\n",59 p, line, pp->gp_offset, pp->fp_offset, pp->overflow_arg_area,60 pp->reg_save_area);61 62 printb((char *)pp->reg_save_area + pp->gp_offset,63 pp->fp_offset - pp->gp_offset, __LINE__, 8);64 printb((char *)pp->reg_save_area + pp->fp_offset, 128, __LINE__, 16);65 printb((char *)pp->overflow_arg_area, 256, __LINE__, 8);66# endif67}68 69__attribute__((noinline, no_sanitize("memory"))) void printtls(int line) {70 uint8_t tmp[kMsanParamTlsSize];71 for (int i = 0; i < kMsanParamTlsSize; ++i)72 tmp[i] = __msan_va_arg_tls[i];73 fprintf(stderr, "\nTLS at line %d: ", line);74 for (int i = 0; i < kMsanParamTlsSize;) {75 fprintf(stderr, "\n");76 for (int j = 0; j < 16; ++i, ++j)77 fprintf(stderr, "%02x ", tmp[i]);78 }79 80 fprintf(stderr, "\n");81}82#endif // DEBUG_VARARG_SHADOW_TEST83 84const int kMsanParamTlsSize = 800;85extern "C" __thread uint8_t __msan_va_arg_tls[];86 87struct IntInt {88 int a;89 int b;90};91 92struct Int64Int64 {93 int64_t a;94 int64_t b;95};96 97struct DoubleDouble {98 double a;99 double b;100};101 102struct Double4 {103 double a[4];104};105 106struct DoubleFloat {107 double a;108 float b;109};110 111struct LongDouble2 {112 long double a[2];113};114 115struct LongDouble4 {116 long double a[4];117};118 119template <class T>120__attribute__((noinline)) void print_shadow(va_list &args, int n,121 const char *function) {122 for (int i = 0; i < n; i++) {123 // 1-based to make it different from clean shadow.124 fprintf(stderr, "\nArgShadow fn:%s n:%d i:%02x ", function, n, i + 1);125 T arg_int = va_arg(args, T);126 if (__msan_test_shadow(&arg_int, sizeof(arg_int)))127 fprintf(stderr, "fake[clean] %02x", i + 1);128 else129 __msan_dump_shadow(&arg_int, sizeof(arg_int));130#ifdef DEBUG_VARARG_SHADOW_TEST131 printb(&arg_int, sizeof(arg_int), __LINE__, 16);132#endif133 }134}135 136template <class T> __attribute__((noinline)) void test1(int n, ...) {137#ifdef DEBUG_VARARG_SHADOW_TEST138 printtls(__LINE__);139#endif140 va_list args;141 va_start(args, n);142#ifdef DEBUG_VARARG_SHADOW_TEST143 printva(&args, __LINE__);144#endif145 print_shadow<T>(args, n, __FUNCTION__);146 va_end(args);147}148 149template <class T> __attribute__((noinline)) void test2(T t, int n, ...) {150#ifdef DEBUG_VARARG_SHADOW_TEST151 printtls(__LINE__);152#endif153 va_list args;154 va_start(args, n);155#ifdef DEBUG_VARARG_SHADOW_TEST156 printva(&args, __LINE__);157#endif158 print_shadow<T>(args, n, __FUNCTION__);159 va_end(args);160}161 162template <class T> __attribute__((noinline)) void test() {163 // Array of values we will pass into variadic functions.164 static T args[32] = {};165 166 // Poison values making the fist byte of the item shadow match the index.167 // E.g. item 3 should be poisoned as '03 ff ff ff'.168 memset(args, 0xff, sizeof(args));169 __msan_poison(args, sizeof(args));170 for (int i = 0; i < 32; ++i) {171 char *first = (char *)(&args[i]);172 *first = char(*(int *)(first)&i);173 }174#ifdef DEBUG_VARARG_SHADOW_TEST175 __msan_print_shadow(args, sizeof(args));176#endif177 178 // Now we will check that index, printed like 'i:03' will match179 // '0x123abc[0x123abc] 03 ff ff ff'180 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);181 test1<T>(1, args[1]);182 // CHECK-COUNT-1: ArgShadow fn:test1 n:1 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]183 184 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);185 test1<T>(4, args[1], args[2], args[3], args[4]);186 // CHECK-COUNT-4: ArgShadow fn:test1 n:4 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]187 188 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);189 test1<T>(20, args[1], args[2], args[3], args[4], args[5], args[6], args[7],190 args[8], args[9], args[10], args[11], args[12], args[13], args[14],191 args[15], args[16], args[17], args[18], args[19], args[20]);192 // CHECK-COUNT-20: ArgShadow fn:test1 n:20 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]193 194 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);195 test2<T>(args[31], 1, args[1]);196 // CHECK-COUNT-1: ArgShadow fn:test2 n:1 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]197 198 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);199 test2<T>(args[31], 4, args[1], args[2], args[3], args[4]);200 // CHECK-COUNT-4: ArgShadow fn:test2 n:4 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]201 202 memset(__msan_va_arg_tls, 0xee, kMsanParamTlsSize);203 test2<T>(args[31], 20, args[1], args[2], args[3], args[4], args[5], args[6],204 args[7], args[8], args[9], args[10], args[11], args[12], args[13],205 args[14], args[15], args[16], args[17], args[18], args[19],206 args[20]);207 // CHECK-COUNT-20: ArgShadow fn:test2 n:20 i:[[ARGI:[[:xdigit:]]{2}]] {{[^]]+}}] [[ARGI]]208}209 210int main(int argc, char *argv[]) {211#define TEST(T...) \212 if (argc == 2 && strcmp(argv[1], #T) == 0) { \213 test<T>(); \214 return 0; \215 }216 217 TEST(char);218 // RUN: %run %t char 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"219 220 TEST(int);221 // RUN: %run %t int 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"222 223 TEST(void*);224 // RUN: %run %t "void*" 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"225 226 TEST(float);227 // RUN: %run %t float 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"228 229 TEST(double);230 // RUN: %run %t double 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"231 232 TEST(long double);233 // RUN: %run %t "long double" 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"234 235 TEST(IntInt);236 // RUN: %run %t IntInt 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"237 238 TEST(Int64Int64);239 // RUN: %run %t Int64Int64 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"240 241 TEST(DoubleDouble);242 // RUN: %run %t DoubleDouble 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"243 244 TEST(Double4);245 // RUN: %run %t Double4 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"246 247 TEST(DoubleFloat);248 // RUN: %run %t DoubleFloat 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"249 250 TEST(LongDouble2);251 // RUN: %run %t LongDouble2 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"252 253 TEST(LongDouble4);254 // RUN: %run %t LongDouble4 2>&1 | FileCheck %s --implicit-check-not="ArgShadow"255 256 return 1;257}258