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