Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_racesan_async.h"
3 : #include "../../util/fd_util.h"
4 : #include <errno.h>
5 :
6 : #if FD_HAS_HOSTED
7 :
8 : #include <sys/mman.h>
9 :
10 : void *
11 0 : fd_racesan_stack_create( ulong stack_sz ) {
12 0 : if( FD_UNLIKELY( !stack_sz || !fd_ulong_is_aligned( stack_sz, FD_SHMEM_NORMAL_PAGE_SZ ) ) ) {
13 0 : FD_LOG_ERR(( "invalid stack_sz %lu", stack_sz ));
14 0 : }
15 :
16 0 : void * mem = mmap( NULL, stack_sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
17 0 : if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
18 0 : FD_LOG_ERR(( "mmap(NULL,%lu,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) failed (%i-%s)",
19 0 : stack_sz, errno, fd_io_strerror( errno ) ));
20 0 : }
21 :
22 0 : return mem;
23 0 : }
24 :
25 : void
26 : fd_racesan_stack_destroy( void * stack_bottom,
27 0 : ulong stack_sz ) {
28 0 : if( FD_UNLIKELY( munmap( stack_bottom, stack_sz )!=0 ) ) {
29 0 : FD_LOG_ERR(( "munmap(%p,%lu) failed (%i-%s)",
30 0 : stack_bottom, stack_sz, errno, fd_io_strerror( errno ) ));
31 0 : }
32 0 : }
33 :
34 : #endif /* FD_HAS_HOSTED */
35 :
36 : /* AddressSanitizer routines for switching stacks. These are called
37 : immediately before calling swapcontext(2) and just after control flow
38 : has entered the target context we swapped to. */
39 :
40 : static inline void
41 0 : fd_racesan_async_enter_start( fd_racesan_async_t * async ) {
42 : // FD_LOG_NOTICE(( "enter_start frame=%p stack=[%p,%p)", __builtin_frame_address(0), async->stack_bottom, (void *)( (ulong)async->stack_bottom+async->stack_sz ) ));
43 0 : FD_COMPILER_MFENCE();
44 0 : fd_asan_start_switch_fiber( NULL, async->stack_bottom, async->stack_sz );
45 0 : FD_COMPILER_MFENCE();
46 0 : }
47 :
48 : static inline void
49 0 : fd_racesan_async_enter_finish( fd_racesan_async_t * async ) {
50 : /* Back up the main context's stack parameters */
51 0 : FD_COMPILER_MFENCE();
52 0 : fd_asan_finish_switch_fiber( NULL, &async->asan_stack_bottom_old, &async->asan_stack_size_old );
53 0 : FD_COMPILER_MFENCE();
54 : // FD_LOG_NOTICE(( "enter_finish frame=%p", __builtin_frame_address(0) ));
55 0 : }
56 :
57 : static inline void
58 0 : fd_racesan_async_exit_start( fd_racesan_async_t * async ) {
59 : /* Restore the main context's stack parameters */
60 : // FD_LOG_NOTICE(( "exit_start frame=%p stack_old=[%p,%p)", __builtin_frame_address(0), async->asan_stack_bottom_old, (void *)( (ulong)async->asan_stack_bottom_old+async->asan_stack_size_old ) ));
61 0 : FD_COMPILER_MFENCE();
62 0 : fd_asan_start_switch_fiber( NULL, async->asan_stack_bottom_old, async->asan_stack_size_old );
63 0 : FD_COMPILER_MFENCE();
64 0 : }
65 :
66 : static inline void
67 0 : fd_racesan_async_exit_finish( fd_racesan_async_t * async ) {
68 0 : (void)async;
69 0 : FD_COMPILER_MFENCE();
70 0 : fd_asan_finish_switch_fiber( NULL, NULL, 0UL );
71 0 : FD_COMPILER_MFENCE();
72 : // FD_LOG_NOTICE(( "exit_finish frame=%p", __builtin_frame_address(0) ));
73 0 : }
74 :
75 : /* fd_racesan_async_yield context switches out to the caller of the
76 : fd_racesan_async_step function. */
77 :
78 : __attribute__((no_sanitize_address)) static void
79 0 : fd_racesan_async_yield( fd_racesan_async_t * async ) {
80 0 : FD_COMPILER_MFENCE();
81 0 : fd_racesan_async_exit_start( async );
82 0 : FD_COMPILER_MFENCE();
83 0 : if( FD_UNLIKELY( 0!=swapcontext( &async->ctx, &async->caller ) ) ) {
84 0 : FD_LOG_ERR(( "failed to yield from async fn: swapcontext failed (%i-%s)", errno, fd_io_strerror( errno ) ));
85 0 : }
86 0 : FD_COMPILER_MFENCE();
87 0 : fd_racesan_async_enter_finish( async );
88 0 : FD_COMPILER_MFENCE();
89 0 : }
90 :
91 : /* fd_racesan_async_hook is called whenever the target reaches a racesan
92 : hook. The racesan_async logic here context-switches out back to the
93 : caller of step. */
94 :
95 : static void
96 : fd_racesan_async_hook( void * ctx,
97 0 : ulong name_hash ) {
98 0 : fd_racesan_async_t * async = ctx;
99 0 : async->name_hash = name_hash;
100 0 : fd_racesan_exit();
101 0 : fd_racesan_async_yield( async );
102 0 : }
103 :
104 : /* fd_racesan_async_target is a long-lived / async function with its own
105 : stack. */
106 :
107 : static void
108 0 : fd_racesan_async_target( fd_racesan_async_t * async ) {
109 0 : fd_racesan_async_enter_finish( async );
110 0 : async->fn( async->fn_ctx );
111 0 : async->done = 1;
112 0 : fd_racesan_exit();
113 0 : fd_racesan_async_yield( async );
114 0 : }
115 :
116 : fd_racesan_async_t *
117 : fd_racesan_async_new( fd_racesan_async_t * async,
118 : void * stack_bottom, /* lowest address of stack */
119 : ulong stack_max,
120 : fd_racesan_async_fn_t * async_fn,
121 0 : void * ctx ) {
122 0 : if( FD_UNLIKELY( !stack_bottom ) ) {
123 0 : FD_LOG_WARNING(( "NULL stack_bottom" ));
124 0 : }
125 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)stack_bottom, 16UL ) ) ) {
126 0 : FD_LOG_WARNING(( "misaligned stack_bottom" ));
127 0 : }
128 0 : if( FD_UNLIKELY( !stack_max ) ) {
129 0 : FD_LOG_WARNING(( "zero stack_max" ));
130 0 : }
131 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( stack_max, 16UL ) ) ) {
132 0 : FD_LOG_WARNING(( "misaligned stack_max" ));
133 0 : }
134 :
135 0 : memset( async, 0, sizeof(fd_racesan_async_t) );
136 0 : async->stack_bottom = stack_bottom;
137 0 : async->stack_sz = stack_max;
138 :
139 0 : fd_racesan_new( async->racesan, async );
140 0 : fd_racesan_inject_default( async->racesan, fd_racesan_async_hook );
141 :
142 0 : async->fn_ctx = ctx;
143 0 : async->fn = async_fn;
144 :
145 0 : if( FD_UNLIKELY( 0!=getcontext( &async->ctx ) ) ) {
146 0 : FD_LOG_ERR(( "getcontext failed" ));
147 0 : }
148 0 : async->ctx.uc_stack.ss_sp = async->stack_bottom;
149 0 : async->ctx.uc_stack.ss_size = async->stack_sz;
150 0 : makecontext( &async->ctx, (void (*)( void ))fd_racesan_async_target, 1, async );
151 :
152 0 : return async;
153 0 : }
154 :
155 : void *
156 0 : fd_racesan_async_delete( fd_racesan_async_t * async ) {
157 0 : fd_racesan_delete( async->racesan );
158 0 : return async;
159 0 : }
160 :
161 : int
162 0 : fd_racesan_async_step_private( fd_racesan_async_t * async ) {
163 0 : if( FD_UNLIKELY( async->done ) ) return FD_RACESAN_ASYNC_RET_EXIT;
164 :
165 0 : fd_racesan_enter( async->racesan );
166 0 : fd_racesan_async_enter_start( async );
167 0 : if( FD_UNLIKELY( 0!=swapcontext( &async->caller, &async->ctx ) ) ) {
168 0 : FD_LOG_ERR(( "failed to step into async fn: swapcontext failed (%i-%s)", errno, fd_io_strerror( errno ) ));
169 0 : }
170 0 : fd_racesan_async_exit_finish( async );
171 :
172 0 : return async->done ? FD_RACESAN_ASYNC_RET_EXIT : FD_RACESAN_ASYNC_RET_HOOK;
173 0 : }
174 :
175 : int
176 : fd_racesan_async_hook_name_eq( fd_racesan_async_t * async,
177 0 : char const * hook_name ) {
178 0 : ulong len = strlen( hook_name );
179 0 : ulong hash = fd_racesan_strhash( hook_name, len );
180 0 : return async->name_hash==hash;
181 0 : }
182 :
183 : int
184 : fd_racesan_async_step_until( fd_racesan_async_t * async,
185 : char const * hook_name,
186 0 : ulong step_max ) {
187 0 : ulong len = strlen( hook_name );
188 0 : ulong hash = fd_racesan_strhash( hook_name, len );
189 0 : for( ulong step=0UL; step<step_max; step++ ) {
190 0 : int step_ret = fd_racesan_async_step( async );
191 0 : if( FD_UNLIKELY( step_ret==FD_RACESAN_ASYNC_RET_EXIT ) ) return FD_RACESAN_ASYNC_RET_EXIT;
192 0 : if( FD_LIKELY( async->name_hash==hash ) ) return FD_RACESAN_ASYNC_RET_HOOK;
193 0 : }
194 0 : return FD_RACESAN_ASYNC_RET_TIMEOUT;
195 0 : }
196 :
197 : void
198 0 : fd_racesan_async_reset( fd_racesan_async_t * async ) {
199 0 : makecontext( &async->ctx, (void (*)( void ))fd_racesan_async_target, 1, async );
200 0 : async->done = 0;
201 0 : }
|