Line data Source code
1 : #include "fd_solfuzz.h"
2 : #include "fd_solfuzz_private.h"
3 : #define _GNU_SOURCE
4 : #include "fd_sol_compat.h"
5 :
6 : #include "../fd_executor_err.h"
7 : #include "../../capture/fd_solcap_writer.h"
8 : #include "../../../ballet/shred/fd_shred.h"
9 :
10 : #include "generated/block.pb.h"
11 : #include "generated/elf.pb.h"
12 : #include "generated/invoke.pb.h"
13 : #include "generated/shred.pb.h"
14 : #include "generated/vm.pb.h"
15 : #include "generated/txn.pb.h"
16 :
17 : #if FD_HAS_FLATCC
18 : #include "flatbuffers/generated/elf_reader.h"
19 : #include "flatbuffers/generated/flatbuffers_common_reader.h"
20 : #endif
21 :
22 : #include <assert.h>
23 : #include <errno.h>
24 : #include <fcntl.h>
25 : #include <stdio.h>
26 : #include <unistd.h>
27 :
28 : static fd_wksp_t * wksp = NULL;
29 : static fd_solfuzz_runner_t * runner = NULL;
30 :
31 : static fd_solfuzz_runner_t *
32 0 : sol_compat_setup_runner( fd_solfuzz_runner_options_t const * options ) {
33 0 : runner = fd_solfuzz_runner_new( wksp, 3UL, options );
34 0 : if( FD_UNLIKELY( !runner ) ) {
35 0 : FD_LOG_ERR(( "fd_solfuzz_runner_new() failed" ));
36 0 : return NULL;
37 0 : }
38 :
39 0 : char const * solcap_path = getenv( "FD_SOLCAP" );
40 0 : if( solcap_path ) {
41 0 : int fd = open( solcap_path, O_WRONLY | O_CREAT | O_TRUNC, 0644 );
42 0 : if( FD_UNLIKELY( fd == -1 ) ) {
43 0 : FD_LOG_ERR(( "open($FD_SOLCAP=%s) failed (%i-%s)", solcap_path, errno, fd_io_strerror( errno ) ));
44 0 : }
45 0 : runner->solcap_file = (void *)(ulong)fd;
46 0 : FD_LOG_NOTICE(( "Logging to solcap file %s", solcap_path ));
47 :
48 0 : void * solcap_mem = fd_wksp_alloc_laddr( runner->wksp, fd_solcap_writer_align(), fd_solcap_writer_footprint(), 1UL );
49 0 : runner->solcap = fd_solcap_writer_init( solcap_mem, fd );
50 0 : FD_TEST( runner->solcap );
51 0 : }
52 :
53 0 : return runner;
54 0 : }
55 :
56 : static void
57 0 : sol_compat_cleanup_runner( fd_solfuzz_runner_t * runner ) {
58 : /* Cleanup test runner */
59 0 : if( runner->solcap ) {
60 0 : fd_wksp_free_laddr( ( runner->solcap ) );
61 0 : runner->solcap = NULL;
62 0 : if( runner->solcap_file ) {
63 0 : close( (int)(ulong)runner->solcap_file );
64 0 : runner->solcap_file = NULL;
65 0 : }
66 0 : }
67 0 : fd_solfuzz_runner_delete( runner );
68 0 : }
69 :
70 : void
71 0 : sol_compat_init( int log_level ) {
72 0 : int argc = 1;
73 0 : char * argv[2] = { (char *)"fd_exec_sol_compat", NULL };
74 0 : char ** argv_ = argv;
75 0 : if( !getenv( "FD_LOG_PATH" ) ) {
76 0 : setenv( "FD_LOG_PATH", "", 1 );
77 0 : }
78 :
79 0 : char const * enable_vm_tracing_env = getenv( "ENABLE_VM_TRACING");
80 0 : int enable_vm_tracing = enable_vm_tracing_env!=NULL;
81 0 : fd_solfuzz_runner_options_t options = {
82 0 : .enable_vm_tracing = enable_vm_tracing
83 0 : };
84 :
85 0 : fd_log_enable_unclean_exit();
86 0 : fd_boot( &argc, &argv_ );
87 :
88 0 : if( FD_UNLIKELY( wksp || runner ) ) {
89 0 : FD_LOG_ERR(( "sol_compat_init() called multiple times" ));
90 0 : }
91 :
92 0 : ulong footprint = 7UL<<30;
93 0 : ulong part_max = fd_wksp_part_max_est( footprint, 64UL<<10 );
94 0 : ulong data_max = fd_wksp_data_max_est( footprint, part_max );
95 0 : wksp = fd_wksp_demand_paged_new( "sol_compat", 42U, part_max, data_max );
96 0 : if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_demand_paged_new() failed" ));
97 :
98 0 : runner = sol_compat_setup_runner( &options );
99 0 : if( FD_UNLIKELY( !runner ) ) FD_LOG_ERR(( "sol_compat_setup_runner() failed" ));
100 :
101 0 : fd_log_level_logfile_set( log_level );
102 0 : fd_log_level_core_set(4); /* abort on FD_LOG_ERR */
103 0 : }
104 :
105 : void
106 0 : sol_compat_fini( void ) {
107 0 : sol_compat_cleanup_runner( runner );
108 0 : fd_wksp_delete_anonymous( wksp );
109 0 : wksp = NULL;
110 0 : runner = NULL;
111 0 : fd_halt();
112 0 : }
113 :
114 : sol_compat_features_t const *
115 0 : sol_compat_get_features_v1( void ) {
116 0 : static sol_compat_features_t features;
117 0 : static ulong hardcoded_features[ FD_FEATURE_ID_CNT ];
118 0 : static ulong supported_features[ FD_FEATURE_ID_CNT ];
119 :
120 0 : FD_ONCE_BEGIN {
121 0 : features.struct_size = sizeof(sol_compat_features_t);
122 0 : features.hardcoded_features = hardcoded_features;
123 0 : features.supported_features = supported_features;
124 0 : for( fd_feature_id_t const * iter = fd_feature_iter_init();
125 0 : !fd_feature_iter_done( iter );
126 0 : iter = fd_feature_iter_next( iter ) ) {
127 0 : if( iter->reverted ) continue; /* skip reverted features */
128 :
129 : /* Pretend that features activated on all clusters are hardcoded */
130 0 : if( iter->hardcode_for_fuzzing ) {
131 0 : hardcoded_features[ features.hardcoded_features_cnt++ ] = iter->id.ul[0];
132 0 : } else {
133 0 : supported_features[ features.supported_feature_cnt++ ] = iter->id.ul[0];
134 0 : }
135 0 : }
136 0 : }
137 0 : FD_ONCE_END;
138 :
139 0 : return &features;
140 0 : }
141 :
142 : /*
143 : * execute_v1
144 : */
145 :
146 : int
147 : sol_compat_instr_execute_v1( uchar * out,
148 : ulong * out_sz,
149 : uchar const * in,
150 0 : ulong in_sz ) {
151 0 : fd_exec_test_instr_context_t input[1] = {0};
152 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_instr_context_t_msg );
153 0 : if( FD_UNLIKELY( !res ) ) return 0;
154 :
155 0 : int ok = 0;
156 0 : fd_spad_push( runner->spad );
157 0 : void * output = NULL;
158 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_instr_run );
159 0 : if( output ) {
160 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_instr_effects_t_msg );
161 0 : }
162 0 : fd_spad_pop( runner->spad );
163 :
164 0 : pb_release( &fd_exec_test_instr_context_t_msg, input );
165 0 : fd_solfuzz_runner_leak_check( runner );
166 0 : return ok;
167 0 : }
168 :
169 : int
170 : sol_compat_txn_execute_v1( uchar * out,
171 : ulong * out_sz,
172 : uchar const * in,
173 0 : ulong in_sz ) {
174 0 : fd_exec_test_txn_context_t input[1] = {0};
175 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_txn_context_t_msg );
176 0 : if( FD_UNLIKELY( !res ) ) return 0;
177 :
178 0 : int ok = 0;
179 0 : fd_spad_push( runner->spad );
180 0 : void * output = NULL;
181 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_txn_run );
182 0 : if( output ) {
183 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_txn_result_t_msg );
184 0 : }
185 0 : fd_spad_pop( runner->spad );
186 :
187 0 : pb_release( &fd_exec_test_txn_context_t_msg, input );
188 0 : fd_solfuzz_runner_leak_check( runner );
189 0 : return ok;
190 0 : }
191 :
192 : int
193 : sol_compat_block_execute_v1( uchar * out,
194 : ulong * out_sz,
195 : uchar const * in,
196 0 : ulong in_sz ) {
197 0 : fd_exec_test_block_context_t input[1] = {0};
198 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_block_context_t_msg );
199 0 : if( FD_UNLIKELY( !res ) ) return 0;
200 :
201 0 : fd_spad_push( runner->spad );
202 0 : int ok = 0;
203 0 : void * output = NULL;
204 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_block_run );
205 0 : if( output ) {
206 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_block_effects_t_msg );
207 0 : }
208 0 : fd_spad_pop( runner->spad );
209 :
210 0 : pb_release( &fd_exec_test_block_context_t_msg, input );
211 0 : fd_solfuzz_runner_leak_check( runner );
212 0 : return ok;
213 0 : }
214 :
215 : int
216 : sol_compat_elf_loader_v1( uchar * out,
217 : ulong * out_sz,
218 : uchar const * in,
219 0 : ulong in_sz ) {
220 0 : fd_exec_test_elf_loader_ctx_t input[1] = {0};
221 0 : void * res = sol_compat_decode( &input, in, in_sz, &fd_exec_test_elf_loader_ctx_t_msg );
222 0 : if( FD_UNLIKELY( !res ) ) return 0;
223 :
224 0 : fd_spad_push( runner->spad );
225 0 : int ok = 0;
226 0 : void * output = NULL;
227 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_elf_loader_run );
228 0 : if( output ) {
229 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_elf_loader_effects_t_msg );
230 0 : }
231 0 : fd_spad_pop( runner->spad );
232 :
233 0 : pb_release( &fd_exec_test_elf_loader_ctx_t_msg, input );
234 0 : fd_solfuzz_runner_leak_check( runner );
235 0 : return ok;
236 0 : }
237 :
238 : int
239 : sol_compat_vm_syscall_execute_v1( uchar * out,
240 : ulong * out_sz,
241 : uchar const * in,
242 0 : ulong in_sz ) {
243 0 : fd_exec_test_syscall_context_t input[1] = {0};
244 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_syscall_context_t_msg );
245 0 : if( FD_UNLIKELY( !res ) ) return 0;
246 :
247 0 : fd_spad_push( runner->spad );
248 0 : int ok = 0;
249 0 : void * output = NULL;
250 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_syscall_run );
251 0 : if( output ) {
252 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_syscall_effects_t_msg );
253 0 : }
254 0 : fd_spad_pop( runner->spad );
255 :
256 0 : pb_release( &fd_exec_test_syscall_context_t_msg, input );
257 0 : fd_solfuzz_runner_leak_check( runner );
258 0 : return ok;
259 0 : }
260 :
261 : int
262 : sol_compat_vm_interp_v1( uchar * out,
263 : ulong * out_sz,
264 : uchar const * in,
265 0 : ulong in_sz ) {
266 0 : fd_exec_test_syscall_context_t input[1] = {0};
267 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_syscall_context_t_msg );
268 0 : if( FD_UNLIKELY( !res ) ) return 0;
269 :
270 0 : fd_spad_push( runner->spad );
271 0 : int ok = 0;
272 0 : void * output = NULL;
273 0 : fd_solfuzz_pb_execute_wrapper( runner, input, &output, fd_solfuzz_pb_vm_interp_run );
274 0 : if( output ) {
275 0 : ok = !!sol_compat_encode( out, out_sz, output, &fd_exec_test_syscall_effects_t_msg );
276 0 : }
277 0 : fd_spad_pop( runner->spad );
278 :
279 0 : pb_release( &fd_exec_test_syscall_context_t_msg, input );
280 0 : fd_solfuzz_runner_leak_check( runner );
281 0 : return ok;
282 0 : }
283 :
284 : int
285 : sol_compat_shred_parse_v1( uchar * out,
286 : ulong * out_sz,
287 : uchar const * in,
288 0 : ulong in_sz ) {
289 0 : fd_exec_test_shred_binary_t input[1] = {0};
290 0 : void * res = sol_compat_decode_lenient( &input, in, in_sz, &fd_exec_test_shred_binary_t_msg );
291 0 : if( FD_UNLIKELY( res==NULL ) ) {
292 0 : return 0;
293 0 : }
294 0 : if( FD_UNLIKELY( input[0].data==NULL ) ) {
295 0 : pb_release( &fd_exec_test_shred_binary_t_msg, input );
296 0 : return 0;
297 0 : }
298 0 : fd_exec_test_accepts_shred_t output[1] = {0};
299 0 : output[0].valid = !!fd_shred_parse( input[0].data->bytes, input[0].data->size );
300 0 : pb_release( &fd_exec_test_shred_binary_t_msg, input );
301 0 : return !!sol_compat_encode( out, out_sz, output, &fd_exec_test_accepts_shred_t_msg );
302 0 : }
303 :
304 : /*
305 : * execute_v2
306 : Unlike sol_compat_execute_v1 APIs, v2 APIs use flatbuffers for
307 : zero-copy decoding. Returns SOL_COMPAT_V2_SUCCESS on success and
308 : SOL_COMPAT_V2_FAILURE on failure.
309 :
310 : out: output buffer
311 : out_sz: output buffer size
312 : in: input buffer
313 : in_sz: input buffer size (unused)
314 :
315 : Since flatbuffers utilizes zero-copy decoding, the v2 API does not
316 : require an input buffer size. Therefore, it is the caller's
317 : responsibility to ensure the input buffer is well-formed (preferably
318 : using a call to _verify_as_root) to avoid any OOB reads.
319 :
320 : TODO: Make sol_compat_v2 APIs infallible???
321 : */
322 :
323 : #if FD_HAS_FLATCC
324 :
325 : int
326 : sol_compat_elf_loader_v2( uchar * out,
327 : ulong * out_sz,
328 : uchar const * in,
329 0 : ulong FD_FN_UNUSED in_sz ) {
330 0 : SOL_COMPAT_NS(ELFLoaderCtx_table_t) input = SOL_COMPAT_NS(ELFLoaderCtx_as_root( in ));
331 0 : if( FD_UNLIKELY( !input ) ) return 0;
332 :
333 0 : int err = fd_solfuzz_fb_execute_wrapper( runner, input, fd_solfuzz_fb_elf_loader_run );
334 0 : if( FD_UNLIKELY( err==SOL_COMPAT_V2_FAILURE ) ) return err;
335 :
336 0 : ulong buffer_sz = flatcc_builder_get_buffer_size( runner->fb_builder );
337 0 : flatcc_builder_copy_buffer( runner->fb_builder, out, buffer_sz );
338 0 : *out_sz = buffer_sz;
339 :
340 0 : return SOL_COMPAT_V2_SUCCESS;
341 0 : }
342 :
343 : #endif /* FD_HAS_FLATCC */
|