LCOV - code coverage report
Current view: top level - flamenco/types - fuzz_types_decode.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 10 127 7.9 %
Date: 2025-10-14 04:31:44 Functions: 1 7 14.3 %

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

Generated by: LCOV version 1.14