Line data Source code
1 : /* fd_solfuzz.c contains support routines */
2 :
3 : #define _GNU_SOURCE
4 : #include "fd_solfuzz.h"
5 : #include "../fd_bank.h"
6 : #include "../fd_runtime_stack_tmpl.h"
7 : #include "../fd_runtime.h"
8 : #include "../../progcache/fd_progcache_admin.h"
9 : #include <errno.h>
10 : #include <sys/mman.h>
11 : #include <unistd.h>
12 : #include "../../../util/shmem/fd_shmem_private.h"
13 :
14 : fd_wksp_t *
15 : fd_wksp_demand_paged_new( char const * name,
16 : uint seed,
17 : ulong part_max,
18 0 : ulong data_max ) {
19 0 : ulong footprint = fd_wksp_footprint( part_max, data_max );
20 0 : if( FD_UNLIKELY( !footprint ) ) {
21 0 : FD_LOG_WARNING(( "invalid workspace params (part_max=%lu data_max=%lu)", part_max, data_max ));
22 0 : return NULL;
23 0 : }
24 :
25 : /* Round up footprint to nearest huge page size */
26 0 : footprint = fd_ulong_align_up( footprint, FD_SHMEM_HUGE_PAGE_SZ );
27 :
28 : /* Acquire anonymous demand-paged memory */
29 0 : void * mem;
30 0 : int map_err = fd_shmem_private_map_rand( footprint, FD_SHMEM_HUGE_PAGE_SZ, FD_SHMEM_JOIN_MODE_READ_WRITE, &mem );
31 0 : if( FD_UNLIKELY( map_err ) ) {
32 0 : FD_LOG_WARNING(( "fd_shmem_private_map_rand() failed (%i-%s)", map_err, fd_io_strerror( map_err ) ));
33 0 : return NULL;
34 0 : }
35 :
36 : /* Indicate to kernel that hugepages are a fine backing store
37 : (Transparent Huge Pages) */
38 0 : if( FD_UNLIKELY( 0!=madvise( mem, footprint, MADV_HUGEPAGE ) ) ) {
39 0 : FD_LOG_WARNING(( "madvise() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
40 0 : munmap( mem, footprint );
41 0 : return NULL;
42 0 : }
43 :
44 : /* Create workspace */
45 0 : fd_wksp_t * wksp = fd_wksp_join( fd_wksp_new( mem, name, seed, part_max, data_max ) );
46 0 : if( FD_UNLIKELY( !wksp ) ) {
47 0 : FD_LOG_WARNING(( "fd_wksp_new() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
48 0 : munmap( mem, footprint );
49 0 : return NULL;
50 0 : }
51 :
52 : /* Register shared memory region */
53 0 : ulong fake_page_cnt = footprint>>FD_SHMEM_HUGE_LG_PAGE_SZ;
54 0 : int join_err = fd_shmem_join_anonymous(
55 0 : name,
56 0 : FD_SHMEM_JOIN_MODE_READ_WRITE,
57 0 : wksp,
58 0 : mem,
59 0 : FD_SHMEM_HUGE_PAGE_SZ,
60 0 : fake_page_cnt
61 0 : );
62 0 : if( FD_UNLIKELY( join_err ) ) {
63 0 : FD_LOG_WARNING(( "fd_shmem_join_anonymous() failed (%i-%s)", join_err, fd_io_strerror( join_err ) ));
64 0 : fd_wksp_delete( fd_wksp_leave( wksp ) );
65 0 : munmap( mem, footprint );
66 0 : return NULL;
67 0 : }
68 :
69 0 : return wksp;
70 0 : }
71 :
72 : void
73 0 : fd_wksp_demand_paged_delete( fd_wksp_t * wksp ) {
74 0 : fd_shmem_leave_anonymous( wksp, NULL );
75 0 : FD_TEST( fd_wksp_delete( fd_wksp_leave( wksp ) ) );
76 0 : }
77 :
78 : fd_solfuzz_runner_t *
79 : fd_solfuzz_runner_new( fd_wksp_t * wksp,
80 : ulong wksp_tag,
81 0 : fd_solfuzz_runner_options_t const * options ) {
82 :
83 0 : int stake_delegations_fd = -1;
84 :
85 : /* Allocate objects */
86 0 : ulong const txn_max = 16UL;
87 0 : ulong const progcache_sz = fd_progcache_shmem_min_sz( txn_max );
88 0 : ulong const spad_max = 1750000000UL; /* 1.75GB: 128 accounts * ~10MB + 100MB execute_wrapper
89 : out buf + 64MB cmp_binary_strict scratch + headroom */
90 0 : ulong const bank_max = 2UL;
91 :
92 0 : ulong const max_accounts = 1024UL;
93 0 : ulong const max_live_slots = txn_max;
94 0 : ulong const writes_per_slot = 1024UL;
95 0 : ulong const partition_cnt = 8192UL;
96 0 : ulong const partition_sz = (1UL<<30UL);
97 0 : ulong const cache_footprint = (12UL<<30UL);
98 0 : ulong const cache_min_reserved = fd_accdb_cache_min_reserved( 1 );
99 :
100 0 : ulong accdb_shmem_sz = fd_accdb_shmem_footprint( max_accounts, max_live_slots,
101 0 : writes_per_slot, partition_cnt,
102 0 : cache_footprint, cache_min_reserved, 1UL, 0UL );
103 0 : ulong accdb_join_sz = fd_accdb_footprint( max_live_slots, 0 );
104 :
105 0 : fd_solfuzz_runner_t * runner = fd_wksp_alloc_laddr( wksp, alignof(fd_solfuzz_runner_t), sizeof(fd_solfuzz_runner_t), wksp_tag );
106 0 : void * accdb_shmem = fd_wksp_alloc_laddr( wksp, fd_accdb_shmem_align(), accdb_shmem_sz, wksp_tag );
107 0 : void * accdb_join = fd_wksp_alloc_laddr( wksp, fd_accdb_align(), accdb_join_sz, wksp_tag );
108 0 : void * pcache_mem = fd_wksp_alloc_laddr( wksp, fd_progcache_shmem_align(), fd_progcache_shmem_footprint( txn_max, progcache_sz ), wksp_tag );
109 0 : uchar * scratch = fd_wksp_alloc_laddr( wksp, FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT, wksp_tag );
110 0 : void * spad_mem = fd_wksp_alloc_laddr( wksp, fd_spad_align(), fd_spad_footprint( spad_max ), wksp_tag );
111 0 : void * banks_mem = fd_wksp_alloc_laddr( wksp, fd_banks_align(), fd_banks_footprint( bank_max, 8UL, 2048UL, 2048UL ), wksp_tag );
112 0 : if( FD_UNLIKELY( !runner ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(solfuzz_runner) failed" )); goto bail1; }
113 0 : if( FD_UNLIKELY( !accdb_shmem ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(accdb_shmem) failed" )); goto bail1; }
114 0 : if( FD_UNLIKELY( !accdb_join ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(accdb_join) failed" )); goto bail1; }
115 0 : if( FD_UNLIKELY( !pcache_mem ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(pcache) failed" )); goto bail1; }
116 0 : if( FD_UNLIKELY( !scratch ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(scratch) failed" )); goto bail1; }
117 0 : if( FD_UNLIKELY( !spad_mem ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(spad) failed (spad_max=%g)", (double)spad_max )); goto bail1; }
118 0 : if( FD_UNLIKELY( !banks_mem ) ) { FD_LOG_WARNING(( "fd_wksp_alloc(banks) failed (bank_max=%lu)", bank_max )); goto bail1; }
119 :
120 : /* Create objects */
121 0 : fd_memset( runner, 0, sizeof(fd_solfuzz_runner_t) );
122 0 : runner->wksp = wksp;
123 0 : runner->wksp_tag = wksp_tag;
124 :
125 : /* Create accdb backed by memfd */
126 0 : int accdb_fd = memfd_create( "accdb_fuzz", 0 );
127 0 : if( FD_UNLIKELY( accdb_fd<0 ) ) { FD_LOG_WARNING(( "memfd_create failed (%i-%s)", errno, fd_io_strerror( errno ) )); goto bail1; }
128 0 : stake_delegations_fd = memfd_create( "stake_delegations_fuzz", 0 );
129 0 : if( FD_UNLIKELY( stake_delegations_fd<0 ) ) { FD_LOG_WARNING(( "memfd_create failed (%i-%s)", errno, fd_io_strerror( errno ) )); goto bail1; }
130 0 : runner->stake_delegations_fd = stake_delegations_fd;
131 :
132 0 : fd_accdb_shmem_t * shmem = fd_accdb_shmem_join(
133 0 : fd_accdb_shmem_new( accdb_shmem, max_accounts, max_live_slots,
134 0 : writes_per_slot, partition_cnt,
135 0 : partition_sz, cache_footprint, cache_min_reserved, 1, 42UL, 1UL, 0UL ) );
136 0 : if( FD_UNLIKELY( !shmem ) ) goto bail1;
137 0 : fd_accdb_t * accdb = fd_accdb_join( fd_accdb_new( accdb_join, shmem, accdb_fd, 0UL, NULL, NULL, 0UL, 0 ) );
138 0 : if( FD_UNLIKELY( !accdb ) ) goto bail1;
139 0 : runner->accdb = accdb;
140 :
141 : /* Create root fork (sentinel parent) */
142 0 : runner->root_fork_id = fd_accdb_attach_child( accdb, (fd_accdb_fork_id_t){ .val=USHORT_MAX } );
143 :
144 0 : void * shpcache = fd_progcache_shmem_new( pcache_mem, wksp_tag, 1UL, txn_max, progcache_sz );
145 0 : if( FD_UNLIKELY( !shpcache ) ) goto bail2;
146 0 : if( FD_UNLIKELY( !fd_progcache_join( runner->progcache, pcache_mem, scratch, FD_PROGCACHE_SCRATCH_FOOTPRINT ) ) ) goto bail2;
147 :
148 0 : runner->runtime = fd_wksp_alloc_laddr( wksp, alignof(fd_runtime_t), sizeof(fd_runtime_t), wksp_tag );
149 0 : if( FD_UNLIKELY( !runner->runtime ) ) goto bail2;
150 0 : runner->runtime->accounts.executable_cnt = 0UL;
151 0 : runner->runtime->accounts.account_cnt = 0UL;
152 0 : runner->runtime_stack = fd_wksp_alloc_laddr( wksp, fd_runtime_stack_align(), fd_runtime_stack_footprint( 2048UL, 2048UL, 2048UL ), wksp_tag );
153 0 : if( FD_UNLIKELY( !runner->runtime_stack ) ) goto bail2;
154 0 : if( FD_UNLIKELY( !fd_runtime_stack_join( fd_runtime_stack_new( runner->runtime_stack, 2048UL, 2048UL, 2048UL, 999UL ) ) ) ) goto bail2;
155 :
156 0 : runner->spad = fd_spad_join( fd_spad_new( spad_mem, spad_max ) );
157 0 : if( FD_UNLIKELY( !runner->spad ) ) goto bail2;
158 : /* Use 2048 for max_vote_accounts to match fd_banks_footprint above (avoids buffer overrun) */
159 0 : runner->banks = fd_banks_join( fd_banks_new( banks_mem, stake_delegations_fd, bank_max, 8UL, 2048UL, 32768UL, 2048UL, 0, 8888UL ) );
160 0 : if( FD_UNLIKELY( !runner->banks ) ) goto bail2;
161 0 : FD_TEST( fd_stake_delegations_join( fd_banks_stake_delegations_root_query( runner->banks ), stake_delegations_fd ) );
162 :
163 : /* Runtime block execution requires every non-genesis bank to have a
164 : parent. Keep the root bank as that parent and run harnesses against
165 : its child. */
166 0 : fd_bank_t * root_bank = fd_banks_init_bank( runner->banks );
167 0 : if( FD_UNLIKELY( !root_bank ) ) goto bail2;
168 0 : runner->bank = fd_banks_new_bank( runner->banks, root_bank->idx, 0L, 0 );
169 0 : if( FD_UNLIKELY( !runner->bank ) ) goto bail2;
170 0 : runner->bank = fd_banks_clone_from_parent( runner->banks, runner->bank->idx );
171 0 : if( FD_UNLIKELY( !runner->bank ) ) goto bail2;
172 0 : runner->bank->f.slot = 0UL;
173 :
174 0 : runner->enable_vm_tracing = options->enable_vm_tracing;
175 0 : FD_TEST( runner->progcache->join->shmem );
176 :
177 0 : ulong tags[1] = { wksp_tag };
178 0 : fd_wksp_usage_t usage[1];
179 0 : fd_wksp_usage( wksp, tags, 1UL, usage );
180 0 : runner->wksp_baseline_used_sz = usage->used_sz;
181 :
182 0 : return runner;
183 :
184 0 : bail2:
185 0 : if( runner->spad ) fd_spad_delete( fd_spad_leave( runner->spad ) );
186 0 : if( shpcache ) fd_progcache_shmem_delete( shpcache );
187 0 : bail1:
188 0 : if( stake_delegations_fd>=0 ) close( stake_delegations_fd );
189 0 : fd_wksp_free_laddr( scratch );
190 0 : fd_wksp_free_laddr( pcache_mem );
191 0 : fd_wksp_free_laddr( accdb_join );
192 0 : fd_wksp_free_laddr( accdb_shmem );
193 0 : fd_wksp_free_laddr( spad_mem );
194 0 : fd_wksp_free_laddr( banks_mem );
195 0 : fd_wksp_free_laddr( runner );
196 0 : FD_LOG_WARNING(( "fd_solfuzz_runner_new failed" ));
197 0 : return NULL;
198 0 : }
199 :
200 : void
201 0 : fd_solfuzz_runner_delete( fd_solfuzz_runner_t * runner ) {
202 :
203 : /* accdb cleanup is handled by the memfd close */
204 : /* TODO: proper accdb teardown if needed */
205 :
206 0 : fd_progcache_shmem_t * shpcache = NULL;
207 0 : fd_progcache_leave( runner->progcache, &shpcache );
208 :
209 0 : fd_wksp_free_laddr( fd_progcache_shmem_delete( shpcache ) );
210 0 : fd_wksp_free_laddr( fd_spad_delete( fd_spad_leave( runner->spad ) ) );
211 0 : FD_TEST( !close( runner->stake_delegations_fd ) );
212 0 : fd_wksp_free_laddr( runner->banks );
213 0 : fd_wksp_free_laddr( runner );
214 0 : }
215 :
216 : void
217 0 : fd_solfuzz_runner_leak_check( fd_solfuzz_runner_t * runner ) {
218 0 : if( FD_UNLIKELY( fd_spad_frame_used( runner->spad ) ) ) {
219 0 : FD_LOG_CRIT(( "leaked spad frame" ));
220 0 : }
221 :
222 0 : if( FD_UNLIKELY( runner->progcache->join->shmem->txn.child_head_idx != UINT_MAX ) ) {
223 0 : FD_LOG_CRIT(( "leaked a txn in progcache" ));
224 0 : }
225 :
226 0 : ulong tags[1] = { runner->wksp_tag };
227 0 : fd_wksp_usage_t usage[1];
228 0 : fd_wksp_usage( runner->wksp, tags, 1UL, usage );
229 0 : if( FD_UNLIKELY( usage->used_sz != runner->wksp_baseline_used_sz ) ) {
230 0 : FD_LOG_CRIT(( "leaked wksp allocations: %lu bytes with tag %lu (baseline %lu bytes, delta %+ld)",
231 0 : usage->used_sz, runner->wksp_tag,
232 0 : runner->wksp_baseline_used_sz,
233 0 : (long)usage->used_sz - (long)runner->wksp_baseline_used_sz ));
234 0 : }
235 0 : }
|