LCOV - code coverage report
Current view: top level - flamenco/types - fuzz_types_decode.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 12 86 14.0 %
Date: 2025-07-10 04:52:38 Functions: 1 6 16.7 %

          Line data    Source code
       1             : #if !FD_HAS_HOSTED
       2             : #error "This target requires FD_HAS_HOSTED"
       3             : #endif
       4             : 
       5             : #define _GNU_SOURCE
       6             : #include <dlfcn.h>
       7             : 
       8             : #include <stdio.h>
       9             : #include <stdlib.h>
      10             : #include <string.h>
      11             : #include <assert.h>
      12             : 
      13             : #include "../../util/sanitize/fd_fuzz.h"
      14             : #include "fd_types_reflect.h"
      15             : #include "../fd_flamenco.h"
      16             : 
      17             : #include "fd_fuzz_types.h"
      18             : 
      19             : 
      20             : static const char *blacklist[] = {
      21             :   "fd_pubkey",
      22             :   // fd_tower_sync_t encoding function is unimplemented
      23             :   "fd_tower_sync",
      24             :   "fd_tower_sync_switch",
      25             :   // fd_flamenco_txn returns -1000001 when decoding if failed to parse txns
      26             :   // skip anything that uses it as well as gossip_msg (cases: 0-2)
      27             :   // FIXME: maybe add ability to load valid Txns
      28             :   "fd_flamenco_txn",
      29             :   "fd_gossip_vote",
      30             :   "fd_crds_data",
      31             :   "fd_crds_value",
      32             :   "fd_gossip_pull_req",
      33             :   "fd_gossip_pull_resp",
      34             :   "fd_gossip_push_msg",
      35             : };
      36             : 
      37             : static int
      38           0 : is_blacklisted( char const * type_name ) {
      39           0 :   if( !type_name ) return 1;
      40             : 
      41           0 :   for( ulong i=0; i < (sizeof(blacklist) / sizeof(blacklist[0])); ++i ) {
      42           0 :     if( strcmp( blacklist[i], type_name ) == 0 ) return 1;
      43           0 :   }
      44           0 :   return 0;
      45           0 : }
      46             : 
      47             : static int
      48             : encode_type( fd_types_vt_t const * type_meta,
      49             :              void *                from,
      50             :              void *                to,
      51             :              size_t const          capacity,
      52           0 :              size_t *              written ) {
      53           0 :   fd_bincode_encode_ctx_t encode_ctx = {
      54           0 :     .data    = to,
      55           0 :     .dataend = (void *) ((ulong) to + capacity),
      56           0 :   };
      57           0 :   int err = type_meta->encode( from, &encode_ctx );
      58             : 
      59           0 :   *written = (size_t) encode_ctx.data - (size_t)to;
      60           0 :   return err;
      61           0 : }
      62             : 
      63             : static int
      64             : decode_type( fd_types_vt_t const * type_meta,
      65             :              void const *          from,
      66             :              void **               to,
      67             :              size_t const          capacity,
      68           0 :              size_t *              written ) {
      69           0 :   fd_bincode_decode_ctx_t decode_ctx = {
      70           0 :     .data    = from,
      71           0 :     .dataend = (void *) ((ulong) from + capacity),
      72           0 :   };
      73             : 
      74           0 :   ulong total_sz = 0UL;
      75           0 :   int   err      = type_meta->decode_footprint( &decode_ctx, &total_sz );
      76           0 :   if (err != FD_BINCODE_SUCCESS) {
      77           0 :     return err;
      78           0 :   }
      79             : 
      80           0 :   *written = total_sz;
      81             : 
      82           0 :   if( FD_UNLIKELY( !fd_scratch_alloc_is_safe( type_meta->align, total_sz ) ) ) {
      83           0 :     return -1004;
      84           0 :   }
      85           0 :   void * decoded = fd_scratch_alloc( type_meta->align, total_sz );
      86             : 
      87           0 :   *to = type_meta->decode( decoded, &decode_ctx );
      88             : 
      89           0 :   return FD_BINCODE_SUCCESS;
      90           0 : }
      91             : 
      92             : static inline void
      93           0 : fd_scratch_detach_null( void ) {
      94           0 :   fd_scratch_detach( NULL );
      95           0 : }
      96             : 
      97             : int
      98             : LLVMFuzzerInitialize( int  *   argc,
      99          15 :                       char *** argv ) {
     100             :   /* Set up shell without signal handlers */
     101          15 :   putenv( "FD_LOG_BACKTRACE=0" );
     102          15 :   fd_boot( argc, argv );
     103          15 :   fd_flamenco_boot( argc, argv );
     104             : 
     105          15 :   static uchar scratch_mem [ 1UL<<30 ];  /* 1 GB */
     106          15 :   static ulong scratch_fmem[ 4UL ] __attribute((aligned(FD_SCRATCH_FMEM_ALIGN)));
     107          15 :   fd_scratch_attach( scratch_mem, scratch_fmem, 1UL<<30, 4UL );
     108             : 
     109          15 :   atexit( fd_halt );
     110          15 :   atexit( fd_flamenco_halt );
     111          15 :   atexit( fd_scratch_detach_null );
     112          15 :   return 0;
     113          15 : }
     114             : 
     115             : static void
     116             : fd_decode_fuzz_data( fd_types_vt_t const * type_meta,
     117             :                      uchar const *         data,
     118           0 :                      ulong                 size ) {
     119             : 
     120             : 
     121           0 :   FD_SCRATCH_SCOPE_BEGIN {
     122             : 
     123           0 :     void * decoded = NULL;
     124           0 :     ulong  written = 0UL;
     125           0 :     int err = decode_type( type_meta, data, &decoded, size, &written );
     126           0 :     if( err != FD_BINCODE_SUCCESS ) {
     127           0 :       return;
     128           0 :     }
     129             : 
     130           0 :     char const * type_name = type_meta->name;
     131           0 :     void * encoded_buffer = fd_scratch_alloc( 1, 100000 );
     132           0 :     err = encode_type( type_meta, decoded, encoded_buffer, 100000, &written );
     133           0 :     if ( err != FD_BINCODE_SUCCESS ) {
     134           0 :       FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_name, err ));
     135           0 :     }
     136             : 
     137           0 :     void * decoded_normalized = NULL;
     138           0 :     ulong  written_normalized = 0UL;
     139           0 :     int err_normalized = decode_type( type_meta, encoded_buffer, &decoded_normalized, written, &written_normalized );
     140           0 :     if( err_normalized != FD_BINCODE_SUCCESS ) {
     141           0 :       return;
     142           0 :     }
     143             : 
     144           0 :     void * encoded_buffer_normalized = fd_scratch_alloc( 1, 50000 );
     145             : 
     146           0 :     err = encode_type( type_meta, decoded_normalized, encoded_buffer_normalized, 50000, &written_normalized );
     147           0 :     if ( err != FD_BINCODE_SUCCESS ) {
     148           0 :       FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_name, err ));
     149           0 :     }
     150             : 
     151           0 :     if( written_normalized > written ) {
     152           0 :       FD_LOG_HEXDUMP_WARNING(( "normalized data", encoded_buffer, written ));
     153           0 :       FD_LOG_HEXDUMP_WARNING(( "encoded", encoded_buffer_normalized, written_normalized ));
     154           0 :       FD_LOG_CRIT(( "encoded size (%lu) > data size (%lu) after decode-encode for: %s", written_normalized, written, type_name ));
     155           0 :     }
     156           0 :     if( memcmp( encoded_buffer, encoded_buffer_normalized, written ) != 0 ) {
     157           0 :       FD_LOG_HEXDUMP_WARNING(( "normalized data", encoded_buffer, written ));
     158           0 :       FD_LOG_HEXDUMP_WARNING(( "encoded", encoded_buffer_normalized, written ));
     159           0 :       FD_LOG_CRIT(( "encoded data differs from the original data after decode-encode for: %s", type_name ));
     160           0 :     }
     161             : 
     162           0 :   } FD_SCRATCH_SCOPE_END;
     163             : 
     164           0 : }
     165             : 
     166             : int
     167             : LLVMFuzzerTestOneInput( uchar const * data,
     168             :                         ulong         size ) {
     169             : 
     170             :   if( FD_UNLIKELY( size == 0 ) ) return 0;
     171             : 
     172             :   assert( fd_types_vt_list_cnt < 256 );
     173             :   if( FD_UNLIKELY( data[0]>=fd_types_vt_list_cnt ) ) return -1;
     174             :   fd_types_vt_t const * type_meta = &fd_types_vt_list[ data[0] ];
     175             :   data = data + 1;
     176             :   size = size - 1;
     177             : 
     178             :   /* fd_pubkey is a #define alias for fd_hash.  It is therefore already
     179             :      fuzzed. Furthermore, dlsym will not be able to find a #define. */
     180             :   if( FD_UNLIKELY( 0==strcmp( type_meta->name, "fd_pubkey" ) ) ) {
     181             :     return -1;
     182             :   } else if( strcmp( "fd_vote_instruction", type_meta->name ) == 0 && size >= sizeof(uint) ) {
     183             :     uint discriminant = *(uint *)data;
     184             :     if (discriminant == 14 || discriminant == 15) {
     185             :       return -1;
     186             :     }
     187             :   } else if( strcmp( "fd_gossip_msg", type_meta->name ) == 0 && size >= sizeof(uint)) {
     188             :     uint discriminant = *(uint *)data;
     189             :     if (discriminant == 0 || discriminant == 1 || discriminant == 2) {
     190             :       return -1;
     191             :     }
     192             :   }
     193             : 
     194             :   if( is_blacklisted( type_meta->name ) ) {
     195             :     return -1;
     196             :   }
     197             : 
     198             :   fd_decode_fuzz_data( type_meta, data, size );
     199             : 
     200             :   return 0;
     201             : }
     202             : 
     203             : ulong
     204             : LLVMFuzzerCustomMutator( uchar * data,
     205             :                          ulong   size,
     206             :                          ulong   max_size,
     207             :                          uint    seed ) {
     208             : 
     209             :   fd_rng_t _rng[1];
     210             :   fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, seed, 0UL ) );
     211             :   int use_generate = fd_rng_uchar( rng ) % 2 == 0;
     212             : 
     213             :   fd_types_vt_t const * type_meta;
     214             :   if( !use_generate ) {
     215             : 
     216             :     ulong mutated_size = LLVMFuzzerMutate( data, size, max_size );
     217             :     data[0] %= (uchar)fd_types_vt_list_cnt;
     218             :     type_meta = &fd_types_vt_list[ data[0] ];
     219             : 
     220             :     // dont bother bruteforcing replace with a structured input
     221             :     int use_generate = is_blacklisted( type_meta->name );
     222             :     if( strcmp( "fd_vote_instruction", type_meta->name ) == 0 ) {
     223             :       uint discriminant = *(uint *)(data+1);
     224             :       use_generate = (discriminant == 14 || discriminant == 15) ? 1 : 0;
     225             :     }
     226             :     else if( strcmp( "fd_gossip_msg", type_meta->name ) == 0 ) {
     227             :       uint discriminant = *(uint *)(data+1);
     228             :       use_generate = (discriminant == 0 || discriminant == 1 || discriminant == 2) ? 1 : 0;
     229             :     }
     230             : 
     231             :     if( !use_generate ) return mutated_size;
     232             :   }
     233             : 
     234             :   // generate inputs
     235             :   void *(*generate_fun)(void *, void **, fd_rng_t *) = NULL;
     236             : 
     237             :   // lookup callbacks
     238             :   do {
     239             :     generate_fun = NULL;
     240             : 
     241             :     data[0] = (uchar)( (uchar)fd_rng_uchar( rng ) % (uchar)fd_types_vt_list_cnt );
     242             :     type_meta = &fd_types_vt_list[ data[0] ];
     243             :     if( is_blacklisted( type_meta->name ) ) continue;
     244             : 
     245             :     char fp[255];
     246             :     sprintf( fp, "%s_generate", type_meta->name );
     247             :     generate_fun = (void *(*)(void *, void **, fd_rng_t *))(ulong)dlsym( RTLD_DEFAULT, fp );
     248             : 
     249             :   } while ( !generate_fun || !type_meta->encode );
     250             : 
     251             :   FD_SCRATCH_SCOPE_BEGIN {
     252             : 
     253             :     void * smem = fd_scratch_alloc( 1, 16384 );
     254             :     void * mem = smem;
     255             : 
     256             :     // generate and encode the payload
     257             :     void * type = generate_fun( mem, &mem, rng );
     258             : 
     259             :     size_t written;
     260             :     int err = encode_type( type_meta, type, data + 1, max_size - 1, &written );
     261             :     if( err != FD_BINCODE_SUCCESS ) {
     262             :       if( err == FD_BINCODE_ERR_OVERFLOW ) {
     263             :         FD_LOG_DEBUG(( "encoding failed for: %s (err: %d)", type_meta->name, err ));
     264             :         // This type is just too large to fit in the max_size (4095 byte) buffer
     265             :         return 0;
     266             :       }
     267             :       FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_meta->name, err ));
     268             :     }
     269             :     size = written;
     270             : 
     271             :   } FD_SCRATCH_SCOPE_END;
     272             : 
     273             :   return size;
     274             : }

Generated by: LCOV version 1.14