Line data Source code
1 : #include "fd_bpf_program_util.h"
2 : #include "fd_bpf_loader_program.h"
3 : #include "fd_loader_v4_program.h"
4 : #include "../sysvar/fd_sysvar_epoch_schedule.h"
5 :
6 : #include <assert.h>
7 :
8 : fd_sbpf_validated_program_t *
9 12 : fd_sbpf_validated_program_new( void * mem, fd_sbpf_elf_info_t const * elf_info ) {
10 12 : fd_sbpf_validated_program_t * validated_prog = (fd_sbpf_validated_program_t *)mem;
11 :
12 : /* Last verified epoch */
13 12 : validated_prog->last_epoch_verification_ran = ULONG_MAX;
14 :
15 : /* Failed verification flag */
16 12 : validated_prog->failed_verification = 0;
17 :
18 12 : ulong l = FD_LAYOUT_INIT;
19 :
20 : /* calldests backing memory */
21 12 : l = FD_LAYOUT_APPEND( l, alignof(fd_sbpf_validated_program_t), sizeof(fd_sbpf_validated_program_t) );
22 12 : validated_prog->calldests_shmem = (uchar *)mem + l;
23 12 : validated_prog->magic = FD_SBPF_VALIDATED_PROGRAM_MAGIC;
24 :
25 : /* rodata backing memory */
26 12 : l = FD_LAYOUT_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint(elf_info->rodata_sz/8UL) );
27 12 : validated_prog->rodata = (uchar *)mem + l;
28 :
29 : /* SBPF version */
30 12 : validated_prog->sbpf_version = elf_info->sbpf_version;
31 :
32 12 : return (fd_sbpf_validated_program_t *)mem;
33 12 : }
34 :
35 : ulong
36 0 : fd_sbpf_validated_program_align( void ) {
37 0 : return alignof(fd_sbpf_validated_program_t);
38 0 : }
39 :
40 : ulong
41 9 : fd_sbpf_validated_program_footprint( fd_sbpf_elf_info_t const * elf_info ) {
42 9 : ulong l = FD_LAYOUT_INIT;
43 9 : l = FD_LAYOUT_APPEND( l, alignof(fd_sbpf_validated_program_t), sizeof(fd_sbpf_validated_program_t) );
44 9 : l = FD_LAYOUT_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint(elf_info->rodata_sz/8UL) );
45 9 : l = FD_LAYOUT_APPEND( l, 8UL, elf_info->rodata_footprint );
46 9 : l = FD_LAYOUT_FINI( l, 128UL );
47 9 : return l;
48 9 : }
49 :
50 : static inline fd_funk_rec_key_t
51 57 : fd_acc_mgr_cache_key( fd_pubkey_t const * pubkey ) {
52 57 : fd_funk_rec_key_t id;
53 57 : memcpy( id.uc, pubkey, sizeof(fd_pubkey_t) );
54 57 : memset( id.uc + sizeof(fd_pubkey_t), 0, sizeof(fd_funk_rec_key_t) - sizeof(fd_pubkey_t) );
55 :
56 57 : id.uc[ FD_FUNK_REC_KEY_FOOTPRINT - 1 ] = FD_FUNK_KEY_TYPE_ELF_CACHE;
57 :
58 57 : return id;
59 57 : }
60 :
61 : /* Similar to the below function, but gets the executable program content for the v4 loader.
62 : Unlike the v3 loader, the programdata is stored in a single program account. The program must
63 : NOT be retracted to be added to the cache. Returns a pointer to the programdata on success,
64 : and NULL on failure.
65 :
66 : Reasons for failure include:
67 : - The program state cannot be read from the account data or is in the `retracted` state. */
68 : static uchar const *
69 : fd_bpf_get_executable_program_content_for_v4_loader( fd_txn_account_t const * program_acc,
70 0 : ulong * program_data_len ) {
71 0 : int err;
72 :
73 : /* Get the current loader v4 state. This implicitly also checks the dlen. */
74 0 : fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program_acc, &err );
75 0 : if( FD_UNLIKELY( err ) ) {
76 0 : return NULL;
77 0 : }
78 :
79 : /* The program must be deployed or finalized. */
80 0 : if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {
81 0 : return NULL;
82 0 : }
83 :
84 0 : *program_data_len = program_acc->vt->get_data_len( program_acc ) - LOADER_V4_PROGRAM_DATA_OFFSET;
85 0 : return program_acc->vt->get_data( program_acc ) + LOADER_V4_PROGRAM_DATA_OFFSET;
86 0 : }
87 :
88 : /* Gets the programdata for a v3 loader-owned account by decoding the account data
89 : as well as the programdata account. Returns a pointer to the programdata on success,
90 : and NULL on failure.
91 :
92 : Reasons for failure include:
93 : - The program account data cannot be decoded or is not in the `program` state.
94 : - The programdata account is not large enough to hold at least `PROGRAMDATA_METADATA_SIZE` bytes. */
95 : static uchar const *
96 : fd_bpf_get_executable_program_content_for_upgradeable_loader( fd_funk_t const * funk,
97 : fd_funk_txn_t const * funk_txn,
98 : fd_txn_account_t const * program_acc,
99 : ulong * program_data_len,
100 0 : fd_spad_t * runtime_spad ) {
101 0 : FD_TXN_ACCOUNT_DECL( programdata_acc );
102 :
103 0 : fd_bpf_upgradeable_loader_state_t * program_account_state =
104 0 : fd_bincode_decode_spad(
105 0 : bpf_upgradeable_loader_state, runtime_spad,
106 0 : program_acc->vt->get_data( program_acc ),
107 0 : program_acc->vt->get_data_len( program_acc ),
108 0 : NULL );
109 0 : if( FD_UNLIKELY( !program_account_state ) ) {
110 0 : return NULL;
111 0 : }
112 0 : if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {
113 0 : return NULL;
114 0 : }
115 :
116 0 : fd_pubkey_t * programdata_address = &program_account_state->inner.program.programdata_address;
117 :
118 0 : if( fd_txn_account_init_from_funk_readonly( programdata_acc, programdata_address, funk, funk_txn )!=FD_ACC_MGR_SUCCESS ) {
119 0 : return NULL;
120 0 : }
121 :
122 : /* We don't actually need to decode here, just make sure that the account
123 : can be decoded successfully. */
124 0 : fd_bincode_decode_ctx_t ctx_programdata = {
125 0 : .data = programdata_acc->vt->get_data( programdata_acc ),
126 0 : .dataend = programdata_acc->vt->get_data( programdata_acc ) + programdata_acc->vt->get_data_len( programdata_acc ),
127 0 : };
128 :
129 0 : ulong total_sz = 0UL;
130 0 : if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_decode_footprint( &ctx_programdata, &total_sz ) ) ) {
131 0 : return NULL;
132 0 : }
133 :
134 0 : if( FD_UNLIKELY( programdata_acc->vt->get_data_len( programdata_acc )<PROGRAMDATA_METADATA_SIZE ) ) {
135 0 : return NULL;
136 0 : }
137 :
138 0 : *program_data_len = programdata_acc->vt->get_data_len( programdata_acc ) - PROGRAMDATA_METADATA_SIZE;
139 0 : return programdata_acc->vt->get_data( programdata_acc ) + PROGRAMDATA_METADATA_SIZE;
140 0 : }
141 :
142 : /* Gets the programdata for a v1/v2 loader-owned account by returning a pointer to the account data.
143 : Returns a pointer to the programdata on success. Given the txn account API always returns a handle
144 : to the account data, this function should NEVER return NULL (since the programdata of v1 and v2 loader)
145 : accounts start at the beginning of the data. */
146 : static uchar const *
147 : fd_bpf_get_executable_program_content_for_v1_v2_loaders( fd_txn_account_t const * program_acc,
148 12 : ulong * program_data_len ) {
149 12 : *program_data_len = program_acc->vt->get_data_len( program_acc );
150 12 : return program_acc->vt->get_data( program_acc );
151 12 : }
152 :
153 : void
154 : fd_bpf_get_sbpf_versions( uint * sbpf_min_version,
155 : uint * sbpf_max_version,
156 : ulong slot,
157 12 : fd_features_t const * features ) {
158 12 : int disable_v0 = FD_FEATURE_ACTIVE( slot, features, disable_sbpf_v0_execution );
159 12 : int reenable_v0 = FD_FEATURE_ACTIVE( slot, features, reenable_sbpf_v0_execution );
160 12 : int enable_v0 = !disable_v0 || reenable_v0;
161 12 : int enable_v1 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v1_deployment_and_execution );
162 12 : int enable_v2 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v2_deployment_and_execution );
163 12 : int enable_v3 = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v3_deployment_and_execution );
164 :
165 12 : *sbpf_min_version = enable_v0 ? FD_SBPF_V0 : FD_SBPF_V3;
166 12 : if( enable_v3 ) {
167 12 : *sbpf_max_version = FD_SBPF_V3;
168 12 : } else if( enable_v2 ) {
169 0 : *sbpf_max_version = FD_SBPF_V2;
170 0 : } else if( enable_v1 ) {
171 0 : *sbpf_max_version = FD_SBPF_V1;
172 0 : } else {
173 0 : *sbpf_max_version = FD_SBPF_V0;
174 0 : }
175 12 : }
176 :
177 : uchar const *
178 : fd_bpf_get_programdata_from_account( fd_funk_t const * funk,
179 : fd_funk_txn_t const * funk_txn,
180 : fd_txn_account_t const * program_acc,
181 : ulong * out_program_data_len,
182 12 : fd_spad_t * runtime_spad ) {
183 : /* v1/v2 loaders: Programdata is just the account data.
184 : v3 loader: Programdata lives in a separate account. Deserialize the program account
185 : and lookup the programdata account. Deserialize the programdata account.
186 : v4 loader: Programdata lives in the program account, offset by LOADER_V4_PROGRAM_DATA_OFFSET. */
187 12 : if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
188 0 : return fd_bpf_get_executable_program_content_for_upgradeable_loader( funk, funk_txn, program_acc, out_program_data_len, runtime_spad );
189 12 : } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
190 0 : return fd_bpf_get_executable_program_content_for_v4_loader( program_acc, out_program_data_len );
191 12 : } else if( !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||
192 12 : !memcmp( program_acc->vt->get_owner( program_acc ), fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) {
193 12 : return fd_bpf_get_executable_program_content_for_v1_v2_loaders( program_acc, out_program_data_len );
194 12 : }
195 0 : return NULL;
196 12 : }
197 :
198 : /* Parse ELF info from programdata. */
199 : static int
200 : fd_bpf_parse_elf_info( fd_sbpf_elf_info_t * elf_info,
201 : uchar const * program_data,
202 : ulong program_data_len,
203 12 : fd_exec_slot_ctx_t const * slot_ctx ) {
204 12 : uint min_sbpf_version, max_sbpf_version;
205 12 : fd_bpf_get_sbpf_versions( &min_sbpf_version,
206 12 : &max_sbpf_version,
207 12 : fd_bank_slot_get( slot_ctx->bank ),
208 12 : fd_bank_features_query( slot_ctx->bank ) );
209 12 : if( FD_UNLIKELY( !fd_sbpf_elf_peek( elf_info, program_data, program_data_len, /* deploy checks */ 0, min_sbpf_version, max_sbpf_version ) ) ) {
210 3 : FD_LOG_DEBUG(( "fd_sbpf_elf_peek() failed: %s", fd_sbpf_strerror() ));
211 3 : return -1;
212 3 : }
213 9 : return 0;
214 12 : }
215 :
216 : /* This function is used to validate an sBPF program and set the program's flags accordingly. The return
217 : code only signifies whether the program was successfully validated or not. Regardless of the return code,
218 : the program should still be added to the cache. `validated_prog` is expected to be a pre-allocated struct with
219 : enough space to hold its field members + calldests info.
220 :
221 : Reasons for failure include:
222 : - Insufficient memory in the spad to allocate memory for local objects.
223 : - The sBPF program fails to be loaded or validated validated.
224 :
225 : On a failure that doesn't kill the client, the `failed_verification` flag for the record is set to 1.
226 :
227 : On success, `validated_prog` is updated with the loaded sBPF program metadata, as well as the `last_verified_epoch`
228 : and `failed_verification` flags. */
229 : static int
230 : fd_bpf_validate_sbpf_program( fd_exec_slot_ctx_t const * slot_ctx,
231 : fd_sbpf_elf_info_t const * elf_info,
232 : uchar const * program_data,
233 : ulong program_data_len,
234 : fd_spad_t * runtime_spad,
235 9 : fd_sbpf_validated_program_t * validated_prog /* out */ ) {
236 : /* Mark the program as validated for this epoch. */
237 :
238 9 : validated_prog->last_epoch_verification_ran = fd_bank_epoch_get( slot_ctx->bank );
239 :
240 9 : ulong prog_align = fd_sbpf_program_align();
241 9 : ulong prog_footprint = fd_sbpf_program_footprint( elf_info );
242 9 : fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_spad_alloc( runtime_spad, prog_align, prog_footprint ), elf_info, validated_prog->rodata );
243 9 : if( FD_UNLIKELY( !prog ) ) {
244 0 : validated_prog->failed_verification = 1;
245 0 : return -1;
246 0 : }
247 :
248 : /* Allocate syscalls */
249 :
250 9 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc( runtime_spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
251 9 : if( FD_UNLIKELY( !syscalls ) ) {
252 0 : FD_LOG_CRIT(( "Call to fd_sbpf_syscalls_new() failed" ));
253 0 : }
254 :
255 9 : fd_vm_syscall_register_slot( syscalls,
256 9 : fd_bank_slot_get( slot_ctx->bank ),
257 9 : fd_bank_features_query( slot_ctx->bank ),
258 9 : 0 );
259 :
260 : /* Load program. */
261 :
262 9 : if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, program_data, program_data_len, syscalls, false ) ) ) {
263 0 : FD_LOG_DEBUG(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() ));
264 0 : validated_prog->failed_verification = 1;
265 0 : return -1;
266 0 : }
267 :
268 : /* Validate the program. */
269 :
270 9 : fd_vm_t _vm[ 1UL ];
271 9 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
272 9 : if( FD_UNLIKELY( !vm ) ) {
273 0 : FD_LOG_CRIT(( "fd_vm_new() or fd_vm_join() failed" ));
274 0 : }
275 :
276 9 : int direct_mapping = FD_FEATURE_ACTIVE( fd_bank_slot_get( slot_ctx->bank ), fd_bank_features_query( slot_ctx->bank ), bpf_account_data_direct_mapping );
277 :
278 9 : vm = fd_vm_init( vm,
279 9 : NULL, /* OK since unused in `fd_vm_validate()` */
280 9 : 0UL,
281 9 : 0UL,
282 9 : prog->rodata,
283 9 : prog->rodata_sz,
284 9 : prog->text,
285 9 : prog->text_cnt,
286 9 : prog->text_off,
287 9 : prog->text_sz,
288 9 : prog->entry_pc,
289 9 : prog->calldests,
290 9 : elf_info->sbpf_version,
291 9 : syscalls,
292 9 : NULL,
293 9 : NULL,
294 9 : NULL,
295 9 : 0U,
296 9 : NULL,
297 9 : 0,
298 9 : direct_mapping,
299 9 : 0 );
300 :
301 9 : if( FD_UNLIKELY( !vm ) ) {
302 0 : FD_LOG_CRIT(( "fd_vm_init() failed" ));
303 0 : }
304 :
305 9 : int res = fd_vm_validate( vm );
306 9 : if( FD_UNLIKELY( res ) ) {
307 0 : FD_LOG_DEBUG(( "fd_vm_validate() failed" ));
308 0 : validated_prog->failed_verification = 1;
309 0 : return -1;
310 0 : }
311 :
312 : /* FIXME: Super expensive memcpy. */
313 9 : fd_memcpy( validated_prog->calldests_shmem, prog->calldests_shmem, fd_sbpf_calldests_footprint( prog->rodata_sz/8UL ) );
314 :
315 9 : validated_prog->calldests = fd_sbpf_calldests_join( validated_prog->calldests_shmem );
316 9 : validated_prog->entry_pc = prog->entry_pc;
317 9 : validated_prog->text_off = prog->text_off;
318 9 : validated_prog->text_cnt = prog->text_cnt;
319 9 : validated_prog->text_sz = prog->text_sz;
320 9 : validated_prog->rodata_sz = prog->rodata_sz;
321 9 : validated_prog->failed_verification = 0;
322 :
323 9 : return 0;
324 9 : }
325 :
326 : /* Publishes an in-prepare funk record for a program that failed verification. Creates a default
327 : sBPF validated program with the `failed_verification` flag set to 1. The passed-in funk record
328 : is expected to be in a prepare. */
329 : static void
330 : fd_publish_failed_verification_rec( fd_funk_t * funk,
331 : fd_funk_rec_prepare_t * prepare,
332 3 : fd_funk_rec_t * rec ) {
333 : /* Truncate the record to have a minimal footprint */
334 3 : fd_sbpf_elf_info_t elf_info = {0};
335 3 : ulong record_sz = fd_sbpf_validated_program_footprint( &elf_info );
336 3 : void * data = fd_funk_val_truncate( rec, fd_funk_alloc( funk ), fd_funk_wksp( funk ), 0UL, record_sz, NULL );
337 3 : if( FD_UNLIKELY( data==NULL ) ) {
338 0 : FD_LOG_ERR(( "fd_funk_val_truncate() failed to truncate record to size %lu", record_sz ));
339 0 : }
340 :
341 : /* Initialize the validated program to default values. This is fine because the `failed_verification` flag indicates
342 : that the should not be executed. */
343 3 : fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( data, &elf_info );
344 3 : validated_prog->failed_verification = 1;
345 :
346 3 : fd_funk_rec_publish( funk, prepare );
347 3 : }
348 :
349 : /* Validates an SBPF program and adds it to the program cache. Verification failure reasons include:
350 : - The programdata cannot be read from the account or programdata account
351 : - The ELF info cannot be parsed from the programdata.
352 : - The sBPF program fails to be validated.
353 :
354 : The program will still be added to the cache even if verifications fail. This is to prevent a DOS
355 : vector where an attacker could spam invocations to programs that failed verification. */
356 : static void
357 : fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
358 : fd_txn_account_t const * program_acc,
359 9 : fd_spad_t * runtime_spad ) {
360 9 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
361 :
362 : /* Prepare the funk record for the program cache. */
363 9 : fd_pubkey_t const * program_pubkey = program_acc->pubkey;
364 9 : fd_funk_t * funk = slot_ctx->funk;
365 9 : fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
366 9 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
367 :
368 : /* This prepare should never fail. */
369 9 : int funk_err = FD_FUNK_SUCCESS;
370 9 : fd_funk_rec_prepare_t prepare[1];
371 9 : fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, funk_txn, &id, prepare, &funk_err );
372 9 : if( rec == NULL || funk_err != FD_FUNK_SUCCESS ) {
373 0 : FD_LOG_CRIT(( "fd_funk_rec_prepare() failed: %i-%s", funk_err, fd_funk_strerror( funk_err ) ));
374 0 : }
375 :
376 9 : ulong program_data_len = 0UL;
377 9 : uchar const * program_data = fd_bpf_get_programdata_from_account( funk, funk_txn, program_acc, &program_data_len, runtime_spad );
378 :
379 9 : if( FD_UNLIKELY( program_data==NULL ) ) {
380 0 : fd_publish_failed_verification_rec( funk, prepare, rec );
381 0 : return;
382 0 : }
383 :
384 9 : fd_sbpf_elf_info_t elf_info = {0};
385 9 : if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
386 3 : fd_publish_failed_verification_rec( funk, prepare, rec );
387 3 : return;
388 3 : }
389 :
390 6 : ulong val_sz = fd_sbpf_validated_program_footprint( &elf_info );
391 6 : void * val = fd_funk_val_truncate(
392 6 : rec,
393 6 : fd_funk_alloc( funk ),
394 6 : fd_funk_wksp( funk ),
395 6 : 0UL,
396 6 : val_sz,
397 6 : &funk_err );
398 6 : if( FD_UNLIKELY( funk_err ) ) {
399 0 : FD_LOG_ERR(( "fd_funk_val_truncate(sz=%lu) for account failed (%i-%s)", val_sz, funk_err, fd_funk_strerror( funk_err ) ));
400 0 : }
401 :
402 : /* Note that the validated program points to the funk record data and writes into the record directly to avoid an expensive memcpy. */
403 6 : fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( val, &elf_info );
404 6 : int res = fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, validated_prog );
405 6 : if( FD_UNLIKELY( res ) ) {
406 0 : fd_publish_failed_verification_rec( funk, prepare, rec );
407 0 : return;
408 0 : }
409 :
410 6 : fd_funk_rec_publish( funk, prepare );
411 9 : } FD_SPAD_FRAME_END;
412 9 : }
413 :
414 : static int
415 : fd_bpf_check_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
416 : fd_pubkey_t const * pubkey,
417 0 : fd_spad_t * runtime_spad ) {
418 0 : FD_TXN_ACCOUNT_DECL( exec_rec );
419 0 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec, pubkey, slot_ctx->funk, slot_ctx->funk_txn ) != FD_ACC_MGR_SUCCESS ) ) {
420 0 : return -1;
421 0 : }
422 :
423 0 : if( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) {
424 0 : return -1;
425 0 : }
426 :
427 0 : fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
428 :
429 0 : return 0;
430 0 : }
431 :
432 : int
433 : fd_bpf_scan_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
434 0 : fd_spad_t * runtime_spad ) {
435 0 : fd_funk_t * funk = slot_ctx->funk;
436 0 : ulong cnt = 0UL;
437 :
438 : /* Use random-ish xid to avoid concurrency issues */
439 0 : fd_funk_txn_xid_t cache_xid = fd_funk_generate_xid();
440 :
441 0 : fd_funk_txn_start_write( funk );
442 0 : fd_funk_txn_t * cache_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, &cache_xid, 1 );
443 0 : if( !cache_txn ) {
444 0 : FD_LOG_ERR(( "fd_funk_txn_prepare() failed" ));
445 0 : return -1;
446 0 : }
447 0 : fd_funk_txn_end_write( funk );
448 :
449 0 : fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
450 0 : slot_ctx->funk_txn = cache_txn;
451 :
452 0 : fd_funk_txn_start_read( funk );
453 0 : for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, funk_txn );
454 0 : NULL != rec;
455 0 : rec = fd_funk_txn_next_rec( funk, rec )) {
456 0 : if( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
457 0 : continue;
458 0 : }
459 :
460 0 : fd_pubkey_t const * pubkey = fd_type_pun_const( rec->pair.key[0].uc );
461 :
462 0 : int res = fd_bpf_check_and_create_bpf_program_cache_entry( slot_ctx, pubkey, runtime_spad );
463 :
464 0 : if( res==0 ) {
465 0 : cnt++;
466 0 : }
467 0 : }
468 0 : fd_funk_txn_end_read( funk );
469 :
470 0 : FD_LOG_DEBUG(( "loaded program cache: %lu", cnt));
471 :
472 0 : fd_funk_txn_start_write( funk );
473 0 : if( fd_funk_txn_publish_into_parent( funk, cache_txn, 1 ) != FD_FUNK_SUCCESS ) {
474 0 : FD_LOG_ERR(( "fd_funk_txn_publish_into_parent() failed" ));
475 0 : return -1;
476 0 : }
477 0 : fd_funk_txn_end_write( funk );
478 :
479 0 : slot_ctx->funk_txn = funk_txn;
480 0 : return 0;
481 0 : }
482 :
483 : int
484 : fd_bpf_load_cache_entry( fd_funk_t const * funk,
485 : fd_funk_txn_t const * funk_txn,
486 : fd_pubkey_t const * program_pubkey,
487 30 : fd_sbpf_validated_program_t const ** valid_prog ) {
488 30 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
489 :
490 30 : for(;;) {
491 30 : fd_funk_rec_query_t query[1];
492 30 : fd_funk_rec_t const * rec = fd_funk_rec_query_try_global(funk, funk_txn, &id, NULL, query);
493 :
494 30 : if( FD_UNLIKELY( !rec || !!( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) ) {
495 15 : if( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) {
496 15 : return -1;
497 15 : } else {
498 0 : continue;
499 0 : }
500 15 : }
501 :
502 15 : void const * data = fd_funk_val_const( rec, fd_funk_wksp(funk) );
503 :
504 15 : *valid_prog = (fd_sbpf_validated_program_t const *)data;
505 :
506 : /* This test is actually too early. It should happen after the
507 : data is actually consumed.
508 :
509 : TODO: this is likely fine because nothing else is modifying the
510 : program cache records at the same time. */
511 15 : if( FD_LIKELY( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) ) {
512 15 : if( FD_UNLIKELY( (*valid_prog)->magic != FD_SBPF_VALIDATED_PROGRAM_MAGIC ) ) FD_LOG_ERR(( "invalid magic" ));
513 15 : return 0;
514 15 : }
515 :
516 : /* Try again */
517 15 : }
518 30 : }
519 :
520 : void
521 : fd_bpf_program_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
522 : fd_pubkey_t const * program_pubkey,
523 18 : fd_spad_t * runtime_spad ) {
524 18 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
525 18 : FD_TXN_ACCOUNT_DECL( exec_rec );
526 18 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
527 :
528 : /* No need to touch the cache if the account no longer exists. */
529 18 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec,
530 18 : program_pubkey,
531 18 : slot_ctx->funk,
532 18 : slot_ctx->funk_txn ) ) ) {
533 3 : return;
534 3 : }
535 :
536 : /* The account owner must be a BPF loader to even be considered. */
537 15 : if( FD_UNLIKELY( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) ) {
538 3 : return;
539 3 : }
540 :
541 : /* If the program is not present in the cache yet, then we should run verifications and add it to the cache.
542 : `fd_bpf_create_bpf_program_cache_entry()` will insert the program into the cache and update the entry's flags
543 : accordingly if it fails verification. */
544 12 : fd_sbpf_validated_program_t const * prog = NULL;
545 12 : int err = fd_bpf_load_cache_entry( slot_ctx->funk, slot_ctx->funk_txn, program_pubkey, &prog );
546 12 : if( FD_UNLIKELY( err ) ) {
547 9 : fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
548 9 : return;
549 9 : }
550 :
551 : /* At this point, the program is in the cache. We need to check the last verified epoch now to determine if it needs to be reverified.
552 : If it has already been reverified for the current epoch, then there is no need to do anything. */
553 3 : ulong current_epoch = fd_bank_epoch_get( slot_ctx->bank );
554 3 : if( FD_LIKELY( prog->last_epoch_verification_ran==current_epoch ) ) {
555 0 : return;
556 0 : }
557 :
558 : /* At this point, the program is in the cache but has not been reverified for the current epoch.
559 : We need to run verifications and update the cache if it passes. */
560 :
561 : /* Copy the record (if needed) down into the current funk txn from one of its ancestors. It is safe to
562 : pass in min_sz=0 because the record is known to exist in the cache already, and the record size will not change */
563 3 : fd_funk_rec_try_clone_safe( slot_ctx->funk, slot_ctx->funk_txn, &id, 0UL, 0UL );
564 :
565 : /* Modify the record within the current funk txn */
566 3 : fd_funk_rec_query_t query[1];
567 3 : fd_funk_rec_t * rec = fd_funk_rec_modify( slot_ctx->funk, slot_ctx->funk_txn, &id, query );
568 :
569 3 : if( FD_UNLIKELY( !rec ) ) {
570 : /* The record does not exist (somehow). Ideally this should never happen since this function is called in a single-threaded context. */
571 0 : FD_LOG_CRIT(( "Failed to modify the BPF program cache record. Perhaps there is a race condition?" ));
572 0 : }
573 :
574 3 : void * data = fd_funk_val( rec, fd_funk_wksp( slot_ctx->funk ) );
575 3 : fd_sbpf_elf_info_t elf_info = {0};
576 3 : fd_sbpf_validated_program_t * modified_prog = (fd_sbpf_validated_program_t *)data;
577 :
578 : /* Get the program data from the account */
579 3 : ulong program_data_len = 0UL;
580 3 : uchar const * program_data = fd_bpf_get_programdata_from_account( slot_ctx->funk,
581 3 : slot_ctx->funk_txn,
582 3 : exec_rec,
583 3 : &program_data_len,
584 3 : runtime_spad );
585 3 : if( FD_UNLIKELY( program_data==NULL ) ) {
586 0 : modified_prog->failed_verification = 1;
587 0 : fd_funk_rec_modify_publish( query );
588 0 : return;
589 0 : }
590 :
591 : /* Parse the ELF info */
592 3 : if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
593 0 : modified_prog->failed_verification = 1;
594 0 : fd_funk_rec_modify_publish( query );
595 0 : return;
596 0 : }
597 :
598 : /* Validate the sBPF program. This will set the program's flags accordingly. The return code does not matter here because we publish
599 : regardless of the return code. */
600 3 : modified_prog = fd_sbpf_validated_program_new( data, &elf_info );
601 3 : fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, modified_prog );
602 :
603 3 : if( modified_prog->failed_verification ) {
604 0 : FD_LOG_ERR(("program fialed veriifecation;"));
605 0 : }
606 :
607 : /* Finish modifying and release lock */
608 3 : fd_funk_rec_modify_publish( query );
609 :
610 18 : } FD_SPAD_FRAME_END;
611 18 : }
|