xref: /llvm-project/compiler-rt/test/msan/vararg_shadow.cpp (revision 9aa88b0f02a4a5dbc4072c11ed992eb617a6b2e2)
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 %t
5 
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_TEST
16 __attribute__((noinline, no_sanitize("memory"))) void
printb(const void * p,size_t n,int line,int align)17 printb(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 
27 struct my_va_list {
28 #  ifdef __ARM_ARCH_ISA_A64
29   void *stack;
30   void *gr_top;
31   void *vr_top;
32   int gr_offs;
33   int vr_offs;
34 #  else
35   unsigned int gp_offset;
36   unsigned int fp_offset;
37   void *overflow_arg_area;
38   void *reg_save_area;
39 #  endif
40 };
41 
printva(const void * p,int line)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_A64
46   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 #  else
56   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 #  endif
67 }
68 
printtls(int line)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_TEST
83 
84 const int kMsanParamTlsSize = 800;
85 extern "C" __thread uint8_t __msan_va_arg_tls[];
86 
87 struct IntInt {
88   int a;
89   int b;
90 };
91 
92 struct Int64Int64 {
93   int64_t a;
94   int64_t b;
95 };
96 
97 struct DoubleDouble {
98   double a;
99   double b;
100 };
101 
102 struct Double4 {
103   double a[4];
104 };
105 
106 struct DoubleFloat {
107   double a;
108   float b;
109 };
110 
111 struct LongDouble2 {
112   long double a[2];
113 };
114 
115 struct LongDouble4 {
116   long double a[4];
117 };
118 
119 template <class T>
print_shadow(va_list & args,int n,const char * function)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     else
129       __msan_dump_shadow(&arg_int, sizeof(arg_int));
130 #ifdef DEBUG_VARARG_SHADOW_TEST
131     printb(&arg_int, sizeof(arg_int), __LINE__, 16);
132 #endif
133   }
134 }
135 
test1(int n,...)136 template <class T> __attribute__((noinline)) void test1(int n, ...) {
137 #ifdef DEBUG_VARARG_SHADOW_TEST
138   printtls(__LINE__);
139 #endif
140   va_list args;
141   va_start(args, n);
142 #ifdef DEBUG_VARARG_SHADOW_TEST
143   printva(&args, __LINE__);
144 #endif
145   print_shadow<T>(args, n, __FUNCTION__);
146   va_end(args);
147 }
148 
test2(T t,int n,...)149 template <class T> __attribute__((noinline)) void test2(T t, int n, ...) {
150 #ifdef DEBUG_VARARG_SHADOW_TEST
151   printtls(__LINE__);
152 #endif
153   va_list args;
154   va_start(args, n);
155 #ifdef DEBUG_VARARG_SHADOW_TEST
156   printva(&args, __LINE__);
157 #endif
158   print_shadow<T>(args, n, __FUNCTION__);
159   va_end(args);
160 }
161 
test()162 template <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_TEST
175   __msan_print_shadow(args, sizeof(args));
176 #endif
177 
178   // Now we will check that index, printed like 'i:03' will match
179   // '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 
main(int argc,char * argv[])210 int 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