@@ -11,6 +11,7 @@ fd_program_cache_entry_new( void * mem,
11
11
ulong last_slot_modified ,
12
12
ulong last_slot_verified ) {
13
13
fd_program_cache_entry_t * cache_entry = (fd_program_cache_entry_t * )mem ;
14
+ cache_entry -> magic = FD_PROGRAM_CACHE_ENTRY_MAGIC ;
14
15
15
16
/* Failed verification flag */
16
17
cache_entry -> failed_verification = 0 ;
@@ -26,7 +27,6 @@ fd_program_cache_entry_new( void * mem,
26
27
/* calldests backing memory */
27
28
l = FD_LAYOUT_APPEND ( l , alignof(fd_program_cache_entry_t ), sizeof (fd_program_cache_entry_t ) );
28
29
cache_entry -> calldests_shmem = (uchar * )mem + l ;
29
- cache_entry -> magic = FD_PROGRAM_CACHE_ENTRY_MAGIC ;
30
30
31
31
/* rodata backing memory */
32
32
l = FD_LAYOUT_APPEND ( l , fd_sbpf_calldests_align (), fd_sbpf_calldests_footprint (elf_info -> rodata_sz /8UL ) );
@@ -35,7 +35,26 @@ fd_program_cache_entry_new( void * mem,
35
35
/* SBPF version */
36
36
cache_entry -> sbpf_version = elf_info -> sbpf_version ;
37
37
38
- return (fd_program_cache_entry_t * )mem ;
38
+ return cache_entry ;
39
+ }
40
+
41
+ /* Sets up defined fields for a record that failed verification. */
42
+ static void
43
+ fd_program_cache_entry_set_failed_verification ( void * mem ,
44
+ ulong last_slot_verified ) {
45
+ fd_program_cache_entry_t * cache_entry = (fd_program_cache_entry_t * )mem ;
46
+ cache_entry -> magic = FD_PROGRAM_CACHE_ENTRY_MAGIC ;
47
+
48
+ /* Failed verification flag */
49
+ cache_entry -> failed_verification = 1 ;
50
+
51
+ /* Last slot the program was modified */
52
+ cache_entry -> last_slot_modified = 0UL ;
53
+
54
+ /* Last slot verification checks were ran for this program */
55
+ cache_entry -> last_slot_verified = last_slot_verified ;
56
+
57
+ /* All other fields are undefined. */
39
58
}
40
59
41
60
ulong
@@ -44,12 +63,23 @@ fd_program_cache_entry_footprint( fd_sbpf_elf_info_t const * elf_info ) {
44
63
l = FD_LAYOUT_APPEND ( l , alignof(fd_program_cache_entry_t ), sizeof (fd_program_cache_entry_t ) );
45
64
l = FD_LAYOUT_APPEND ( l , fd_sbpf_calldests_align (), fd_sbpf_calldests_footprint (elf_info -> rodata_sz /8UL ) );
46
65
l = FD_LAYOUT_APPEND ( l , 8UL , elf_info -> rodata_footprint );
47
- l = FD_LAYOUT_FINI ( l , 128UL );
66
+ l = FD_LAYOUT_FINI ( l , alignof( fd_program_cache_entry_t ) );
48
67
return l ;
49
68
}
50
69
51
- /* Gets the program cache funk record key for a given program pubkey. */
52
- static inline fd_funk_rec_key_t
70
+ /* Returns the footprint of a record that failed verification. Note that
71
+ all other fields are undefined besides the `failed_verification`,
72
+ `last_slot_verified`, and `last_slot_modified` fields, so we can
73
+ just return the core struct's size. */
74
+ static inline FD_FN_PURE ulong
75
+ fd_program_cache_failed_verification_entry_footprint ( void ) {
76
+ ulong l = FD_LAYOUT_INIT ;
77
+ l = FD_LAYOUT_APPEND ( l , alignof(fd_program_cache_entry_t ), sizeof (fd_program_cache_entry_t ) );
78
+ l = FD_LAYOUT_FINI ( l , alignof(fd_program_cache_entry_t ) );
79
+ return l ;
80
+ }
81
+
82
+ fd_funk_rec_key_t
53
83
fd_program_cache_key ( fd_pubkey_t const * pubkey ) {
54
84
fd_funk_rec_key_t id ;
55
85
memcpy ( id .uc , pubkey , sizeof (fd_pubkey_t ) );
@@ -161,10 +191,12 @@ fd_get_executable_program_content_for_upgradeable_loader( fd_funk_t const *
161
191
return fd_txn_account_get_data ( programdata_acc ) + PROGRAMDATA_METADATA_SIZE ;
162
192
}
163
193
164
- /* Gets the programdata for a v1/v2 loader-owned account by returning a pointer to the account data.
165
- Returns a pointer to the programdata on success. Given the txn account API always returns a handle
166
- to the account data, this function should NEVER return NULL (since the programdata of v1 and v2 loader)
167
- accounts start at the beginning of the data. */
194
+ /* Gets the programdata for a v1/v2 loader-owned account by returning a
195
+ pointer to the account data. Returns a pointer to the programdata on
196
+ success. Given the txn account API always returns a handle to the
197
+ account data, this function should NEVER return NULL (since the
198
+ programdata of v1 and v2 loader) accounts start at the beginning of
199
+ the data. */
168
200
static uchar const *
169
201
fd_get_executable_program_content_for_v1_v2_loaders ( fd_txn_account_t const * program_acc ,
170
202
ulong * program_data_len ) {
@@ -179,9 +211,11 @@ fd_program_cache_get_account_programdata( fd_funk_t const * funk,
179
211
ulong * out_program_data_len ,
180
212
fd_spad_t * runtime_spad ) {
181
213
/* v1/v2 loaders: Programdata is just the account data.
182
- v3 loader: Programdata lives in a separate account. Deserialize the program account
183
- and lookup the programdata account. Deserialize the programdata account.
184
- v4 loader: Programdata lives in the program account, offset by LOADER_V4_PROGRAM_DATA_OFFSET. */
214
+ v3 loader: Programdata lives in a separate account. Deserialize the
215
+ program account and lookup the programdata account.
216
+ Deserialize the programdata account.
217
+ v4 loader: Programdata lives in the program account, offset by
218
+ LOADER_V4_PROGRAM_DATA_OFFSET. */
185
219
if ( !memcmp ( fd_txn_account_get_owner ( program_acc ), fd_solana_bpf_loader_upgradeable_program_id .key , sizeof (fd_pubkey_t ) ) ) {
186
220
return fd_get_executable_program_content_for_upgradeable_loader ( funk , funk_txn , program_acc , out_program_data_len , runtime_spad );
187
221
} else if ( !memcmp ( fd_txn_account_get_owner ( program_acc ), fd_solana_bpf_loader_v4_program_id .key , sizeof (fd_pubkey_t ) ) ) {
@@ -317,19 +351,13 @@ fd_program_cache_publish_failed_verification_rec( fd_funk_t * funk,
317
351
fd_funk_rec_t * rec ,
318
352
ulong current_slot ) {
319
353
/* Truncate the record to have a minimal footprint */
320
- fd_sbpf_elf_info_t elf_info = {0 };
321
- ulong record_sz = fd_program_cache_entry_footprint ( & elf_info );
354
+ ulong record_sz = fd_program_cache_failed_verification_entry_footprint ();
322
355
void * data = fd_funk_val_truncate ( rec , fd_funk_alloc ( funk ), fd_funk_wksp ( funk ), 0UL , record_sz , NULL );
323
356
if ( FD_UNLIKELY ( data == NULL ) ) {
324
357
FD_LOG_ERR (( "fd_funk_val_truncate() failed to truncate record to size %lu" , record_sz ));
325
358
}
326
359
327
- /* Initialize the validated program to default values. This is fine
328
- because the `failed_verification` flag indicates that the should
329
- not be executed. */
330
- fd_program_cache_entry_t * failed_entry = fd_program_cache_entry_new ( data , & elf_info , 0UL , current_slot );
331
- failed_entry -> failed_verification = 1 ;
332
-
360
+ fd_program_cache_entry_set_failed_verification ( data , current_slot );
333
361
fd_funk_rec_publish ( funk , prepare );
334
362
}
335
363
@@ -540,10 +568,26 @@ FD_SPAD_FRAME_BEGIN( runtime_spad ) {
540
568
541
569
/* From here on out, we need to reverify the program. */
542
570
571
+ /* Parse out the ELF info so we can determine the record footprint.
572
+ If the parsing fails, then we need to make sure to later publish
573
+ a failed verification record. */
574
+ uchar failed_elf_parsing = 0 ;
575
+ ulong record_sz = 0UL ;
576
+ fd_sbpf_elf_info_t elf_info = {0 };
577
+ if ( FD_UNLIKELY ( fd_program_cache_parse_elf_info ( & elf_info , program_data , program_data_len , slot_ctx ) ) ) {
578
+ failed_elf_parsing = 1 ;
579
+ record_sz = fd_program_cache_failed_verification_entry_footprint ();
580
+ } else {
581
+ failed_elf_parsing = 0 ;
582
+ record_sz = fd_program_cache_entry_footprint ( & elf_info );
583
+ }
584
+
543
585
/* Copy the record (if needed) down into the current funk txn from one
544
- of its ancestors. It is safe to pass in min_sz=0 because the record
545
- is known to exist in the cache already, and the record size will
546
- not change */
586
+ of its ancestors.
587
+
588
+ TODO: We pass in a `min_sz` of 0 because this API does not resize
589
+ the record if it already exists in the current funk transaction.
590
+ This maybe needs to change. */
547
591
fd_funk_rec_try_clone_safe ( slot_ctx -> funk , slot_ctx -> funk_txn , & id , 0UL , 0UL );
548
592
549
593
/* Modify the record within the current funk txn */
@@ -552,19 +596,25 @@ FD_SPAD_FRAME_BEGIN( runtime_spad ) {
552
596
553
597
if ( FD_UNLIKELY ( !rec ) ) {
554
598
/* The record does not exist (somehow). Ideally this should never
555
- happen since this function is called in a single-threaded context. */
599
+ happen as this function is called in a single-threaded context. */
556
600
FD_LOG_CRIT (( "Failed to modify the BPF program cache record. Perhaps there is a race condition?" ));
557
601
}
558
602
559
- void * data = fd_funk_val ( rec , fd_funk_wksp ( slot_ctx -> funk ) );
560
- fd_sbpf_elf_info_t elf_info = {0 };
603
+ /* Resize the record to the new footprint if needed */
604
+ void * data = fd_funk_val_truncate (
605
+ rec ,
606
+ fd_funk_alloc ( slot_ctx -> funk ),
607
+ fd_funk_wksp ( slot_ctx -> funk ),
608
+ 0UL ,
609
+ record_sz ,
610
+ NULL );
611
+
561
612
fd_program_cache_entry_t * writable_entry = fd_type_pun ( data );
562
613
563
- /* Parse the ELF info */
564
- if ( FD_UNLIKELY ( fd_program_cache_parse_elf_info ( & elf_info , program_data , program_data_len , slot_ctx ) ) ) {
565
- writable_entry -> failed_verification = 1 ;
566
- writable_entry -> last_slot_modified = 0UL ;
567
- writable_entry -> last_slot_verified = current_slot ;
614
+ /* If the ELF header parsing failed, publish a failed verification
615
+ record. */
616
+ if ( FD_UNLIKELY ( failed_elf_parsing ) ) {
617
+ fd_program_cache_entry_set_failed_verification ( writable_entry , current_slot );
568
618
fd_funk_rec_modify_publish ( query );
569
619
return ;
570
620
}
0 commit comments