diff --git a/cli/src/program.rs b/cli/src/program.rs index 04863c108..72f4ae845 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -44,7 +44,7 @@ use { }, solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery, solana_sdk::{ - account::Account, + account::{is_executable, Account}, account_utils::StateMut, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, @@ -1036,6 +1036,15 @@ fn get_default_program_keypair(program_location: &Option) -> Keypair { program_keypair } +fn is_account_executable(account: &Account) -> bool { + if account.owner == bpf_loader_deprecated::id() || account.owner == bpf_loader::id() { + account.executable + } else { + let feature_set = FeatureSet::all_enabled(); + is_executable(account, &feature_set) + } +} + /// Deploy program using upgradeable loader. It also can process program upgrades #[allow(clippy::too_many_arguments)] fn process_program_deploy( @@ -1092,7 +1101,7 @@ fn process_program_deploy( .into()); } - if !account.executable { + if !is_account_executable(&account) { // Continue an initial deploy true } else if let Ok(UpgradeableLoaderState::Program { @@ -2444,7 +2453,7 @@ fn complete_partial_program_init( ) -> Result<(Vec, u64), Box> { let mut instructions: Vec = vec![]; let mut balance_needed = 0; - if account.executable { + if is_account_executable(account) { return Err("Buffer account is already executable".into()); } if account.owner != *loader_id && !system_program::check_id(&account.owner) { diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 153cb0c11..ac937ef0d 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -14,9 +14,11 @@ use { solana_rpc_client::rpc_client::RpcClient, solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery, solana_sdk::{ + account::is_executable, account_utils::StateMut, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, commitment_config::CommitmentConfig, + feature_set::FeatureSet, pubkey::Pubkey, signature::{Keypair, NullSigner, Signer}, }, @@ -100,7 +102,8 @@ fn test_cli_program_deploy_non_upgradeable() { let account0 = rpc_client.get_account(&program_id).unwrap(); assert_eq!(account0.lamports, minimum_balance_for_program); assert_eq!(account0.owner, bpf_loader_upgradeable::id()); - assert!(account0.executable); + assert!(is_executable(&account0, &FeatureSet::all_enabled())); + let (programdata_pubkey, _) = Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id()); let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); @@ -109,7 +112,10 @@ fn test_cli_program_deploy_non_upgradeable() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] @@ -137,7 +143,7 @@ fn test_cli_program_deploy_non_upgradeable() { .unwrap(); assert_eq!(account1.lamports, minimum_balance_for_program); assert_eq!(account1.owner, bpf_loader_upgradeable::id()); - assert!(account1.executable); + assert!(is_executable(&account1, &FeatureSet::all_enabled())); let (programdata_pubkey, _) = Pubkey::find_program_address( &[custom_address_keypair.pubkey().as_ref()], &bpf_loader_upgradeable::id(), @@ -148,7 +154,10 @@ fn test_cli_program_deploy_non_upgradeable() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] @@ -376,7 +385,7 @@ fn test_cli_program_deploy_with_authority() { let program_account = rpc_client.get_account(&program_keypair.pubkey()).unwrap(); assert_eq!(program_account.lamports, minimum_balance_for_program); assert_eq!(program_account.owner, bpf_loader_upgradeable::id()); - assert!(program_account.executable); + assert!(is_executable(&program_account, &FeatureSet::all_enabled())); let (programdata_pubkey, _) = Pubkey::find_program_address( &[program_keypair.pubkey().as_ref()], &bpf_loader_upgradeable::id(), @@ -387,7 +396,10 @@ fn test_cli_program_deploy_with_authority() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] @@ -421,7 +433,7 @@ fn test_cli_program_deploy_with_authority() { let program_account = rpc_client.get_account(&program_pubkey).unwrap(); assert_eq!(program_account.lamports, minimum_balance_for_program); assert_eq!(program_account.owner, bpf_loader_upgradeable::id()); - assert!(program_account.executable); + assert!(is_executable(&program_account, &FeatureSet::all_enabled())); let (programdata_pubkey, _) = Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id()); let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); @@ -430,7 +442,10 @@ fn test_cli_program_deploy_with_authority() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] @@ -455,7 +470,7 @@ fn test_cli_program_deploy_with_authority() { let program_account = rpc_client.get_account(&program_pubkey).unwrap(); assert_eq!(program_account.lamports, minimum_balance_for_program); assert_eq!(program_account.owner, bpf_loader_upgradeable::id()); - assert!(program_account.executable); + assert!(is_executable(&program_account, &FeatureSet::all_enabled())); let (programdata_pubkey, _) = Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id()); let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); @@ -464,7 +479,10 @@ fn test_cli_program_deploy_with_authority() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] @@ -511,7 +529,7 @@ fn test_cli_program_deploy_with_authority() { let program_account = rpc_client.get_account(&program_pubkey).unwrap(); assert_eq!(program_account.lamports, minimum_balance_for_program); assert_eq!(program_account.owner, bpf_loader_upgradeable::id()); - assert!(program_account.executable); + assert!(is_executable(&program_account, &FeatureSet::all_enabled())); let (programdata_pubkey, _) = Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id()); let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); @@ -520,7 +538,10 @@ fn test_cli_program_deploy_with_authority() { minimum_balance_for_programdata ); assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id()); - assert!(!programdata_account.executable); + assert!(!is_executable( + &programdata_account, + &FeatureSet::all_enabled() + )); assert_eq!( programdata_account.data[UpgradeableLoaderState::size_of_programdata_metadata()..], program_data[..] diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 76ecb1013..fc40cdbba 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -962,8 +962,8 @@ mod tests { let owned_account = AccountSharedData::new(42, 1, &callee_program_id); let not_owned_account = AccountSharedData::new(84, 1, &solana_sdk::pubkey::new_rand()); let readonly_account = AccountSharedData::new(168, 1, &solana_sdk::pubkey::new_rand()); - let loader_account = AccountSharedData::new(0, 0, &native_loader::id()); - let mut program_account = AccountSharedData::new(1, 0, &native_loader::id()); + let loader_account = AccountSharedData::new(0, 1, &native_loader::id()); + let mut program_account = AccountSharedData::new(1, 1, &native_loader::id()); program_account.set_executable(true); let transaction_accounts = vec![ (solana_sdk::pubkey::new_rand(), owned_account), @@ -990,7 +990,7 @@ mod tests { let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default(); programs_loaded_for_tx_batch.replenish( callee_program_id, - Arc::new(LoadedProgram::new_builtin(0, 0, MockBuiltin::vm)), + Arc::new(LoadedProgram::new_builtin(0, 1, MockBuiltin::vm)), ); invoke_context.programs_loaded_for_tx_batch = &programs_loaded_for_tx_batch; diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 570efb02b..18ca16760 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -34,7 +34,8 @@ use { clock::Slot, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ - bpf_account_data_direct_mapping, enable_bpf_loader_extend_program_ix, + bpf_account_data_direct_mapping, deprecate_executable_meta_update_in_bpf_loader, + disable_bpf_loader_instructions, enable_bpf_loader_extend_program_ix, enable_bpf_loader_set_authority_checked_ix, native_programs_consume_cu, remove_bpf_loader_incorrect_program_id, FeatureSet, }, @@ -785,7 +786,15 @@ fn process_loader_upgradeable_instruction( }, &invoke_context.feature_set, )?; - program.set_executable(true)?; + + // Skip writing true to executable meta after bpf program deployment when + // `deprecate_executable_meta_update_in_bpf_loader` feature is activated. + if !invoke_context + .feature_set + .is_active(&deprecate_executable_meta_update_in_bpf_loader::id()) + { + program.set_executable(true)?; + } drop(program); ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id); @@ -1469,6 +1478,20 @@ fn process_loader_instruction(invoke_context: &mut InvokeContext) -> Result<(), ); return Err(InstructionError::IncorrectProgramId); } + + // Return `UnsupportedProgramId` error for bpf_loader when + // `disable_bpf_loader_instruction` feature is activated. + if invoke_context + .feature_set + .is_active(&disable_bpf_loader_instructions::id()) + { + ic_msg!( + invoke_context, + "BPF loader management instructions are no longer supported" + ); + return Err(InstructionError::UnsupportedProgramId); + } + let is_program_signer = program.is_signer(); match limited_deserialize(instruction_data)? { LoaderInstruction::Write { offset, bytes } => { @@ -1493,6 +1516,13 @@ fn process_loader_instruction(invoke_context: &mut InvokeContext) -> Result<(), {}, program.get_data(), ); + + // `deprecate_executable_meta_update_in_bpf_loader` feature doesn't + // apply to bpf_loader v2. Instead, the deployment by bpf_loader + // will be deprecated by its own feature + // `disable_bpf_loader_instructions`. Before we activate + // deprecate_executable_meta_update_in_bpf_loader, we should + // activate `disable_bpf_loader_instructions` first. program.set_executable(true)?; ic_msg!(invoke_context, "Finalized account {:?}", program.get_key()); } @@ -1779,6 +1809,10 @@ mod tests { expected_result, Entrypoint::vm, |invoke_context| { + let mut features = FeatureSet::all_enabled(); + features.deactivate(&disable_bpf_loader_instructions::id()); + features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); + invoke_context.feature_set = Arc::new(features); test_utils::load_all_invoked_programs(invoke_context); }, |_invoke_context| {}, @@ -1998,6 +2032,10 @@ mod tests { Err(InstructionError::ProgramFailedToComplete), Entrypoint::vm, |invoke_context| { + let mut features = FeatureSet::all_enabled(); + features.deactivate(&disable_bpf_loader_instructions::id()); + features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); + invoke_context.feature_set = Arc::new(features); invoke_context.mock_set_remaining(0); test_utils::load_all_invoked_programs(invoke_context); }, @@ -2543,7 +2581,12 @@ mod tests { instruction_accounts, expected_result, Entrypoint::vm, - |_invoke_context| {}, + |invoke_context| { + let mut features = FeatureSet::all_enabled(); + features.deactivate(&disable_bpf_loader_instructions::id()); + features.deactivate(&deprecate_executable_meta_update_in_bpf_loader::id()); + invoke_context.feature_set = Arc::new(features); + }, |_invoke_context| {}, ) } diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index ff54eca6b..d4cbd0964 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -938,7 +938,7 @@ mod tests { assert_eq!(account.lamports(), account_info.lamports()); assert_eq!(account.data(), &account_info.data.borrow()[..]); assert_eq!(account.owner(), account_info.owner); - assert_eq!(account.executable(), account_info.executable); + assert!(account_info.executable); assert_eq!(account.rent_epoch(), account_info.rent_epoch); assert_eq!( @@ -1023,7 +1023,7 @@ mod tests { assert_eq!(account.lamports(), account_info.lamports()); assert_eq!(account.data(), &account_info.data.borrow()[..]); assert_eq!(account.owner(), account_info.owner); - assert_eq!(account.executable(), account_info.executable); + assert!(account_info.executable); assert_eq!(account.rent_epoch(), account_info.rent_epoch); } diff --git a/programs/loader-v4/src/lib.rs b/programs/loader-v4/src/lib.rs index 9bc1bfb24..881dc7a1b 100644 --- a/programs/loader-v4/src/lib.rs +++ b/programs/loader-v4/src/lib.rs @@ -466,6 +466,7 @@ pub fn process_instruction_retract( let transaction_context = &invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let authority_address = instruction_context .get_index_of_instruction_account_in_transaction(1) .and_then(|index| transaction_context.get_key_of_account_at_index(index))?; diff --git a/programs/sbf/benches/bpf_loader.rs b/programs/sbf/benches/bpf_loader.rs index ac2a235c1..f433c8374 100644 --- a/programs/sbf/benches/bpf_loader.rs +++ b/programs/sbf/benches/bpf_loader.rs @@ -34,7 +34,7 @@ use { bpf_loader, client::SyncClient, entrypoint::SUCCESS, - feature_set::FeatureSet, + feature_set::{self, FeatureSet}, instruction::{AccountMeta, Instruction}, message::Message, native_loader, @@ -185,10 +185,21 @@ fn bench_program_alu(bencher: &mut Bencher) { #[bench] fn bench_program_execute_noop(bencher: &mut Bencher) { let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and benched. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let bank = Bank::new_for_benches(&genesis_config); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank.clone()); diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index ec76a5cca..c4006f205 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -323,11 +323,21 @@ fn test_program_sbf_sanity() { println!("Test program: {:?}", program.0); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); @@ -403,10 +413,21 @@ fn test_sol_alloc_free_no_longer_deployable() { let program_address = program_keypair.pubkey(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); // Populate loader account with elf that depends on _sol_alloc_free syscall @@ -516,10 +537,21 @@ fn test_program_sbf_duplicate_accounts() { println!("Test program: {:?}", program); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); let (bank, program_id) = load_program_and_advance_slot( @@ -620,10 +652,21 @@ fn test_program_sbf_error_handling() { println!("Test program: {:?}", program); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let (_, program_id) = load_program_and_advance_slot( @@ -726,10 +769,21 @@ fn test_return_data_and_log_data_syscall() { for program in programs.iter() { let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -794,10 +848,21 @@ fn test_program_sbf_invoke_sanity() { println!("Test program: {:?}", program); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -1191,10 +1256,20 @@ fn test_program_sbf_invoke_sanity() { #[cfg(feature = "sbf_rust")] fn test_program_sbf_program_id_spoofing() { let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -1242,10 +1317,20 @@ fn test_program_sbf_program_id_spoofing() { #[cfg(feature = "sbf_rust")] fn test_program_sbf_caller_has_access_to_cpi_program() { let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -1280,10 +1365,20 @@ fn test_program_sbf_ro_modify() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -1335,10 +1430,20 @@ fn test_program_sbf_call_depth() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let (_, program_id) = load_program_and_advance_slot( @@ -1369,10 +1474,20 @@ fn test_program_sbf_compute_budget() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let (_, program_id) = load_program_and_advance_slot( @@ -1497,10 +1612,20 @@ fn test_program_sbf_instruction_introspection() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50_000); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -1555,10 +1680,20 @@ fn test_program_sbf_test_use_latest_executor() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let panic_id = load_program( @@ -2247,10 +2382,21 @@ fn test_program_sbf_invoke_upgradeable_via_cpi() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let invoke_and_return = load_program( @@ -2376,7 +2522,6 @@ fn test_program_sbf_disguised_as_sbf_loader() { mint_keypair, .. } = create_genesis_config(50); - let mut bank = Bank::new_for_tests(&genesis_config); // disable native_programs_consume_cu feature to allow test program // not consume units. @@ -2386,6 +2531,8 @@ fn test_program_sbf_disguised_as_sbf_loader() { bank.deactivate_feature( &solana_sdk::feature_set::remove_bpf_loader_incorrect_program_id::id(), ); + bank.deactivate_feature(&feature_set::disable_bpf_loader_instructions::id()); + bank.deactivate_feature(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); let bank = bank.wrap_with_bank_forks_for_tests().0; let bank_client = BankClient::new_shared(bank); @@ -2406,10 +2553,21 @@ fn test_program_reads_from_program_account() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); @@ -2433,10 +2591,21 @@ fn test_program_sbf_c_dup() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let account_address = Pubkey::new_unique(); @@ -2468,10 +2637,21 @@ fn test_program_sbf_upgrade_via_cpi() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); let invoke_and_return = load_program( @@ -2578,10 +2758,21 @@ fn test_program_sbf_set_upgrade_authority_via_cpi() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank); @@ -2793,10 +2984,21 @@ fn test_program_sbf_finalize() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -2841,10 +3043,21 @@ fn test_program_sbf_ro_account_modify() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -2903,10 +3116,21 @@ fn test_program_sbf_realloc() { const START_BALANCE: u64 = 100_000_000_000; let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(1_000_000_000_000); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let mint_pubkey = mint_keypair.pubkey(); let signer = &[&mint_keypair]; for direct_mapping in [false, true] { @@ -3242,6 +3466,17 @@ fn test_program_sbf_realloc_invoke() { .. } = create_genesis_config(1_000_000_000_000); genesis_config.rent = Rent::default(); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let mint_pubkey = mint_keypair.pubkey(); let signer = &[&mint_keypair]; @@ -3757,10 +3992,21 @@ fn test_program_sbf_processed_inner_instruction() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(50); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let (bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -3837,12 +4083,22 @@ fn test_program_fees() { mint_keypair, .. } = create_genesis_config(500_000_000); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + genesis_config.fee_rate_governor = FeeRateGovernor::new(congestion_multiplier, 0); let mut bank = Bank::new_for_tests(&genesis_config); let fee_structure = FeeStructure::new(0.000005, 0.0, vec![(200, 0.0000005), (1400000, 0.000005)]); bank.fee_structure = fee_structure.clone(); - bank.feature_set = Arc::new(FeatureSet::all_enabled()); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank); @@ -3905,12 +4161,22 @@ fn test_program_fees() { #[cfg(feature = "sbf_rust")] fn test_get_minimum_delegation() { let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(100_123_456_789); - let mut bank = Bank::new_for_tests(&genesis_config); - bank.feature_set = Arc::new(FeatureSet::all_enabled()); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + + let bank = Bank::new_for_tests(&genesis_config); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank.clone()); @@ -3977,11 +4243,16 @@ fn test_cpi_account_ownership_writability() { mint_keypair, .. } = create_genesis_config(100_123_456_789); + let mut bank = Bank::new_for_tests(&genesis_config); let mut feature_set = FeatureSet::all_enabled(); if !direct_mapping { feature_set.deactivate(&feature_set::bpf_account_data_direct_mapping::id()); } + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + feature_set.deactivate(&feature_set::disable_bpf_loader_instructions::id()); + feature_set.deactivate(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); bank.feature_set = Arc::new(feature_set); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank); @@ -4164,6 +4435,11 @@ fn test_cpi_account_data_updates() { if !direct_mapping { feature_set.deactivate(&feature_set::bpf_account_data_direct_mapping::id()); } + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + feature_set.deactivate(&feature_set::disable_bpf_loader_instructions::id()); + feature_set.deactivate(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + bank.feature_set = Arc::new(feature_set); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank); @@ -4307,11 +4583,18 @@ fn test_cpi_deprecated_loader_realloc() { mint_keypair, .. } = create_genesis_config(100_123_456_789); + let mut bank = Bank::new_for_tests(&genesis_config); let mut feature_set = FeatureSet::all_enabled(); if !direct_mapping { feature_set.deactivate(&feature_set::bpf_account_data_direct_mapping::id()); } + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + feature_set.deactivate(&feature_set::disable_bpf_loader_instructions::id()); + feature_set.deactivate(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + bank.feature_set = Arc::new(feature_set); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); @@ -4416,13 +4699,22 @@ fn test_cpi_change_account_data_memory_allocation() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(100_123_456_789); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + let mut bank = Bank::new_for_tests(&genesis_config); - let feature_set = FeatureSet::all_enabled(); - bank.feature_set = Arc::new(feature_set); declare_process_instruction!(MockBuiltin, 42, |invoke_context| { let transaction_context = &invoke_context.transaction_context; @@ -4502,13 +4794,22 @@ fn test_cpi_invalid_account_info_pointers() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(100_123_456_789); - let mut bank = Bank::new_for_tests(&genesis_config); - let feature_set = FeatureSet::all_enabled(); - bank.feature_set = Arc::new(feature_set); + + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + + let bank = Bank::new_for_tests(&genesis_config); let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests(); let mut bank_client = BankClient::new_shared(bank); @@ -4565,11 +4866,21 @@ fn test_deny_executable_write() { solana_logger::setup(); let GenesisConfigInfo { - genesis_config, + mut genesis_config, mint_keypair, .. } = create_genesis_config(100_123_456_789); + // deactivate `disable_bpf_loader_instructions` feature so that the program + // can be loaded, finalized and tested. + genesis_config + .accounts + .remove(&feature_set::disable_bpf_loader_instructions::id()); + + genesis_config + .accounts + .remove(&feature_set::deprecate_executable_meta_update_in_bpf_loader::id()); + for direct_mapping in [false, true] { let mut bank = Bank::new_for_tests(&genesis_config); let feature_set = Arc::make_mut(&mut bank.feature_set); diff --git a/runtime/src/accounts/mod.rs b/runtime/src/accounts/mod.rs index 8b1121588..ef801be65 100644 --- a/runtime/src/accounts/mod.rs +++ b/runtime/src/accounts/mod.rs @@ -24,7 +24,10 @@ use { loaded_programs::LoadedProgramsForTxBatch, }, solana_sdk::{ - account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, + account::{ + create_executable_meta, is_builtin, is_executable, Account, AccountSharedData, + ReadableAccount, WritableAccount, + }, account_utils::StateMut, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, feature_set::{ @@ -279,7 +282,7 @@ fn load_transaction_accounts( return Err(TransactionError::InvalidWritableAccount); } - if account.executable() { + if is_builtin(&account) || is_executable(&account, feature_set) { // The upgradeable loader requires the derived ProgramData account if let Ok(UpgradeableLoaderState::Program { programdata_address, @@ -297,9 +300,13 @@ fn load_transaction_accounts( return Err(TransactionError::InvalidProgramForExecution); } } - } else if account.executable() && message.is_writable(i) { - error_counters.invalid_writable_account += 1; - return Err(TransactionError::InvalidWritableAccount); + } else { + if (is_builtin(&account) || is_executable(&account, feature_set)) + && message.is_writable(i) + { + error_counters.invalid_writable_account += 1; + return Err(TransactionError::InvalidWritableAccount); + } } } @@ -346,15 +353,17 @@ fn load_transaction_accounts( let (program_id, program_account) = accounts .get(program_index) .ok_or(TransactionError::ProgramAccountNotFound)?; - let account_found = accounts_found.get(program_index).unwrap_or(&true); if native_loader::check_id(program_id) { return Ok(account_indices); } + + let account_found = accounts_found.get(program_index).unwrap_or(&true); if !account_found { error_counters.account_not_found += 1; return Err(TransactionError::ProgramAccountNotFound); } - if !program_account.executable() { + + if !(is_builtin(program_account) || is_executable(program_account, feature_set)) { error_counters.invalid_program_for_execution += 1; return Err(TransactionError::InvalidProgramForExecution); } @@ -376,7 +385,8 @@ fn load_transaction_accounts( accounts_db.load_with_fixed_root(ancestors, owner_id) { if !native_loader::check_id(owner_account.owner()) - || !owner_account.executable() + || !(is_builtin(&owner_account) + || is_executable(&owner_account, feature_set)) { error_counters.invalid_program_for_execution += 1; return Err(TransactionError::InvalidProgramForExecution); @@ -442,6 +452,7 @@ fn account_shared_data_from_program( .ok_or(TransactionError::AccountNotFound)?; program_account.set_owner(**program_owner); program_account.set_executable(true); + program_account.set_data_from_slice(create_executable_meta(program_owner)); Ok(program_account) } @@ -908,7 +919,8 @@ mod tests { accounts.push((key0, account)); let mut account = AccountSharedData::new(40, 1, &Pubkey::default()); - account.set_executable(true); + account.set_owner(bpf_loader_upgradeable::id()); + account.set_data(create_executable_meta(account.owner()).to_vec()); accounts.push((key1, account)); let instructions = vec![CompiledInstruction::new(1, &(), vec![0])]; @@ -942,7 +954,7 @@ mod tests { let account = AccountSharedData::new(1, 0, &Pubkey::default()); accounts.push((key0, account)); - let account = AccountSharedData::new(40, 1, &native_loader::id()); + let account = AccountSharedData::new(40, 0, &native_loader::id()); accounts.push((key1, account)); let instructions = vec![CompiledInstruction::new(1, &(), vec![0])]; @@ -971,7 +983,7 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::from([5u8; 32]); + let key1 = bpf_loader_upgradeable::id(); let key2 = Pubkey::from([6u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); @@ -988,6 +1000,7 @@ mod tests { account.set_executable(true); account.set_rent_epoch(1); account.set_owner(key1); + account.set_data(create_executable_meta(account.owner()).to_vec()); accounts.push((key2, account)); let instructions = vec![ diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 54f58640b..9d0342fb2 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -126,9 +126,9 @@ use { }, solana_sdk::{ account::{ - create_account_shared_data_with_fields as create_account, from_account, Account, - AccountSharedData, InheritableAccountFields, ReadableAccount, WritableAccount, - PROGRAM_OWNERS, + create_account_shared_data_with_fields as create_account, create_executable_meta, + from_account, Account, AccountSharedData, InheritableAccountFields, ReadableAccount, + WritableAccount, PROGRAM_OWNERS, }, account_utils::StateMut, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, @@ -3882,7 +3882,6 @@ impl Bank { fn add_precompiled_account_with_owner(&self, program_id: &Pubkey, owner: Pubkey) { if let Some(account) = self.get_account_with_fixed_root(program_id) { if account.executable() { - // The account is already executable, that's all we need return; } else { // malicious account is pre-occupying at program_id @@ -3898,10 +3897,13 @@ impl Bank { // Add a bogus executable account, which will be loaded and ignored. let (lamports, rent_epoch) = self.inherit_specially_retained_account_fields(&None); + + // Mock account_data with executable_meta so that the account is executable. + let account_data = create_executable_meta(&owner); let account = AccountSharedData::from(Account { lamports, owner, - data: vec![], + data: account_data.to_vec(), executable: true, rent_epoch, }); diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index b746dad95..b31706b6e 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -1025,6 +1025,8 @@ fn test_rent_exempt_executable_account() { let account_balance = 1; let mut account = AccountSharedData::new(account_balance, 0, &solana_sdk::pubkey::new_rand()); account.set_executable(true); + account.set_owner(bpf_loader_upgradeable::id()); + account.set_data(create_executable_meta(account.owner()).to_vec()); bank.store_account(&account_pubkey, &account); let transfer_lamports = 1; @@ -6458,25 +6460,25 @@ fn test_bank_hash_consistency() { if bank.slot == 0 { assert_eq!( bank.hash().to_string(), - "3KE2bigpBiiMLGYNqmWkgbrQGSqMt5ccG6ED87CFCVpt" + "trdzvRDTAXAqo1i2GX4JfK9ReixV1NYNG7DRaVq43Do", ); } if bank.slot == 32 { assert_eq!( bank.hash().to_string(), - "FpNDsd21HXznXf6tRpMNiWhFyhZ4aCCECQm3gL4jGV22" + "2rdj8QEnDnBSyMv81rCmncss4UERACyXXB3pEvkep8eS", ); } if bank.slot == 64 { assert_eq!( bank.hash().to_string(), - "7gDCoXPfFtKPALi212akhhQHEuLdAqyf7DE3yUN4bR2p" + "7g3ofXVQB3reFt9ki8zLA8S4w1GdmEWsWuWrwkPN3SSv" ); } if bank.slot == 128 { assert_eq!( bank.hash().to_string(), - "6FREbeHdTNYnEXg4zobL2mqGfevukg75frkQJqKpYnk4" + "4uX1AZFbqwjwWBACWbAW3V8rjbWH4N3ZRTbNysSLAzj2" ); break; } diff --git a/sdk/src/account.rs b/sdk/src/account.rs index d94d7dc2c..a67f49ded 100644 --- a/sdk/src/account.rs +++ b/sdk/src/account.rs @@ -6,8 +6,9 @@ use { crate::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, clock::{Epoch, INITIAL_RENT_EPOCH}, + feature_set::{deprecate_executable_meta_update_in_bpf_loader, FeatureSet}, lamports::LamportsError, - loader_v4, + loader_v4, native_loader, pubkey::Pubkey, }, serde::{ @@ -39,6 +40,9 @@ pub struct Account { /// the program that owns this account. If executable, the program that loads this account. pub owner: Pubkey, /// this account's data contains a loaded program (and is now read-only) + /// + /// When feature `deprecate_executable_meta_update_in_bpf_loader` is active, + /// `executable` is deprecated, please use `fn is_executable(&account)` instead. pub executable: bool, /// the epoch at which this account will next owe rent pub rent_epoch: Epoch, @@ -764,6 +768,91 @@ pub const PROGRAM_OWNERS: &[Pubkey] = &[ loader_v4::id(), ]; +const LOADER_V4_STATUS_BYTE_OFFSET: usize = 40; + +/// Create executable account meta data based on account's `owner`. +/// +/// This function is only used for testing and an optimization during +/// transaction loading. +/// +/// When the program account is already present in the program cache, we don't +/// need to load the full account data during transaction loading. Instead, all +/// we need is a minimal executable account meta data, which is what this +/// function returns. +pub fn create_executable_meta(owner: &Pubkey) -> &[u8] { + // For upgradable program account, only `UpgradeableLoaderState::Program` + // variant (i.e. discriminant = 2) should *executable*, which means the + // discriminant for the enum at byte offset 0 in account data is 2. + const EXECUTABLE_META_FOR_BPF_LOADER_UPGRADABLE: [u8; 1] = [2]; + + // For loader v4 program, when LoaderV4Status (byte_offset = 40 in account + // data) is set, the program is executable. + const fn get_executable_meta_for_loader_v4() -> [u8; 41] { + let mut v = [0; LOADER_V4_STATUS_BYTE_OFFSET + 1]; + v[LOADER_V4_STATUS_BYTE_OFFSET] = 1; + v + } + const EXECUTABLE_META_FOR_LOADER_V4: [u8; LOADER_V4_STATUS_BYTE_OFFSET + 1] = + get_executable_meta_for_loader_v4(); + + // For other owners, simple returns a 1 byte array would make it executable. + const DEFAULT_EXECUTABLE_META: [u8; 1] = [1]; + + if bpf_loader_upgradeable::check_id(owner) { + &EXECUTABLE_META_FOR_BPF_LOADER_UPGRADABLE + } else if loader_v4::check_id(owner) { + &EXECUTABLE_META_FOR_LOADER_V4 + } else { + &DEFAULT_EXECUTABLE_META + } +} + +/// Return true if the account program is executable. +pub fn is_executable(account: &impl ReadableAccount, feature_set: &FeatureSet) -> bool { + if !feature_set.is_active(&deprecate_executable_meta_update_in_bpf_loader::id()) { + account.executable() + } else { + // First, check if the account is empty. Empty accounts are not executable. + if account.data().is_empty() { + return false; + } + + // bpf_loader/bpf_loader_deprecated still relies on `executable` on the + // program account. When the program account is finalized, the loader will + // mark `executable` flag on the account. We can't emulate `executable` for + // these two loaders. However, when `deprecate_executable` is true, we + // should have already disabled the deployment of bpf_loader and + // bpf_loader_deprecated. Therefore, we can safely assume that all those + // programs are `executable`. + if bpf_loader::check_id(account.owner()) || bpf_loader_deprecated::check_id(account.owner()) + { + return true; + } + + if bpf_loader_upgradeable::check_id(account.owner()) { + // For upgradable program account, only + // `UpgradeableLoaderState::Program` variant (i.e. discriminant = 2) is + // *executable*. + return account.data()[0] == 2; + } + + if loader_v4::check_id(account.owner()) { + // LoaderV4Status (byte_offset = 40) + // return account.data()[LOADER_V4_STATUS_BYTE_OFFSET] != 0; + return false; // TODO: return false for now + } + + false + } +} + +/// Return true if the account program is a builtin program. Note that for +/// builtin program, even when its account data is empty, it is still be +/// executable, such as vote program etc. +pub fn is_builtin(account: &impl ReadableAccount) -> bool { + native_loader::check_id(account.owner()) && !account.data().is_empty() +} + #[cfg(test)] pub mod tests { use super::*; diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 87d781fab..0a06cdd86 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -752,6 +752,14 @@ pub mod merkle_conflict_duplicate_proofs { solana_sdk::declare_id!("mrkPjRg79B2oK2ZLgd7S3AfEJaX9B6gAF3H9aEykRUS"); } +pub mod disable_bpf_loader_instructions { + solana_sdk::declare_id!("7WeS1vfPRgeeoXArLh7879YcB9mgE9ktjPDtajXeWfXn"); +} + +pub mod deprecate_executable_meta_update_in_bpf_loader { + solana_sdk::declare_id!("k6uR1J9VtKJnTukBV2Eo15BEy434MBg8bT6hHQgmU8v"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -935,6 +943,8 @@ lazy_static! { (consume_blockstore_duplicate_proofs::id(), "consume duplicate proofs from blockstore in consensus #34372"), (index_erasure_conflict_duplicate_proofs::id(), "generate duplicate proofs for index and erasure conflicts #34360"), (merkle_conflict_duplicate_proofs::id(), "generate duplicate proofs for merkle root conflicts #34270"), + (disable_bpf_loader_instructions::id(), "disable bpf loader management instructions #34194"), + (deprecate_executable_meta_update_in_bpf_loader::id(), "deprecate executable meta flag update in bpf loader #34194"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/sdk/src/transaction_context.rs b/sdk/src/transaction_context.rs index 8922a01f4..981f64870 100644 --- a/sdk/src/transaction_context.rs +++ b/sdk/src/transaction_context.rs @@ -17,7 +17,7 @@ use { }; use { crate::{ - account::{AccountSharedData, ReadableAccount}, + account::{is_builtin, is_executable, AccountSharedData, ReadableAccount}, feature_set::FeatureSet, instruction::InstructionError, pubkey::Pubkey, @@ -1040,8 +1040,8 @@ impl<'a> BorrowedAccount<'a> { /// Returns whether this account is executable (transaction wide) #[inline] - pub fn is_executable(&self, _feature_set: &FeatureSet) -> bool { - self.account.executable() + pub fn is_executable(&self, feature_set: &FeatureSet) -> bool { + is_builtin(&*self.account) || is_executable(&*self.account, feature_set) } /// Configures whether this account is executable (transaction wide)