// 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;
}