Line data Source code
1 : #ifndef HEADER_fd_src_util_racesan_fd_racesan_async_h 2 : #define HEADER_fd_src_util_racesan_fd_racesan_async_h 3 : 4 : #include "fd_racesan.h" 5 : #include <stddef.h> 6 : #include <ucontext.h> 7 : 8 0 : #define FD_RACESAN_ASYNC_RET_EXIT (0) 9 0 : #define FD_RACESAN_ASYNC_RET_HOOK (1) 10 0 : #define FD_RACESAN_ASYNC_RET_TIMEOUT (2) 11 : 12 : typedef void 13 : fd_racesan_async_fn_t( void * ctx ); 14 : 15 : struct fd_racesan_async { 16 : fd_racesan_t racesan[1]; 17 : 18 : ucontext_t ctx; 19 : ucontext_t caller; 20 : 21 : void * fn_ctx; 22 : fd_racesan_async_fn_t * fn; 23 : 24 : ulong name_hash; 25 : uint done : 1; 26 : 27 : void * stack_bottom; 28 : ulong stack_sz; 29 : 30 : /* When using ASan, remember stack params of caller/main thread */ 31 : void const * asan_stack_bottom_old; 32 : ulong asan_stack_size_old; 33 : }; 34 : 35 : typedef struct fd_racesan_async fd_racesan_async_t; 36 : 37 : FD_PROTOTYPES_BEGIN 38 : 39 : #if FD_HAS_HOSTED 40 : 41 : /* fd_racesan_stack_create requests a new private anonymous memory 42 : region from the OS suitable for use as a racesan_async stack, and 43 : maps it at an address chosen by the OS. Returns the lowest address 44 : (bottom) of that stack. Terminates with FD_LOG_ERR on failure to 45 : map stack region. stack_sz MUST be FD_SHMEM_NORMAL_PAGE_SZ aligned. */ 46 : 47 : void * 48 : fd_racesan_stack_create( ulong stack_sz ); 49 : 50 : /* fd_racesan_stack_destroy undoes the effects of 51 : fd_racesan_stack_create. Terminates with FD_LOG_ERR on failure to 52 : unmap stack. */ 53 : 54 : void 55 : fd_racesan_stack_destroy( void * stack_bottom, 56 : ulong stack_sz ); 57 : 58 : #endif /* FD_HAS_HOSTED */ 59 : 60 : /* fd_racesan_async_new begins an async function call. 61 : 62 : WARNING: stack_bottom SHOULD be normal page-aligned. Technically, 63 : 16 byte alignment is sufficient on x86-psABI. LLVM compiler-rt ASan, 64 : however, uses munmap() when pivoting stacks in swapcontext(). Due to 65 : a bug in that code, if the stack is less than 4K aligned, the munmap 66 : call will blow away random other data that happens to be in the same 67 : page. 68 : 69 : Use fd_racesan_stack_{create,destroy} to get new (reusable) stack 70 : regions instead. */ 71 : 72 : fd_racesan_async_t * 73 : fd_racesan_async_new( fd_racesan_async_t * async, 74 : void * stack_bottom, /* lowest address of stack */ 75 : ulong stack_max, 76 : fd_racesan_async_fn_t * async_fn, 77 : void * ctx ); 78 : 79 : void * 80 : fd_racesan_async_delete( fd_racesan_async_t * async ); 81 : 82 : /* fd_progcache_async_step continues executing an async function call 83 : until it reaches a hook or until the function exits. Returns 84 : RET_HOOK if a hook was reached (call still in-progress), or RET_EXIT 85 : if the function call exited. */ 86 : 87 : int 88 : fd_racesan_async_step_private( fd_racesan_async_t * async ); 89 : 90 : #define fd_racesan_async_step( async ) \ 91 0 : __extension__({ \ 92 0 : FD_COMPILER_MFENCE(); \ 93 0 : int r = fd_racesan_async_step_private( async ); \ 94 0 : FD_COMPILER_MFENCE(); \ 95 0 : r; \ 96 0 : }) 97 : 98 : /* fd_racesan_async_hook_name_eq returns 1 if the async function call is 99 : currently suspended at a hook with the given name. Else returns 0. */ 100 : 101 : int 102 : fd_racesan_async_hook_name_eq( fd_racesan_async_t * async, 103 : char const * hook_name ); 104 : 105 : /* fd_progcache_async_step_until continues executing an async function 106 : call until it reaches a hook with the given name. Returns RET_HOOK 107 : if the hook was reached, RET_EXIT if the call exited, or RET_TIMEOUT 108 : if more than step_max steps were made. */ 109 : 110 : int 111 : fd_racesan_async_step_until( fd_racesan_async_t * async, 112 : char const * hook_name, 113 : ulong step_max ); 114 : 115 : void 116 : fd_racesan_async_reset( fd_racesan_async_t * async ); 117 : 118 : FD_PROTOTYPES_END 119 : 120 : #endif /* HEADER_fd_src_util_racesan_fd_racesan_async_h */