Skip to content

Commit a85c56e

Browse files
committed
elf: solfuzz v2 harness
1 parent ea1bd0d commit a85c56e

25 files changed

+3143
-22
lines changed

NOTICE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,3 +545,24 @@ https://github.com/solana-program/token/tree/program%40v8.0.0/p-token
545545
http://www.apache.org/licenses/
546546

547547
=======================================================================
548+
549+
The flatbuffers generated code in src/flamenco/runtime/tests/flatbuffers/
550+
is generated by flatcc.
551+
552+
https://github.com/dvidelabs/flatcc/
553+
554+
Copyright 2015 Mikkel F. Jørgensen, dvide.com
555+
556+
Licensed under the Apache License, Version 2.0 (the "License");
557+
you may not use this file except in compliance with the License.
558+
You may obtain a copy of the License at
559+
560+
http://www.apache.org/licenses/LICENSE-2.0
561+
562+
Unless required by applicable law or agreed to in writing, software
563+
distributed under the License is distributed on an "AS IS" BASIS,
564+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
565+
See the License for the specific language governing permissions and
566+
limitations under the License.
567+
568+
=======================================================================

config/extra/with-arm.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ include config/extra/with-bzip2.mk
3434
include config/extra/with-lz4.mk
3535
include config/extra/with-openssl.mk
3636
include config/extra/with-rocksdb.mk
37+
include config/extra/with-flatcc.mk
3738

3839
endif
3940

config/extra/with-flatcc.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ifneq (,$(wildcard $(OPT)/lib/libflatcc.a))
2+
FLATCC_LIBS:=$(OPT)/lib/libflatcc.a $(OPT)/lib/libflatccrt.a
3+
else
4+
$(info "flatcc not installed, skipping")
5+
endif

config/extra/with-x86-64.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ include config/extra/with-bzip2.mk
2020
include config/extra/with-lz4.mk
2121
include config/extra/with-openssl.mk
2222
include config/extra/with-rocksdb.mk
23+
include config/extra/with-flatcc.mk
2324

2425
FD_ARCH_SUPPORTS_SANDBOX:=1

deps.sh

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,16 @@ fetch () {
133133
if [[ $MSAN == 1 ]]; then
134134
checkout_llvm
135135
fi
136-
checkout_repo zstd https://github.com/facebook/zstd "v1.5.7"
137-
checkout_repo lz4 https://github.com/lz4/lz4 "v1.10.0"
138-
checkout_repo s2n https://github.com/awslabs/s2n-bignum "" "4d2e22a"
139-
checkout_repo openssl https://github.com/openssl/openssl "openssl-3.6.0"
140-
checkout_repo secp256k1 https://github.com/bitcoin-core/secp256k1 "v0.7.0"
136+
checkout_repo zstd https://github.com/facebook/zstd "v1.5.7"
137+
checkout_repo lz4 https://github.com/lz4/lz4 "v1.10.0"
138+
checkout_repo s2n https://github.com/awslabs/s2n-bignum "" "4d2e22a"
139+
checkout_repo openssl https://github.com/openssl/openssl "openssl-3.6.0"
140+
checkout_repo secp256k1 https://github.com/bitcoin-core/secp256k1 "v0.7.0"
141+
checkout_repo flatcc https://github.com/firedancer-io/flatcc.git "master"
141142
if [[ $DEVMODE == 1 ]]; then
142-
checkout_repo bzip2 https://gitlab.com/bzip2/bzip2 "bzip2-1.0.8"
143-
checkout_repo rocksdb https://github.com/facebook/rocksdb "v10.5.1"
144-
checkout_repo snappy https://github.com/google/snappy "1.2.2"
143+
checkout_repo bzip2 https://gitlab.com/bzip2/bzip2 "bzip2-1.0.8"
144+
checkout_repo rocksdb https://github.com/facebook/rocksdb "v10.5.1"
145+
checkout_repo snappy https://github.com/google/snappy "1.2.2"
145146
fi
146147
}
147148

@@ -610,6 +611,21 @@ install_snappy () {
610611
echo "[+] Successfully installed snappy"
611612
}
612613

