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