258 lines
8 KiB
C++
258 lines
8 KiB
C++
|
// 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;
|
||
|
}
|