614+
install_flatcc () {
615+
echo "[+] Installing flatcc"
616+
cd "$PREFIX/git/flatcc"
617+
cmake -B build \
618+
-DCMAKE_INSTALL_PREFIX=$PREFIX \
619+
-DCMAKE_BUILD_TYPE=Release \
620+
-DFLATCC_INSTALL=ON \
621+
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
622+
-DCMAKE_CXX_FLAGS="$EXTRA_CXXFLAGS" \
623+
-DCMAKE_EXE_LINKER_FLAGS="$EXTRA_LDFLAGS"
624+
cmake --build build -j
625+
cmake --install build
626+
echo "[+] Successfully installed flatcc"
627+
}
628+
613629
install () {
614630
CC="$(command -v $_CC)"
615631
cc="$CC"
@@ -638,6 +654,7 @@ install () {
638654
( install_bzip2 )
639655
( install_snappy )
640656
( install_rocksdb )
657+
( install_flatcc )
641658
fi
642659

643660
# Merge lib64 with lib

src/flamenco/runtime/tests/Local.mk

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ $(call add-objs,fd_sol_compat,fd_flamenco_test)
1111
$(call add-hdrs,generated/context.pb.h,generated/elf.pb.h,generated/invoke.pb.h,generated/txn.pb.h,generated/block.pb.h,generated/vm.pb.h,generated/type.pb.h,generated/shred.pb.h generated/metadata.pb.h)
1212
$(call add-objs,generated/context.pb generated/elf.pb generated/invoke.pb generated/txn.pb generated/block.pb generated/vm.pb generated/type.pb generated/shred.pb generated/metadata.pb,fd_flamenco)
1313

14+
$(call add-hdrs,flatbuffers/generated/elf_builder.h,flatbuffers/generated/elf_reader.h)
15+
1416
SOL_COMPAT_FLAGS:=-Wl,--undefined=fd_types_vt_by_name -Wl,--version-script=src/flamenco/runtime/tests/libfd_exec_sol_compat.map
15-
$(call make-unit-test,test_sol_compat,test_sol_compat,fd_flamenco_test fd_flamenco fd_tango fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS))
16-
$(call make-shared,libfd_exec_sol_compat.so,fd_sol_compat,fd_flamenco_test fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS) $(SOL_COMPAT_FLAGS))
17+
$(call make-unit-test,test_sol_compat,test_sol_compat,fd_flamenco_test fd_flamenco fd_tango fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS) $(FLATCC_LIBS))
18+
$(call make-shared,libfd_exec_sol_compat.so,fd_sol_compat,fd_flamenco_test fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS) $(FLATCC_LIBS) $(SOL_COMPAT_FLAGS))
1719
$(call make-unit-test,test_sol_compat_so,test_sol_compat_so,fd_util)
1820

19-
$(call make-unit-test,test_dump_block,test_dump_block,fd_flamenco_test fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS))
21+
$(call make-unit-test,test_dump_block,test_dump_block,fd_flamenco_test fd_flamenco fd_funk fd_ballet fd_util fd_disco,$(SECP256K1_LIBS) $(FLATCC_LIBS))
2022

2123
run-runtime-backtest: $(OBJDIR)/bin/fd_ledger $(OBJDIR)/bin/firedancer-dev
2224
OBJDIR=$(OBJDIR) src/flamenco/runtime/tests/run_backtest_ci.sh $(BACKTEST_ARGS)

src/flamenco/runtime/tests/fd_elf_harness.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
#include "fd_solfuzz.h"
22
#include "fd_solfuzz_private.h"
3+
#include "flatbuffers/generated/flatbuffers_common_builder.h"
4+
#include "flatbuffers/generated/flatbuffers_common_reader.h"
35
#include "generated/elf.pb.h"
46
#include "../../../ballet/sbpf/fd_sbpf_loader.h"
57
#include "../program/fd_bpf_loader_program.h"
68
#include "../../vm/fd_vm_base.h"
79
#include "../../progcache/fd_prog_load.h"
810

