LCOV - code coverage report
Current view: top level - util/racesan - fd_racesan_async.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 123 0.0 %
Date: 2025-12-07 04:58:33 Functions: 0 15 0.0 %

          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 : }

Generated by: LCOV version 1.14