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 : slot_ctx->slot,
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 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
239 9 : validated_prog->last_epoch_verification_ran = fd_slot_to_epoch( epoch_schedule,
240 9 : slot_ctx->slot,
241 9 : NULL );
242 :
243 9 : ulong prog_align = fd_sbpf_program_align();
244 9 : ulong prog_footprint = fd_sbpf_program_footprint( elf_info );
245 9 : fd_sbpf_program_t * prog = fd_sbpf_program_new( fd_spad_alloc( runtime_spad, prog_align, prog_footprint ), elf_info, validated_prog->rodata );
246 9 : if( FD_UNLIKELY( !prog ) ) {
247 0 : validated_prog->failed_verification = 1;
248 0 : return -1;
249 0 : }
250 :
251 : /* Allocate syscalls */
252 :
253 9 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_spad_alloc( runtime_spad, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
254 9 : if( FD_UNLIKELY( !syscalls ) ) {
255 0 : FD_LOG_CRIT(( "Call to fd_sbpf_syscalls_new() failed" ));
256 0 : }
257 :
258 9 : fd_vm_syscall_register_slot( syscalls,
259 9 : slot_ctx->slot,
260 9 : fd_bank_features_query( slot_ctx->bank ),
261 9 : 0 );
262 :
263 : /* Load program. */
264 :
265 9 : if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, program_data, program_data_len, syscalls, false ) ) ) {
266 0 : FD_LOG_DEBUG(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() ));
267 0 : validated_prog->failed_verification = 1;
268 0 : return -1;
269 0 : }
270 :
271 : /* Validate the program. */
272 :
273 9 : fd_vm_t _vm[ 1UL ];
274 9 : fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
275 9 : if( FD_UNLIKELY( !vm ) ) {
276 0 : FD_LOG_CRIT(( "fd_vm_new() or fd_vm_join() failed" ));
277 0 : }
278 :
279 9 : int direct_mapping = FD_FEATURE_ACTIVE( slot_ctx->slot, fd_bank_features_query( slot_ctx->bank ), bpf_account_data_direct_mapping );
280 :
281 9 : vm = fd_vm_init( vm,
282 9 : NULL, /* OK since unused in `fd_vm_validate()` */
283 9 : 0UL,
284 9 : 0UL,
285 9 : prog->rodata,
286 9 : prog->rodata_sz,
287 9 : prog->text,
288 9 : prog->text_cnt,
289 9 : prog->text_off,
290 9 : prog->text_sz,
291 9 : prog->entry_pc,
292 9 : prog->calldests,
293 9 : elf_info->sbpf_version,
294 9 : syscalls,
295 9 : NULL,
296 9 : NULL,
297 9 : NULL,
298 9 : 0U,
299 9 : NULL,
300 9 : 0,
301 9 : direct_mapping,
302 9 : 0 );
303 :
304 9 : if( FD_UNLIKELY( !vm ) ) {
305 0 : FD_LOG_CRIT(( "fd_vm_init() failed" ));
306 0 : }
307 :
308 9 : int res = fd_vm_validate( vm );
309 9 : if( FD_UNLIKELY( res ) ) {
310 0 : FD_LOG_DEBUG(( "fd_vm_validate() failed" ));
311 0 : validated_prog->failed_verification = 1;
312 0 : return -1;
313 0 : }
314 :
315 : /* FIXME: Super expensive memcpy. */
316 9 : fd_memcpy( validated_prog->calldests_shmem, prog->calldests_shmem, fd_sbpf_calldests_footprint( prog->rodata_sz/8UL ) );
317 :
318 9 : validated_prog->calldests = fd_sbpf_calldests_join( validated_prog->calldests_shmem );
319 9 : validated_prog->entry_pc = prog->entry_pc;
320 9 : validated_prog->text_off = prog->text_off;
321 9 : validated_prog->text_cnt = prog->text_cnt;
322 9 : validated_prog->text_sz = prog->text_sz;
323 9 : validated_prog->rodata_sz = prog->rodata_sz;
324 9 : validated_prog->failed_verification = 0;
325 :
326 9 : return 0;
327 9 : }
328 :
329 : /* Publishes an in-prepare funk record for a program that failed verification. Creates a default
330 : sBPF validated program with the `failed_verification` flag set to 1. The passed-in funk record
331 : is expected to be in a prepare. */
332 : static void
333 : fd_publish_failed_verification_rec( fd_funk_t * funk,
334 : fd_funk_rec_prepare_t * prepare,
335 3 : fd_funk_rec_t * rec ) {
336 : /* Truncate the record to have a minimal footprint */
337 3 : fd_sbpf_elf_info_t elf_info = {0};
338 3 : ulong record_sz = fd_sbpf_validated_program_footprint( &elf_info );
339 3 : void * data = fd_funk_val_truncate( rec, fd_funk_alloc( funk ), fd_funk_wksp( funk ), 0UL, record_sz, NULL );
340 3 : if( FD_UNLIKELY( data==NULL ) ) {
341 0 : FD_LOG_ERR(( "fd_funk_val_truncate() failed to truncate record to size %lu", record_sz ));
342 0 : }
343 :
344 : /* Initialize the validated program to default values. This is fine because the `failed_verification` flag indicates
345 : that the should not be executed. */
346 3 : fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( data, &elf_info );
347 3 : validated_prog->failed_verification = 1;
348 :
349 3 : fd_funk_rec_publish( funk, prepare );
350 3 : }
351 :
352 : /* Validates an SBPF program and adds it to the program cache. Verification failure reasons include:
353 : - The programdata cannot be read from the account or programdata account
354 : - The ELF info cannot be parsed from the programdata.
355 : - The sBPF program fails to be validated.
356 :
357 : The program will still be added to the cache even if verifications fail. This is to prevent a DOS
358 : vector where an attacker could spam invocations to programs that failed verification. */
359 : static void
360 : fd_bpf_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
361 : fd_txn_account_t const * program_acc,
362 9 : fd_spad_t * runtime_spad ) {
363 9 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
364 :
365 : /* Prepare the funk record for the program cache. */
366 9 : fd_pubkey_t const * program_pubkey = program_acc->pubkey;
367 9 : fd_funk_t * funk = slot_ctx->funk;
368 9 : fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
369 9 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
370 :
371 : /* This prepare should never fail. */
372 9 : int funk_err = FD_FUNK_SUCCESS;
373 9 : fd_funk_rec_prepare_t prepare[1];
374 9 : fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, funk_txn, &id, prepare, &funk_err );
375 9 : if( rec == NULL || funk_err != FD_FUNK_SUCCESS ) {
376 0 : FD_LOG_CRIT(( "fd_funk_rec_prepare() failed: %i-%s", funk_err, fd_funk_strerror( funk_err ) ));
377 0 : }
378 :
379 9 : ulong program_data_len = 0UL;
380 9 : uchar const * program_data = fd_bpf_get_programdata_from_account( funk, funk_txn, program_acc, &program_data_len, runtime_spad );
381 :
382 9 : if( FD_UNLIKELY( program_data==NULL ) ) {
383 0 : fd_publish_failed_verification_rec( funk, prepare, rec );
384 0 : return;
385 0 : }
386 :
387 9 : fd_sbpf_elf_info_t elf_info = {0};
388 9 : if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
389 3 : fd_publish_failed_verification_rec( funk, prepare, rec );
390 3 : return;
391 3 : }
392 :
393 6 : ulong val_sz = fd_sbpf_validated_program_footprint( &elf_info );
394 6 : void * val = fd_funk_val_truncate(
395 6 : rec,
396 6 : fd_funk_alloc( funk ),
397 6 : fd_funk_wksp( funk ),
398 6 : 0UL,
399 6 : val_sz,
400 6 : &funk_err );
401 6 : if( FD_UNLIKELY( funk_err ) ) {
402 0 : FD_LOG_ERR(( "fd_funk_val_truncate(sz=%lu) for account failed (%i-%s)", val_sz, funk_err, fd_funk_strerror( funk_err ) ));
403 0 : }
404 :
405 : /* Note that the validated program points to the funk record data and writes into the record directly to avoid an expensive memcpy. */
406 6 : fd_sbpf_validated_program_t * validated_prog = fd_sbpf_validated_program_new( val, &elf_info );
407 6 : int res = fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, validated_prog );
408 6 : if( FD_UNLIKELY( res ) ) {
409 0 : fd_publish_failed_verification_rec( funk, prepare, rec );
410 0 : return;
411 0 : }
412 :
413 6 : fd_funk_rec_publish( funk, prepare );
414 9 : } FD_SPAD_FRAME_END;
415 9 : }
416 :
417 : static int
418 : fd_bpf_check_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
419 : fd_pubkey_t const * pubkey,
420 0 : fd_spad_t * runtime_spad ) {
421 0 : FD_TXN_ACCOUNT_DECL( exec_rec );
422 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 ) ) {
423 0 : return -1;
424 0 : }
425 :
426 0 : if( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) {
427 0 : return -1;
428 0 : }
429 :
430 0 : fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
431 :
432 0 : return 0;
433 0 : }
434 :
435 : int
436 : fd_bpf_scan_and_create_bpf_program_cache_entry( fd_exec_slot_ctx_t * slot_ctx,
437 0 : fd_spad_t * runtime_spad ) {
438 0 : fd_funk_t * funk = slot_ctx->funk;
439 0 : ulong cnt = 0UL;
440 :
441 : /* Use random-ish xid to avoid concurrency issues */
442 0 : fd_funk_txn_xid_t cache_xid = fd_funk_generate_xid();
443 :
444 0 : fd_funk_txn_start_write( funk );
445 0 : fd_funk_txn_t * cache_txn = fd_funk_txn_prepare( funk, slot_ctx->funk_txn, &cache_xid, 1 );
446 0 : if( !cache_txn ) {
447 0 : FD_LOG_ERR(( "fd_funk_txn_prepare() failed" ));
448 0 : return -1;
449 0 : }
450 0 : fd_funk_txn_end_write( funk );
451 :
452 0 : fd_funk_txn_t * funk_txn = slot_ctx->funk_txn;
453 0 : slot_ctx->funk_txn = cache_txn;
454 :
455 0 : fd_funk_txn_start_read( funk );
456 0 : for (fd_funk_rec_t const *rec = fd_funk_txn_first_rec( funk, funk_txn );
457 0 : NULL != rec;
458 0 : rec = fd_funk_txn_next_rec( funk, rec )) {
459 0 : if( !fd_funk_key_is_acc( rec->pair.key ) || ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) {
460 0 : continue;
461 0 : }
462 :
463 0 : fd_pubkey_t const * pubkey = fd_type_pun_const( rec->pair.key[0].uc );
464 :
465 0 : int res = fd_bpf_check_and_create_bpf_program_cache_entry( slot_ctx, pubkey, runtime_spad );
466 :
467 0 : if( res==0 ) {
468 0 : cnt++;
469 0 : }
470 0 : }
471 0 : fd_funk_txn_end_read( funk );
472 :
473 0 : FD_LOG_DEBUG(( "loaded program cache: %lu", cnt));
474 :
475 0 : fd_funk_txn_start_write( funk );
476 0 : if( fd_funk_txn_publish_into_parent( funk, cache_txn, 1 ) != FD_FUNK_SUCCESS ) {
477 0 : FD_LOG_ERR(( "fd_funk_txn_publish_into_parent() failed" ));
478 0 : return -1;
479 0 : }
480 0 : fd_funk_txn_end_write( funk );
481 :
482 0 : slot_ctx->funk_txn = funk_txn;
483 0 : return 0;
484 0 : }
485 :
486 : int
487 : fd_bpf_load_cache_entry( fd_funk_t const * funk,
488 : fd_funk_txn_t const * funk_txn,
489 : fd_pubkey_t const * program_pubkey,
490 30 : fd_sbpf_validated_program_t const ** valid_prog ) {
491 30 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
492 :
493 30 : for(;;) {
494 30 : fd_funk_rec_query_t query[1];
495 30 : fd_funk_rec_t const * rec = fd_funk_rec_query_try_global(funk, funk_txn, &id, NULL, query);
496 :
497 30 : if( FD_UNLIKELY( !rec || !!( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) ) {
498 15 : if( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) {
499 15 : return -1;
500 15 : } else {
501 0 : continue;
502 0 : }
503 15 : }
504 :
505 15 : void const * data = fd_funk_val_const( rec, fd_funk_wksp(funk) );
506 :
507 15 : *valid_prog = (fd_sbpf_validated_program_t const *)data;
508 :
509 : /* This test is actually too early. It should happen after the
510 : data is actually consumed.
511 :
512 : TODO: this is likely fine because nothing else is modifying the
513 : program cache records at the same time. */
514 15 : if( FD_LIKELY( fd_funk_rec_query_test( query ) == FD_FUNK_SUCCESS ) ) {
515 15 : if( FD_UNLIKELY( (*valid_prog)->magic != FD_SBPF_VALIDATED_PROGRAM_MAGIC ) ) FD_LOG_ERR(( "invalid magic" ));
516 15 : return 0;
517 15 : }
518 :
519 : /* Try again */
520 15 : }
521 30 : }
522 :
523 : void
524 : fd_bpf_program_update_program_cache( fd_exec_slot_ctx_t * slot_ctx,
525 : fd_pubkey_t const * program_pubkey,
526 18 : fd_spad_t * runtime_spad ) {
527 18 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
528 18 : FD_TXN_ACCOUNT_DECL( exec_rec );
529 18 : fd_funk_rec_key_t id = fd_acc_mgr_cache_key( program_pubkey );
530 :
531 : /* No need to touch the cache if the account no longer exists. */
532 18 : if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( exec_rec,
533 18 : program_pubkey,
534 18 : slot_ctx->funk,
535 18 : slot_ctx->funk_txn ) ) ) {
536 3 : return;
537 3 : }
538 :
539 : /* The account owner must be a BPF loader to even be considered. */
540 15 : if( FD_UNLIKELY( !fd_executor_pubkey_is_bpf_loader( exec_rec->vt->get_owner( exec_rec ) ) ) ) {
541 3 : return;
542 3 : }
543 :
544 : /* If the program is not present in the cache yet, then we should run verifications and add it to the cache.
545 : `fd_bpf_create_bpf_program_cache_entry()` will insert the program into the cache and update the entry's flags
546 : accordingly if it fails verification. */
547 12 : fd_sbpf_validated_program_t const * prog = NULL;
548 12 : int err = fd_bpf_load_cache_entry( slot_ctx->funk, slot_ctx->funk_txn, program_pubkey, &prog );
549 12 : if( FD_UNLIKELY( err ) ) {
550 9 : fd_bpf_create_bpf_program_cache_entry( slot_ctx, exec_rec, runtime_spad );
551 9 : return;
552 9 : }
553 :
554 : /* 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.
555 : If it has already been reverified for the current epoch, then there is no need to do anything. */
556 3 : fd_epoch_schedule_t const * epoch_schedule = fd_bank_epoch_schedule_query( slot_ctx->bank );
557 3 : ulong current_epoch = fd_slot_to_epoch( epoch_schedule, slot_ctx->slot, NULL );
558 3 : if( FD_LIKELY( prog->last_epoch_verification_ran==current_epoch ) ) {
559 0 : return;
560 0 : }
561 :
562 : /* At this point, the program is in the cache but has not been reverified for the current epoch.
563 : We need to run verifications and update the cache if it passes. */
564 :
565 : /* Copy the record (if needed) down into the current funk txn from one of its ancestors. It is safe to
566 : pass in min_sz=0 because the record is known to exist in the cache already, and the record size will not change */
567 3 : fd_funk_rec_try_clone_safe( slot_ctx->funk, slot_ctx->funk_txn, &id, 0UL, 0UL );
568 :
569 : /* Modify the record within the current funk txn */
570 3 : fd_funk_rec_query_t query[1];
571 3 : fd_funk_rec_t * rec = fd_funk_rec_modify( slot_ctx->funk, slot_ctx->funk_txn, &id, query );
572 :
573 3 : if( FD_UNLIKELY( !rec ) ) {
574 : /* The record does not exist (somehow). Ideally this should never happen since this function is called in a single-threaded context. */
575 0 : FD_LOG_CRIT(( "Failed to modify the BPF program cache record. Perhaps there is a race condition?" ));
576 0 : }
577 :
578 3 : void * data = fd_funk_val( rec, fd_funk_wksp( slot_ctx->funk ) );
579 3 : fd_sbpf_elf_info_t elf_info = {0};
580 3 : fd_sbpf_validated_program_t * modified_prog = (fd_sbpf_validated_program_t *)data;
581 :
582 : /* Get the program data from the account */
583 3 : ulong program_data_len = 0UL;
584 3 : uchar const * program_data = fd_bpf_get_programdata_from_account( slot_ctx->funk,
585 3 : slot_ctx->funk_txn,
586 3 : exec_rec,
587 3 : &program_data_len,
588 3 : runtime_spad );
589 3 : if( FD_UNLIKELY( program_data==NULL ) ) {
590 0 : modified_prog->failed_verification = 1;
591 0 : fd_funk_rec_modify_publish( query );
592 0 : return;
593 0 : }
594 :
595 : /* Parse the ELF info */
596 3 : if( FD_UNLIKELY( fd_bpf_parse_elf_info( &elf_info, program_data, program_data_len, slot_ctx ) ) ) {
597 0 : modified_prog->failed_verification = 1;
598 0 : fd_funk_rec_modify_publish( query );
599 0 : return;
600 0 : }
601 :
602 : /* Validate the sBPF program. This will set the program's flags accordingly. The return code does not matter here because we publish
603 : regardless of the return code. */
604 3 : modified_prog = fd_sbpf_validated_program_new( data, &elf_info );
605 3 : fd_bpf_validate_sbpf_program( slot_ctx, &elf_info, program_data, program_data_len, runtime_spad, modified_prog );
606 :
607 3 : if( modified_prog->failed_verification ) {
608 0 : FD_LOG_ERR(("program fialed veriifecation;"));
609 0 : }
610 :
611 : /* Finish modifying and release lock */
612 3 : fd_funk_rec_modify_publish( query );
613 :
614 18 : } FD_SPAD_FRAME_END;
615 18 : }
|