11+
#include "flatbuffers/generated/elf_reader.h"
12+
#include "flatbuffers/generated/elf_builder.h"
13+
914
#define SORT_NAME sort_ulong
1015
#define SORT_KEY_T ulong
1116
#define SORT_BEFORE(a,b) (a)<(b)
@@ -134,3 +139,110 @@ fd_solfuzz_pb_elf_loader_run( fd_solfuzz_runner_t * runner,
134139
*output = elf_effects;
135140
return actual_end - (ulong) output_buf;
136141
}
142+
143+
void
144+
fd_solfuzz_fb_elf_loader_build_err_effects( fd_solfuzz_runner_t * runner, int err ) {
145+
FD_TEST( !SOL_COMPAT_NS(ELFLoaderEffects_start_as_root)( runner->fb_builder ) );
146+
FD_TEST( !SOL_COMPAT_NS(ELFLoaderEffects_err_code_add)( runner->fb_builder, (uchar)(-err) ) );
147+
FD_TEST( SOL_COMPAT_NS(ELFLoaderEffects_end_as_root)( runner->fb_builder ) );
148+
}
149+
150+
int
151+
fd_solfuzz_fb_elf_loader_run( fd_solfuzz_runner_t * runner,
152+
void const * input_ ) {
153+
SOL_COMPAT_NS(ELFLoaderCtx_table_t) input = fd_type_pun_const( input_ );
154+
155+
fd_spad_t * spad = runner->spad;
156+
flatbuffers_uint8_vec_t elf_bin_ = SOL_COMPAT_NS(ELFLoaderCtx_elf_data( input ));
157+
uchar const * elf_bin = (uchar const*)elf_bin_;
158+
ulong elf_sz = flatbuffers_uint8_vec_len( elf_bin_ );
159+
160+
/* Restore feature set */
161+
fd_features_t feature_set = {0};
162+
fd_solfuzz_fb_restore_features( &feature_set, SOL_COMPAT_NS(ELFLoaderCtx_features( input )));
163+
164+
fd_sbpf_loader_config_t config = {
165+
.elf_deploy_checks = SOL_COMPAT_NS(ELFLoaderCtx_deploy_checks( input )),
166+
};
167+
168+
fd_prog_versions_t versions = fd_prog_versions( &feature_set, UINT_MAX );
169+
config.sbpf_min_version = versions.min_sbpf_version;
170+
config.sbpf_max_version = versions.max_sbpf_version;
171+
172+
/* Peek */
173+
fd_sbpf_elf_info_t info;
174+
int err = fd_sbpf_elf_peek( &info, elf_bin, elf_sz, &config );
175+
if( err ) {
176+
fd_solfuzz_fb_elf_loader_build_err_effects( runner, err );
177+
return SOL_COMPAT_V2_SUCCESS;
178+
}
179+
180+
/* Set up loading context */
181+
void * rodata = fd_spad_alloc_check( spad, FD_SBPF_PROG_RODATA_ALIGN, info.bin_sz );
182+
fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_spad_alloc_check( spad, fd_sbpf_program_align(), fd_sbpf_program_footprint( &info ) ), &info, rodata );
183+
fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc_check( spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ));
184+
void * rodata_scratch = fd_spad_alloc_check( spad, 1UL, elf_sz );
185+
186+
/* Register syscalls given the active feature set. We can pass in an
187+
arbitrary slot as its just used to check if features should be
188+
active or not. */
189+
FD_TEST( !fd_vm_syscall_register_slot( syscalls, UINT_MAX, &feature_set, !!config.elf_deploy_checks ) );
190+
191+
/* Load */
192+
err = fd_sbpf_program_load( prog, elf_bin, elf_sz, syscalls, &config, rodata_scratch, elf_sz );
193+
if( err ) {
194+
fd_solfuzz_fb_elf_loader_build_err_effects( runner, err );
195+
return SOL_COMPAT_V2_SUCCESS;
196+
}
197+
198+
/**** Capture effects ****/
199+
200+
/* Error code */
201+
uchar out_err_code = FD_SBPF_ELF_SUCCESS;
202+
203+
/* Rodata */
204+
ulong out_rodata_hash_u64 = fd_hash( 0UL, prog->rodata, prog->rodata_sz );
205+
SOL_COMPAT_NS(XXHash_t) out_rodata_hash;
206+
fd_memcpy( out_rodata_hash.hash, &out_rodata_hash_u64, sizeof(ulong) );
207+
208+
/* Text count */
209+
ulong out_text_cnt = prog->info.text_cnt;
210+
211+
/* Text off */
212+
ulong out_text_off = prog->info.text_off;
213+
214+
/* Entry PC */
215+
ulong out_entry_pc = prog->entry_pc;
216+
217+
/* Calldests */
218+
ulong max_out_calldests_cnt = 1UL + ( prog->calldests ? fd_sbpf_calldests_cnt( prog->calldests ) : 0UL );
219+
ulong * tmp_out_calldests = fd_spad_alloc_check( spad, alignof(ulong), sizeof(ulong)*max_out_calldests_cnt );
220+
ulong out_calldests_cnt = 0UL;
221+
222+
/* Add the entrypoint to the calldests */
223+
tmp_out_calldests[out_calldests_cnt++] = prog->entry_pc;
224+
225+
/* Add the rest of the calldests */
226+
if( FD_LIKELY( prog->calldests ) ) {
227+
for( ulong target_pc=fd_sbpf_calldests_const_iter_init(prog->calldests);
228+
!fd_sbpf_calldests_const_iter_done(target_pc);
229+
target_pc=fd_sbpf_calldests_const_iter_next(prog->calldests, target_pc) ) {
230+
if( FD_LIKELY( target_pc!=prog->entry_pc ) ) {
231+
tmp_out_calldests[out_calldests_cnt++] = target_pc;
232+
}
233+
}
234+
}
235+
236+
/* Sort the calldests in ascending order */
237+
sort_ulong_inplace( tmp_out_calldests, out_calldests_cnt );
238+
239+
/* Create output calldests vector */
240+
ulong out_calldests_hash_u64 = fd_hash( 0UL, tmp_out_calldests, sizeof(ulong) * out_calldests_cnt );
241+
SOL_COMPAT_NS(XXHash_t) out_calldests_hash;
242+
fd_memcpy( out_calldests_hash.hash, &out_calldests_hash_u64, sizeof(ulong) );
243+
244+
/* Build effects */
245+
SOL_COMPAT_NS(ELFLoaderEffects_create_as_root)( runner->fb_builder, out_err_code, &out_rodata_hash, out_text_cnt, out_text_off, out_entry_pc, &out_calldests_hash );
246+
247+
return SOL_COMPAT_V2_SUCCESS;
248+
}

