Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 3ec2ffa

Browse files
Lichtsomergify[bot]
authored andcommitted
Fix - LoadedProgramType::Closed (#31922)
* Makes Bank::load_program() return correct tombstones. * Removes early TX failure caused by closed and invalid programs. * Adjusts the feature gate of simplify_writable_program_account_check. (cherry picked from commit 3f13cd3) # Conflicts: # runtime/src/accounts.rs
1 parent bf789b6 commit 3ec2ffa

File tree

4 files changed

+94
-96
lines changed

4 files changed

+94
-96
lines changed

ledger-tool/src/program.rs

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ use {
1515
},
1616
solana_program_runtime::{
1717
invoke_context::InvokeContext,
18-
loaded_programs::{
19-
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
20-
},
18+
loaded_programs::{LoadProgramMetrics, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET},
2119
with_mock_invoke_context,
2220
},
2321
solana_rbpf::{
@@ -540,22 +538,8 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
540538
let mut loaded_programs =
541539
LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
542540
for key in cached_account_keys {
543-
let program = bank.load_program(&key).unwrap_or_else(|err| {
544-
// Create a tombstone for the program in the cache
545-
debug!("Failed to load program {}, error {:?}", key, err);
546-
Arc::new(LoadedProgram::new_tombstone(
547-
0,
548-
LoadedProgramType::FailedVerification(
549-
bank.loaded_programs_cache
550-
.read()
551-
.unwrap()
552-
.program_runtime_environment_v1
553-
.clone(),
554-
),
555-
))
556-
});
541+
loaded_programs.replenish(key, bank.load_program(&key));
557542
debug!("Loaded program {}", key);
558-
loaded_programs.replenish(key, program);
559543
}
560544
invoke_context.programs_loaded_for_tx_batch = &loaded_programs;
561545

runtime/src/accounts.rs

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,15 @@ use {
2626
solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable},
2727
solana_program_runtime::{
2828
compute_budget::{self, ComputeBudget},
29-
loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch},
29+
loaded_programs::LoadedProgramsForTxBatch,
3030
},
3131
solana_sdk::{
3232
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
3333
account_utils::StateMut,
34-
bpf_loader_upgradeable,
34+
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
3535
clock::{BankId, Slot},
3636
feature_set::{
37-
self, add_set_tx_loaded_accounts_data_size_instruction,
38-
delay_visibility_of_program_deployment, enable_request_heap_frame_ix,
37+
self, add_set_tx_loaded_accounts_data_size_instruction, enable_request_heap_frame_ix,
3938
include_loaded_accounts_data_size_in_fee_calculation,
4039
remove_congestion_multiplier_from_fee_calculation, remove_deprecated_request_unit_ix,
4140
simplify_writable_program_account_check, use_default_units_in_fee_calculation,
@@ -292,27 +291,8 @@ impl Accounts {
292291

293292
fn account_shared_data_from_program(
294293
key: &Pubkey,
295-
feature_set: &FeatureSet,
296-
program: &LoadedProgram,
297294
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
298295
) -> Result<AccountSharedData> {
299-
// Check for tombstone
300-
let result = match &program.program {
301-
LoadedProgramType::FailedVerification(_) | LoadedProgramType::Closed => {
302-
Err(TransactionError::InvalidProgramForExecution)
303-
}
304-
LoadedProgramType::DelayVisibility => {
305-
debug_assert!(feature_set.is_active(&delay_visibility_of_program_deployment::id()));
306-
Err(TransactionError::InvalidProgramForExecution)
307-
}
308-
_ => Ok(()),
309-
};
310-
if feature_set.is_active(&simplify_writable_program_account_check::id()) {
311-
// Currently CPI only fails if an execution is actually attempted. With this check it
312-
// would also fail if a transaction just references an invalid program. So the checking
313-
// of the result is being feature gated.
314-
result?;
315-
}
316296
// It's an executable program account. The program is already loaded in the cache.
317297
// So the account data is not needed. Return a dummy AccountSharedData with meta
318298
// information.
@@ -384,23 +364,21 @@ impl Accounts {
384364
account_overrides.and_then(|overrides| overrides.get(key))
385365
{
386366
(account_override.data().len(), account_override.clone(), 0)
387-
} else if let Some(program) = (!instruction_account && !message.is_writable(i))
388-
.then_some(())
389-
.and_then(|_| loaded_programs.find(key))
367+
} else if let Some(program) = (feature_set
368+
.is_active(&simplify_writable_program_account_check::id())
369+
&& !instruction_account
370+
&& !message.is_writable(i))
371+
.then_some(())
372+
.and_then(|_| loaded_programs.find(key))
390373
{
391374
// This condition block does special handling for accounts that are passed
392375
// as instruction account to any of the instructions in the transaction.
393376
// It's been noticed that some programs are reading other program accounts
394377
// (that are passed to the program as instruction accounts). So such accounts
395378
// are needed to be loaded even though corresponding compiled program may
396379
// already be present in the cache.
397-
Self::account_shared_data_from_program(
398-
key,
399-
feature_set,
400-
program.as_ref(),
401-
program_accounts,
402-
)
403-
.map(|program_account| (program.account_size, program_account, 0))?
380+
Self::account_shared_data_from_program(key, program_accounts)
381+
.map(|program_account| (program.account_size, program_account, 0))?
404382
} else {
405383
self.accounts_db
406384
.load_with_fixed_root(ancestors, key)
@@ -456,17 +434,52 @@ impl Accounts {
456434
validated_fee_payer = true;
457435
}
458436

459-
if bpf_loader_upgradeable::check_id(account.owner()) {
460-
if !feature_set.is_active(&simplify_writable_program_account_check::id())
461-
&& message.is_writable(i)
462-
&& !message.is_upgradeable_loader_present()
463-
{
437+
if !feature_set.is_active(&simplify_writable_program_account_check::id()) {
438+
if bpf_loader_upgradeable::check_id(account.owner()) {
439+
if message.is_writable(i) && !message.is_upgradeable_loader_present() {
440+
error_counters.invalid_writable_account += 1;
441+
return Err(TransactionError::InvalidWritableAccount);
442+
}
443+
444+
if account.executable() {
445+
// The upgradeable loader requires the derived ProgramData account
446+
if let Ok(UpgradeableLoaderState::Program {
447+
programdata_address,
448+
}) = account.state()
449+
{
450+
if self
451+
.accounts_db
452+
.load_with_fixed_root(ancestors, &programdata_address)
453+
.is_none()
454+
{
455+
error_counters.account_not_found += 1;
456+
return Err(TransactionError::ProgramAccountNotFound);
457+
}
458+
} else {
459+
error_counters.invalid_program_for_execution += 1;
460+
return Err(TransactionError::InvalidProgramForExecution);
461+
}
462+
}
463+
} else if account.executable() && message.is_writable(i) {
464464
error_counters.invalid_writable_account += 1;
465465
return Err(TransactionError::InvalidWritableAccount);
466466
}
467+
<<<<<<< HEAD
467468
} else if account.executable() && message.is_writable(i) {
468469
error_counters.invalid_writable_account += 1;
469470
return Err(TransactionError::InvalidWritableAccount);
471+
=======
472+
}
473+
474+
if in_reward_interval
475+
&& message.is_writable(i)
476+
&& solana_stake_program::check_id(account.owner())
477+
{
478+
error_counters.program_execution_temporarily_restricted += 1;
479+
return Err(TransactionError::ProgramExecutionTemporarilyRestricted {
480+
account_index: i as u8,
481+
});
482+
>>>>>>> 3f13cd353 (Fix - LoadedProgramType::Closed (#31922))
470483
}
471484

472485
tx_rent += rent;
@@ -1478,7 +1491,6 @@ mod tests {
14781491
},
14791492
solana_sdk::{
14801493
account::{AccountSharedData, WritableAccount},
1481-
bpf_loader_upgradeable::UpgradeableLoaderState,
14821494
compute_budget::ComputeBudgetInstruction,
14831495
epoch_schedule::EpochSchedule,
14841496
genesis_config::ClusterType,
@@ -2477,8 +2489,12 @@ mod tests {
24772489
instructions,
24782490
);
24792491
let tx = Transaction::new(&[&keypair], message.clone(), Hash::default());
2480-
let loaded_accounts =
2481-
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
2492+
let loaded_accounts = load_accounts_with_excluded_features(
2493+
tx,
2494+
&accounts,
2495+
&mut error_counters,
2496+
Some(&[simplify_writable_program_account_check::id()]),
2497+
);
24822498

24832499
assert_eq!(error_counters.invalid_writable_account, 1);
24842500
assert_eq!(loaded_accounts.len(), 1);
@@ -2491,8 +2507,12 @@ mod tests {
24912507
message.account_keys = vec![key0, key1, key2]; // revert key change
24922508
message.header.num_readonly_unsigned_accounts = 2; // mark both executables as readonly
24932509
let tx = Transaction::new(&[&keypair], message, Hash::default());
2494-
let loaded_accounts =
2495-
load_accounts_with_excluded_features(tx, &accounts, &mut error_counters, None);
2510+
let loaded_accounts = load_accounts_with_excluded_features(
2511+
tx,
2512+
&accounts,
2513+
&mut error_counters,
2514+
Some(&[simplify_writable_program_account_check::id()]),
2515+
);
24962516

24972517
assert_eq!(error_counters.invalid_writable_account, 1);
24982518
assert_eq!(loaded_accounts.len(), 1);

runtime/src/bank.rs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,11 +4128,14 @@ impl Bank {
41284128
}
41294129
}
41304130

4131-
pub fn load_program(&self, pubkey: &Pubkey) -> Result<Arc<LoadedProgram>> {
4131+
pub fn load_program(&self, pubkey: &Pubkey) -> Arc<LoadedProgram> {
41324132
let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) {
41334133
program
41344134
} else {
4135-
return Err(TransactionError::ProgramAccountNotFound);
4135+
return Arc::new(LoadedProgram::new_tombstone(
4136+
self.slot,
4137+
LoadedProgramType::Closed,
4138+
));
41364139
};
41374140
let mut transaction_accounts = vec![(*pubkey, program)];
41384141
let is_upgradeable_loader =
@@ -4147,10 +4150,16 @@ impl Bank {
41474150
{
41484151
transaction_accounts.push((programdata_address, programdata_account));
41494152
} else {
4150-
return Err(TransactionError::ProgramAccountNotFound);
4153+
return Arc::new(LoadedProgram::new_tombstone(
4154+
self.slot,
4155+
LoadedProgramType::Closed,
4156+
));
41514157
}
41524158
} else {
4153-
return Err(TransactionError::ProgramAccountNotFound);
4159+
return Arc::new(LoadedProgram::new_tombstone(
4160+
self.slot,
4161+
LoadedProgramType::Closed,
4162+
));
41544163
}
41554164
}
41564165
let mut transaction_context = TransactionContext::new(
@@ -4159,24 +4168,20 @@ impl Bank {
41594168
1,
41604169
1,
41614170
);
4162-
let instruction_context = transaction_context
4163-
.get_next_instruction_context()
4164-
.map_err(|err| TransactionError::InstructionError(0, err))?;
4171+
let instruction_context = transaction_context.get_next_instruction_context().unwrap();
41654172
instruction_context.configure(if is_upgradeable_loader { &[0, 1] } else { &[0] }, &[], &[]);
4166-
transaction_context
4167-
.push()
4168-
.map_err(|err| TransactionError::InstructionError(0, err))?;
4173+
transaction_context.push().unwrap();
41694174
let instruction_context = transaction_context
41704175
.get_current_instruction_context()
4171-
.map_err(|err| TransactionError::InstructionError(0, err))?;
4176+
.unwrap();
41724177
let program = instruction_context
41734178
.try_borrow_program_account(&transaction_context, 0)
4174-
.map_err(|err| TransactionError::InstructionError(0, err))?;
4179+
.unwrap();
41754180
let programdata = if is_upgradeable_loader {
41764181
Some(
41774182
instruction_context
41784183
.try_borrow_program_account(&transaction_context, 1)
4179-
.map_err(|err| TransactionError::InstructionError(0, err))?,
4184+
.unwrap(),
41804185
)
41814186
} else {
41824187
None
@@ -4192,14 +4197,19 @@ impl Bank {
41924197
None, // log_collector
41934198
&program,
41944199
programdata.as_ref().unwrap_or(&program),
4195-
program_runtime_environment_v1,
4200+
program_runtime_environment_v1.clone(),
41964201
)
41974202
.map(|(loaded_program, metrics)| {
41984203
let mut timings = ExecuteDetailsTimings::default();
41994204
metrics.submit_datapoint(&mut timings);
42004205
loaded_program
42014206
})
4202-
.map_err(|err| TransactionError::InstructionError(0, err))
4207+
.unwrap_or_else(|_| {
4208+
Arc::new(LoadedProgram::new_tombstone(
4209+
self.slot,
4210+
LoadedProgramType::FailedVerification(program_runtime_environment_v1),
4211+
))
4212+
})
42034213
}
42044214

42054215
pub fn clear_program_cache(&self) {
@@ -4445,20 +4455,7 @@ impl Bank {
44454455
let missing_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = missing_programs
44464456
.iter()
44474457
.map(|(key, count)| {
4448-
let program = self.load_program(key).unwrap_or_else(|err| {
4449-
// Create a tombstone for the program in the cache
4450-
debug!("Failed to load program {}, error {:?}", key, err);
4451-
Arc::new(LoadedProgram::new_tombstone(
4452-
self.slot,
4453-
LoadedProgramType::FailedVerification(
4454-
self.loaded_programs_cache
4455-
.read()
4456-
.unwrap()
4457-
.program_runtime_environment_v1
4458-
.clone(),
4459-
),
4460-
))
4461-
});
4458+
let program = self.load_program(key);
44624459
program.tx_usage_counter.store(*count, Ordering::Relaxed);
44634460
(*key, program)
44644461
})

runtime/src/bank/tests.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7397,8 +7397,6 @@ fn test_bank_load_program() {
73977397
bank.store_account_and_update_capitalization(&key1, &program_account);
73987398
bank.store_account_and_update_capitalization(&programdata_key, &programdata_account);
73997399
let program = bank.load_program(&key1);
7400-
assert!(program.is_ok());
7401-
let program = program.unwrap();
74027400
assert!(matches!(program.program, LoadedProgramType::LegacyV1(_)));
74037401
assert_eq!(
74047402
program.account_size,
@@ -7412,7 +7410,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
74127410
let mut bank = Bank::new_for_tests(&genesis_config);
74137411
bank.feature_set = Arc::new(FeatureSet::all_enabled());
74147412
let bank = Arc::new(bank);
7415-
let bank_client = BankClient::new_shared(&bank);
7413+
let mut bank_client = BankClient::new_shared(&bank);
74167414

74177415
// Setup keypairs and addresses
74187416
let payer_keypair = Keypair::new();
@@ -7553,9 +7551,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
75537551
assert_eq!(*elf.get(i).unwrap(), *byte);
75547552
}
75557553

7556-
let loaded_program = bank
7557-
.load_program(&program_keypair.pubkey())
7558-
.expect("Failed to load the program");
7554+
let loaded_program = bank.load_program(&program_keypair.pubkey());
75597555

75607556
// Invoke deployed program
75617557
mock_process_instruction(
@@ -7583,6 +7579,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
75837579
// Test initialized program account
75847580
bank.clear_signatures();
75857581
bank.store_account(&buffer_address, &buffer_account);
7582+
let bank = bank_client.advance_slot(1, &mint_keypair.pubkey()).unwrap();
75867583
let message = Message::new(
75877584
&[Instruction::new_with_bincode(
75887585
bpf_loader_upgradeable::id(),

0 commit comments

Comments
 (0)