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