src/flamenco/runtime/tests/fd_harness_common.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,22 @@ fd_solfuzz_pb_restore_features( fd_features_t * features,
6969
}
7070
return 1;
7171
}
72+
73+
void
74+
fd_solfuzz_fb_restore_features( fd_features_t * features,
75+
SOL_COMPAT_NS(FeatureSet_table_t) feature_set ) {
76+
if( FD_UNLIKELY( !feature_set ) ) return;
77+
78+
fd_features_disable_all( features );
79+
flatbuffers_uint64_vec_t input_features = SOL_COMPAT_NS(FeatureSet_features( feature_set ));
80+
ulong input_features_cnt = flatbuffers_uint64_vec_len( input_features );
81+
for( ulong i=0UL; i<input_features_cnt; i++ ) {
82+
ulong prefix = flatbuffers_uint64_vec_at( input_features, i );
83+
fd_feature_id_t const * id = fd_feature_id_query( prefix );
84+
if( FD_UNLIKELY( !id ) ) {
85+
FD_LOG_ERR(( "unsupported feature ID 0x%016lx", prefix ));
86+
}
87+
/* Enabled since genesis */
88+
fd_features_set( features, id, 0UL );
89+
}
90+
}

src/flamenco/runtime/tests/fd_sol_compat.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#include "generated/txn.pb.h"
1616
#include "generated/type.pb.h"
1717

