201 lines · cpp
1// Test hwasan __sanitizer_start_switch_fiber and2// __sanitizer_finish_switch_fiber interface.3 4// RUN: %clangxx_hwasan -std=c++11 -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %s5// RUN: %clangxx_hwasan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %s6// RUN: %clangxx_hwasan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %s7// RUN: %clangxx_hwasan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %s8// RUN: seq 30 | xargs -i -- grep LOOPCHECK %s > %t.checks9// RUN: %clangxx_hwasan -std=c++11 -lpthread -O0 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK10// RUN: %clangxx_hwasan -std=c++11 -lpthread -O1 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK11// RUN: %clangxx_hwasan -std=c++11 -lpthread -O2 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK12// RUN: %clangxx_hwasan -std=c++11 -lpthread -O3 %s -o %t && %run %t 2>&1 | FileCheck %t.checks --check-prefix LOOPCHECK13 14//15// Android and musl do not support swapcontext.16// REQUIRES: glibc-2.2717 18#include <pthread.h>19#include <setjmp.h>20#include <signal.h>21#include <stdio.h>22#include <sys/time.h>23#include <ucontext.h>24#include <unistd.h>25 26#include <sanitizer/common_interface_defs.h>27 28ucontext_t orig_context;29ucontext_t child_context;30ucontext_t next_child_context;31 32char *next_child_stack;33 34const int kStackSize = 1 << 20;35 36const void *main_thread_stack;37size_t main_thread_stacksize;38 39const void *from_stack;40size_t from_stacksize;41 42// hwasan does not support longjmp with tagged stack pointer, make sure it is43// not tagged.44char __attribute__((no_sanitize("hwaddress"))) allocated_stack[kStackSize + 1];45char __attribute__((46 no_sanitize("hwaddress"))) allocated_child_stack[kStackSize + 1];47 48__attribute__((noinline, noreturn)) void LongJump(jmp_buf env) {49 longjmp(env, 1);50 _exit(1);51}52 53// Simulate __asan_handle_no_return().54__attribute__((noinline)) void CallNoReturn() {55 jmp_buf env;56 if (setjmp(env) != 0)57 return;58 59 LongJump(env);60 _exit(1);61}62 63void NextChild() {64 CallNoReturn();65 __sanitizer_finish_switch_fiber(nullptr, &from_stack, &from_stacksize);66 67 printf("NextChild from: %p %zu\n", from_stack, from_stacksize);68 69 char x[32] = {0}; // Stack gets poisoned.70 printf("NextChild: %p\n", x);71 72 CallNoReturn();73 74 __sanitizer_start_switch_fiber(nullptr, main_thread_stack,75 main_thread_stacksize);76 CallNoReturn();77 if (swapcontext(&next_child_context, &orig_context) < 0) {78 perror("swapcontext");79 _exit(1);80 }81}82 83void Child(int mode) {84 CallNoReturn();85 __sanitizer_finish_switch_fiber(nullptr, &main_thread_stack,86 &main_thread_stacksize);87 char x[32] = {0}; // Stack gets poisoned.88 printf("Child: %p\n", x);89 CallNoReturn();90 // (a) Do nothing, just return to parent function.91 // (b) Jump into the original function. Stack remains poisoned unless we do92 // something.93 // (c) Jump to another function which will then jump back to the main function94 if (mode == 0) {95 __sanitizer_start_switch_fiber(nullptr, main_thread_stack,96 main_thread_stacksize);97 CallNoReturn();98 } else if (mode == 1) {99 __sanitizer_start_switch_fiber(nullptr, main_thread_stack,100 main_thread_stacksize);101 CallNoReturn();102 if (swapcontext(&child_context, &orig_context) < 0) {103 perror("swapcontext");104 _exit(1);105 }106 } else if (mode == 2) {107 printf("NextChild stack: %p\n", next_child_stack);108 109 getcontext(&next_child_context);110 next_child_context.uc_stack.ss_sp = next_child_stack;111 next_child_context.uc_stack.ss_size = kStackSize / 2;112 makecontext(&next_child_context, (void (*)())NextChild, 0);113 __sanitizer_start_switch_fiber(nullptr, next_child_context.uc_stack.ss_sp,114 next_child_context.uc_stack.ss_size);115 CallNoReturn();116 if (swapcontext(&child_context, &next_child_context) < 0) {117 perror("swapcontext");118 _exit(1);119 }120 }121}122 123int Run(int arg, int mode, char *child_stack) {124 printf("Child stack: %p\n", child_stack);125 // Setup child context.126 getcontext(&child_context);127 child_context.uc_stack.ss_sp = child_stack;128 child_context.uc_stack.ss_size = kStackSize / 2;129 if (mode == 0) {130 child_context.uc_link = &orig_context;131 }132 makecontext(&child_context, (void (*)())Child, 1, mode);133 CallNoReturn();134 __sanitizer_start_switch_fiber(nullptr, child_context.uc_stack.ss_sp,135 child_context.uc_stack.ss_size);136 CallNoReturn();137 if (swapcontext(&orig_context, &child_context) < 0) {138 perror("swapcontext");139 _exit(1);140 }141 CallNoReturn();142 __sanitizer_finish_switch_fiber(nullptr, &from_stack, &from_stacksize);143 CallNoReturn();144 printf("Main context from: %p %zu\n", from_stack, from_stacksize);145 146 return child_stack[arg];147}148 149void handler(int sig) { CallNoReturn(); }150 151int main(int argc, char **argv) {152 // This testcase is copied from ASan's swapcontext_annotation.cpp testcase153 // and adapted to HWASan:154 // 1. removed huge stack test since hwasan has no huge stack limitations155 // 2. stack allocations are now done with original malloc/free instead of156 // hwasan interceptor, since HWASan does not support tagged stack pointer157 // in longjmp (see __hwasan_handle_longjmp)158 159 // set up a signal that will spam and trigger __hwasan_handle_vfork at160 // tricky moments161 struct sigaction act = {};162 act.sa_handler = &handler;163 if (sigaction(SIGPROF, &act, 0)) {164 perror("sigaction");165 _exit(1);166 }167 168 itimerval t;169 t.it_interval.tv_sec = 0;170 t.it_interval.tv_usec = 10;171 t.it_value = t.it_interval;172 if (setitimer(ITIMER_PROF, &t, 0)) {173 perror("setitimer");174 _exit(1);175 }176 177 char *heap = allocated_stack;178 next_child_stack = allocated_child_stack;179 int ret = 0;180 // CHECK-NOT: WARNING: HWASan is ignoring requested __hwasan_handle_vfork181 for (unsigned int i = 0; i < 30; ++i) {182 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]183 // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288184 ret += Run(argc - 1, 0, heap);185 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]186 // LOOPCHECK: Main context from: [[CHILD_STACK]] 524288187 ret += Run(argc - 1, 1, heap);188 // LOOPCHECK: Child stack: [[CHILD_STACK:0x[0-9a-f]*]]189 // LOOPCHECK: NextChild stack: [[NEXT_CHILD_STACK:0x[0-9a-f]*]]190 // LOOPCHECK: NextChild from: [[CHILD_STACK]] 524288191 // LOOPCHECK: Main context from: [[NEXT_CHILD_STACK]] 524288192 ret += Run(argc - 1, 2, heap);193 printf("Iteration %d passed\n", i);194 }195 196 // CHECK: Test passed197 printf("Test passed\n");198 199 return ret;200}201