Line data Source code
1 : #if !FD_HAS_HOSTED 2 : #error "This target requires FD_HAS_HOSTED" 3 : #endif 4 : 5 : #include <stdio.h> 6 : #include <stdlib.h> 7 : 8 : #include "../../util/fd_util.h" 9 : #include "../../util/sanitize/fd_fuzz.h" 10 : #include "fd_reedsol.h" 11 : 12 : int 13 : LLVMFuzzerInitialize( int * argc, 14 12 : char *** argv ) { 15 : /* Set up shell without signal handlers */ 16 12 : putenv( "FD_LOG_BACKTRACE=0" ); 17 12 : setenv( "FD_LOG_PATH", "", 0 ); 18 12 : fd_boot( argc, argv ); 19 12 : atexit( fd_halt ); 20 12 : fd_log_level_core_set(3); /* crash on warning log */ 21 12 : return 0; 22 12 : } 23 : 24 : #define SHRED_SZ_MIN ( 32UL ) 25 : #define SHRED_SZ_MAX ( 63UL ) 26 : 27 : struct reedsol_test { 28 : ulong shred_sz; 29 : ulong data_shred_cnt; 30 : ulong parity_shred_cnt; 31 : ulong erased_shred_cnt; 32 : ulong corrupt_shred_idx; 33 : uchar data[ ]; 34 : }; 35 : typedef struct reedsol_test reedsol_test_t; 36 : 37 : int 38 : LLVMFuzzerTestOneInput( uchar const * data, 39 : ulong size ) { 40 : if( FD_UNLIKELY( size<sizeof( reedsol_test_t ) ) ) return -1; 41 : reedsol_test_t const * test = ( reedsol_test_t const * ) data; 42 : 43 : fd_rng_t _rng[1]; 44 : fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, 0U, 0UL ) ); 45 : 46 : uchar mem[ FD_REEDSOL_FOOTPRINT ] __attribute__((aligned(FD_REEDSOL_ALIGN))); 47 : 48 : uchar const * data_shreds = test->data; 49 : uchar parity_shreds[ SHRED_SZ_MAX * FD_REEDSOL_PARITY_SHREDS_MAX ]; 50 : uchar recovered_shreds[ SHRED_SZ_MAX * ( FD_REEDSOL_PARITY_SHREDS_MAX+1UL ) ]; 51 : 52 : uchar const * d[ FD_REEDSOL_DATA_SHREDS_MAX ]; 53 : uchar * p[ FD_REEDSOL_PARITY_SHREDS_MAX ]; 54 : uchar * r[ FD_REEDSOL_PARITY_SHREDS_MAX+1UL ]; 55 : uchar const * erased_truth[ FD_REEDSOL_PARITY_SHREDS_MAX+1UL ]; 56 : 57 : ulong shred_sz = SHRED_SZ_MIN + test->shred_sz % ( SHRED_SZ_MAX-SHRED_SZ_MIN+1UL ); 58 : ulong d_cnt = test->data_shred_cnt % FD_REEDSOL_DATA_SHREDS_MAX + 1UL; 59 : ulong p_cnt = test->parity_shred_cnt % FD_REEDSOL_PARITY_SHREDS_MAX + 1UL; 60 : ulong e_cnt = test->erased_shred_cnt % ( p_cnt+2UL ); 61 : ulong corrupt_idx = test->corrupt_shred_idx % ( d_cnt+p_cnt ); 62 : 63 : if( FD_UNLIKELY( size < sizeof( reedsol_test_t ) + shred_sz*d_cnt ) ) return -1; 64 : 65 : for( ulong i=0UL; i<d_cnt; i++ ) d[ i ] = data_shreds + shred_sz*i; 66 : for( ulong i=0UL; i<p_cnt; i++ ) p[ i ] = parity_shreds + shred_sz*i; 67 : for( ulong i=0UL; i<e_cnt; i++ ) r[ i ] = recovered_shreds + shred_sz*i; 68 : 69 : fd_reedsol_t * rs = fd_reedsol_encode_init( mem, shred_sz ); 70 : for( ulong i=0UL; i<d_cnt; i++ ) fd_reedsol_encode_add_data_shred( rs, d[ i ] ); 71 : for( ulong i=0UL; i<p_cnt; i++ ) fd_reedsol_encode_add_parity_shred( rs, p[ i ] ); 72 : fd_reedsol_encode_fini( rs ); 73 : 74 : /* Use reservoir sampling to select exactly e_cnt of the shreds 75 : to erased */ 76 : ulong erased_cnt = 0UL; 77 : rs = fd_reedsol_recover_init( mem, shred_sz ); 78 : for( ulong i=0UL; i<d_cnt; i++ ) { 79 : /* Erase with probability: 80 : (e_cnt - erased_cnt)/(d_cnt + p_cnt - i) */ 81 : if( fd_rng_ulong_roll( rng, d_cnt+p_cnt-i ) < (e_cnt-erased_cnt) ) { 82 : erased_truth[ erased_cnt ] = d[ i ]; 83 : fd_reedsol_recover_add_erased_shred( rs, 1, r[ erased_cnt++ ] ); 84 : } else fd_reedsol_recover_add_rcvd_shred( rs, 1, d[ i ] ); 85 : } 86 : for( ulong i=0UL; i<p_cnt; i++ ) { 87 : if( fd_rng_ulong_roll( rng, p_cnt-i ) < (e_cnt-erased_cnt) ) { 88 : erased_truth[ erased_cnt ] = p[ i ]; 89 : fd_reedsol_recover_add_erased_shred( rs, 0, r[ erased_cnt++ ] ); 90 : } else fd_reedsol_recover_add_rcvd_shred( rs, 0, p[ i ] ); 91 : } 92 : 93 : if( FD_UNLIKELY( erased_cnt!=e_cnt ) ) { 94 : /* If this fails, the test is wrong. */ 95 : __builtin_trap(); 96 : } 97 : int retval = fd_reedsol_recover_fini( rs ); 98 : 99 : if( FD_UNLIKELY( e_cnt>p_cnt ) ) { 100 : if( FD_UNLIKELY( retval!=FD_REEDSOL_ERR_PARTIAL ) ) { 101 : __builtin_trap(); 102 : } 103 : } else { 104 : if( FD_UNLIKELY( FD_REEDSOL_SUCCESS!=retval ) ) { 105 : __builtin_trap(); 106 : } 107 : 108 : for( ulong i=0UL; i<e_cnt; i++ ) { 109 : if( FD_UNLIKELY( memcmp( erased_truth[ i ], r[ i ], shred_sz ) ) ) { 110 : __builtin_trap(); 111 : } 112 : } 113 : } 114 : 115 : /* Corrupt one shred and make sure it gets caught */ 116 : uchar corrupt_shred[ SHRED_SZ_MAX ]; 117 : ulong byte_idx = fd_rng_ulong_roll( rng, shred_sz ); 118 : if( corrupt_idx<d_cnt ) { 119 : fd_memcpy( corrupt_shred, d[ corrupt_idx ], shred_sz ); 120 : corrupt_shred[ byte_idx ] ^= (uchar)1; 121 : d[ corrupt_idx ] = &corrupt_shred[0]; 122 : } else p[ corrupt_idx-d_cnt ][ byte_idx ] ^= (uchar)1; 123 : 124 : rs = fd_reedsol_recover_init( mem, shred_sz ); 125 : for( ulong i=0UL; i<d_cnt; i++ ) fd_reedsol_recover_add_rcvd_shred( rs, 1, d[ i ] ); 126 : for( ulong i=0UL; i<p_cnt; i++ ) fd_reedsol_recover_add_rcvd_shred( rs, 0, p[ i ] ); 127 : 128 : if( FD_UNLIKELY( FD_REEDSOL_ERR_CORRUPT!=fd_reedsol_recover_fini( rs ) ) ) { 129 : __builtin_trap(); 130 : } 131 : 132 : if( corrupt_idx<d_cnt ) d[ corrupt_idx ] = data_shreds + shred_sz*corrupt_idx; 133 : else p[ corrupt_idx-d_cnt ][ byte_idx ] ^= (uchar)1; 134 : 135 : fd_rng_delete( fd_rng_leave( rng ) ); 136 : FD_FUZZ_MUST_BE_COVERED; 137 : return 0; 138 : }