18+
#include "flatbuffers/generated/elf_reader.h"
19+
#include "flatbuffers/generated/flatbuffers_common_reader.h"
20+
1821
#include <assert.h>
1922
#include <errno.h>
2023
#include <stdio.h>
@@ -287,3 +290,40 @@ sol_compat_shred_parse_v1( uchar * out,
287290
pb_release( &fd_exec_test_shred_binary_t_msg, input );
288291
return !!sol_compat_encode( out, out_sz, output, &fd_exec_test_accepts_shred_t_msg );
289292
}
293+
294+
/*
295+
* execute_v2
296+
Unlike sol_compat_execute_v1 APIs, v2 APIs use flatbuffers for
297+
zero-copy decoding. Returns SOL_COMPAT_V2_SUCCESS on success and
298+
SOL_COMPAT_V2_FAILURE on failure.
299+
300+
out: output buffer
301+
out_sz: output buffer size
302+
in: input buffer
303+
in_sz: input buffer size (unused)
304+
305+
Since flatbuffers utilizes zero-copy decoding, the v2 API does not
306+
require an input buffer size. Therefore, it is the caller's
307+
responsibility to ensure the input buffer is well-formed (preferably
308+
using a call to _verify_as_root) to avoid any OOB reads.
309+
310+
TODO: Make sol_compat_v2 APIs infallible???
311+
*/
312+
313+
int
314+
sol_compat_elf_loader_v2( uchar * out,
315+
ulong * out_sz,
316+
uchar const * in,
317+
ulong FD_FN_UNUSED in_sz ) {
318+
SOL_COMPAT_NS(ELFLoaderCtx_table_t) input = SOL_COMPAT_NS(ELFLoaderCtx_as_root( in ));
319+
if( FD_UNLIKELY( !input ) ) return 0;
320+
321+
int err = fd_solfuzz_fb_execute_wrapper( runner, input, fd_solfuzz_fb_elf_loader_run );
322+
if( FD_UNLIKELY( err==SOL_COMPAT_V2_FAILURE ) ) return err;
323+
324+
ulong buffer_sz = flatcc_builder_get_buffer_size( runner->fb_builder );
325+
flatcc_builder_copy_buffer( runner->fb_builder, out, buffer_sz );
326+
*out_sz = buffer_sz;
327+
328+
return SOL_COMPAT_V2_SUCCESS;
329+
}

src/flamenco/runtime/tests/fd_solfuzz.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ fd_solfuzz_runner_new( fd_wksp_t * wksp,
117117
runner->runtime_stack = fd_wksp_alloc_laddr( wksp, alignof(fd_runtime_stack_t), sizeof(fd_runtime_stack_t), wksp_tag );
118118
if( FD_UNLIKELY( !runner->runtime_stack ) ) goto bail2;
119119

120+
/* TODO: Consider implementing custom allocators and emitters.
121+
The default builder / emitter uses libc allocators */
122+
int builder_err = flatcc_builder_init( runner->fb_builder );
123+
if( FD_UNLIKELY( builder_err ) ) goto bail2;
124+
120125
runner->spad = fd_spad_join( fd_spad_new( spad_mem, spad_max ) );
121126
if( FD_UNLIKELY( !runner->spad ) ) goto bail2;
122127
runner->banks = fd_banks_join( fd_banks_new( banks_mem, bank_max, fork_max, 0, 8888UL ) );
@@ -161,6 +166,8 @@ fd_solfuzz_runner_delete( fd_solfuzz_runner_t * runner ) {
161166
fd_progcache_admin_leave( runner->progcache_admin, &shpcache );
162167
if( shpcache ) fd_wksp_free_laddr( fd_funk_delete( shpcache ) );
163168

169+
flatcc_builder_clear( runner->fb_builder );
170+
164171
if( runner->spad ) fd_wksp_free_laddr( fd_spad_delete( fd_spad_leave( runner->spad ) ) );
165172
if( runner->banks ) fd_wksp_free_laddr( fd_banks_delete( fd_banks_leave( runner->banks ) ) );
166173
fd_wksp_free_laddr( runner );

0 commit comments

Comments
 (0)