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 12 : char *** argv ) {
100 : /* Set up shell without signal handlers */
101 12 : putenv( "FD_LOG_BACKTRACE=0" );
102 12 : fd_boot( argc, argv );
103 :
104 12 : static uchar scratch_mem [ 1UL<<30 ]; /* 1 GB */
105 12 : static ulong scratch_fmem[ 4UL ] __attribute((aligned(FD_SCRATCH_FMEM_ALIGN)));
106 12 : fd_scratch_attach( scratch_mem, scratch_fmem, 1UL<<30, 4UL );
107 :
108 12 : atexit( fd_halt );
109 12 : atexit( fd_scratch_detach_null );
110 12 : return 0;
111 12 : }
112 :
113 : static void
114 : fd_decode_fuzz_data( fd_types_vt_t const * type_meta,
115 : uchar const * data,
116 0 : ulong size ) {
117 :
118 :
119 0 : FD_SCRATCH_SCOPE_BEGIN {
120 :
121 0 : void * decoded = NULL;
122 0 : ulong written = 0UL;
123 0 : int err = decode_type( type_meta, data, &decoded, size, &written );
124 0 : if( err != FD_BINCODE_SUCCESS ) {
125 0 : return;
126 0 : }
127 :
128 0 : char const * type_name = type_meta->name;
129 0 : void * encoded_buffer = fd_scratch_alloc( 1, 100000 );
130 0 : err = encode_type( type_meta, decoded, encoded_buffer, 100000, &written );
131 0 : if ( err != FD_BINCODE_SUCCESS ) {
132 0 : FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_name, err ));
133 0 : }
134 :
135 0 : void * decoded_normalized = NULL;
136 0 : ulong written_normalized = 0UL;
137 0 : int err_normalized = decode_type( type_meta, encoded_buffer, &decoded_normalized, written, &written_normalized );
138 0 : if( err_normalized != FD_BINCODE_SUCCESS ) {
139 0 : return;
140 0 : }
141 :
142 0 : void * encoded_buffer_normalized = fd_scratch_alloc( 1, 50000 );
143 :
144 0 : err = encode_type( type_meta, decoded_normalized, encoded_buffer_normalized, 50000, &written_normalized );
145 0 : if ( err != FD_BINCODE_SUCCESS ) {
146 0 : FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_name, err ));
147 0 : }
148 :
149 0 : if( written_normalized > written ) {
150 0 : FD_LOG_HEXDUMP_WARNING(( "normalized data", encoded_buffer, written ));
151 0 : FD_LOG_HEXDUMP_WARNING(( "encoded", encoded_buffer_normalized, written_normalized ));
152 0 : FD_LOG_CRIT(( "encoded size (%lu) > data size (%lu) after decode-encode for: %s", written_normalized, written, type_name ));
153 0 : }
154 0 : if( memcmp( encoded_buffer, encoded_buffer_normalized, written ) != 0 ) {
155 0 : FD_LOG_HEXDUMP_WARNING(( "normalized data", encoded_buffer, written ));
156 0 : FD_LOG_HEXDUMP_WARNING(( "encoded", encoded_buffer_normalized, written ));
157 0 : FD_LOG_CRIT(( "encoded data differs from the original data after decode-encode for: %s", type_name ));
158 0 : }
159 :
160 0 : } FD_SCRATCH_SCOPE_END;
161 :
162 0 : }
163 :
164 : int
165 : LLVMFuzzerTestOneInput( uchar const * data,
166 : ulong size ) {
167 :
168 : if( FD_UNLIKELY( size == 0 ) ) return 0;
169 :
170 : assert( fd_types_vt_list_cnt < 256 );
171 : if( FD_UNLIKELY( data[0]>=fd_types_vt_list_cnt ) ) return -1;
172 : fd_types_vt_t const * type_meta = &fd_types_vt_list[ data[0] ];
173 : data = data + 1;
174 : size = size - 1;
175 :
176 : /* fd_pubkey is a #define alias for fd_hash. It is therefore already
177 : fuzzed. Furthermore, dlsym will not be able to find a #define. */
178 : if( FD_UNLIKELY( 0==strcmp( type_meta->name, "fd_pubkey" ) ) ) {
179 : return -1;
180 : } else if( strcmp( "fd_vote_instruction", type_meta->name ) == 0 && size >= sizeof(uint) ) {
181 : uint discriminant = *(uint *)data;
182 : if (discriminant == 14 || discriminant == 15) {
183 : return -1;
184 : }
185 : } else if( strcmp( "fd_gossip_msg", type_meta->name ) == 0 && size >= sizeof(uint)) {
186 : uint discriminant = *(uint *)data;
187 : if (discriminant == 0 || discriminant == 1 || discriminant == 2) {
188 : return -1;
189 : }
190 : }
191 :
192 : if( is_blacklisted( type_meta->name ) ) {
193 : return -1;
194 : }
195 :
196 : fd_decode_fuzz_data( type_meta, data, size );
197 :
198 : return 0;
199 : }
200 :
201 : ulong
202 : LLVMFuzzerCustomMutator( uchar * data,
203 : ulong size,
204 : ulong max_size,
205 : uint seed ) {
206 :
207 : fd_rng_t _rng[1];
208 : fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, seed, 0UL ) );
209 : int use_generate = fd_rng_uchar( rng ) % 2 == 0;
210 :
211 : fd_types_vt_t const * type_meta;
212 : if( !use_generate ) {
213 :
214 : ulong mutated_size = LLVMFuzzerMutate( data, size, max_size );
215 : data[0] %= (uchar)fd_types_vt_list_cnt;
216 : type_meta = &fd_types_vt_list[ data[0] ];
217 :
218 : // dont bother bruteforcing replace with a structured input
219 : int use_generate = is_blacklisted( type_meta->name );
220 : if( strcmp( "fd_vote_instruction", type_meta->name ) == 0 ) {
221 : uint discriminant = *(uint *)(data+1);
222 : use_generate = (discriminant == 14 || discriminant == 15) ? 1 : 0;
223 : }
224 : else if( strcmp( "fd_gossip_msg", type_meta->name ) == 0 ) {
225 : uint discriminant = *(uint *)(data+1);
226 : use_generate = (discriminant == 0 || discriminant == 1 || discriminant == 2) ? 1 : 0;
227 : }
228 :
229 : if( !use_generate ) return mutated_size;
230 : }
231 :
232 : // generate inputs
233 : void *(*generate_fun)(void *, void **, fd_rng_t *) = NULL;
234 :
235 : // lookup callbacks
236 : do {
237 : generate_fun = NULL;
238 :
239 : data[0] = (uchar)( (uchar)fd_rng_uchar( rng ) % (uchar)fd_types_vt_list_cnt );
240 : type_meta = &fd_types_vt_list[ data[0] ];
241 : if( is_blacklisted( type_meta->name ) ) continue;
242 :
243 : char fp[255];
244 : sprintf( fp, "%s_generate", type_meta->name );
245 : generate_fun = (void *(*)(void *, void **, fd_rng_t *))(ulong)dlsym( RTLD_DEFAULT, fp );
246 :
247 : } while ( !generate_fun || !type_meta->encode );
248 :
249 : FD_SCRATCH_SCOPE_BEGIN {
250 :
251 : void * smem = fd_scratch_alloc( 1, 16384 );
252 : void * mem = smem;
253 :
254 : // generate and encode the payload
255 : void * type = generate_fun( mem, &mem, rng );
256 :
257 : size_t written;
258 : int err = encode_type( type_meta, type, data + 1, max_size - 1, &written );
259 : if( err != FD_BINCODE_SUCCESS ) {
260 : if( err == FD_BINCODE_ERR_OVERFLOW ) {
261 : FD_LOG_DEBUG(( "encoding failed for: %s (err: %d)", type_meta->name, err ));
262 : // This type is just too large to fit in the max_size (4095 byte) buffer
263 : return 0;
264 : }
265 : FD_LOG_CRIT(( "encoding failed for: %s (err: %d)", type_meta->name, err ));
266 : }
267 : size = written;
268 :
269 : } FD_SCRATCH_SCOPE_END;
270 :
271 : return size;
272 : }
|