From d03075ec23ccdc91b722c5ff1043c1b0783dd650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Wed, 10 Jul 2024 10:48:01 +0200 Subject: [PATCH] Adjusts tests. --- programs/bpf_loader/src/lib.rs | 26 ++++++- programs/sbf/c/src/invoke/invoke.c | 64 ++++++++------- .../sbf/rust/deprecated_loader/src/lib.rs | 4 +- programs/sbf/rust/invoke/src/lib.rs | 13 +++- programs/sbf/rust/invoke_dep/src/lib.rs | 78 ++++++++++--------- programs/sbf/tests/programs.rs | 18 ++++- svm/src/account_loader.rs | 10 ++- 7 files changed, 138 insertions(+), 75 deletions(-) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 6db74a50553794..496f524e169c78 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -1723,13 +1723,21 @@ mod tests { // Case: Account not a program program_account.set_executable(false); - process_instruction( + mock_process_instruction( &loader_id, - &[0], + vec![0], &[], vec![(program_id, program_account)], Vec::new(), Err(InstructionError::IncorrectProgramId), + Entrypoint::vm, + |invoke_context| { + let mut feature_set = invoke_context.get_feature_set().clone(); + feature_set.deactivate(&infer_account_is_executable_flag::id()); + invoke_context.mock_set_feature_set(Arc::new(feature_set)); + test_utils::load_all_invoked_programs(invoke_context); + }, + |_invoke_context| {}, ); } @@ -2404,10 +2412,22 @@ mod tests { .unwrap() .1 .set_executable(false); - process_instruction( + let instruction_data = bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap(); + mock_process_instruction( + &bpf_loader_upgradeable::id(), + Vec::new(), + &instruction_data, transaction_accounts, instruction_accounts, Err(InstructionError::AccountNotExecutable), + Entrypoint::vm, + |invoke_context| { + let mut feature_set = invoke_context.get_feature_set().clone(); + feature_set.deactivate(&infer_account_is_executable_flag::id()); + invoke_context.mock_set_feature_set(Arc::new(feature_set)); + test_utils::load_all_invoked_programs(invoke_context); + }, + |_invoke_context| {}, ); // Case: Program account now owned by loader diff --git a/programs/sbf/c/src/invoke/invoke.c b/programs/sbf/c/src/invoke/invoke.c index 1ff4e6b69a096c..1e5691ffdc8e3b 100644 --- a/programs/sbf/c/src/invoke/invoke.c +++ b/programs/sbf/c/src/invoke/invoke.c @@ -13,30 +13,31 @@ static const uint8_t TEST_SUCCESS = 1; static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2; static const uint8_t TEST_PRIVILEGE_ESCALATION_WRITABLE = 3; -static const uint8_t TEST_PPROGRAM_NOT_EXECUTABLE = 4; -static const uint8_t TEST_EMPTY_ACCOUNTS_SLICE = 5; -static const uint8_t TEST_CAP_SEEDS = 6; -static const uint8_t TEST_CAP_SIGNERS = 7; -static const uint8_t TEST_ALLOC_ACCESS_VIOLATION = 8; -static const uint8_t TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED = 9; -static const uint8_t TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED = 10; -static const uint8_t TEST_RETURN_ERROR = 11; -static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 12; -static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 13; -static const uint8_t TEST_WRITABLE_DEESCALATION_WRITABLE = 14; -static const uint8_t TEST_NESTED_INVOKE_TOO_DEEP = 15; -static const uint8_t TEST_CALL_PRECOMPILE = 16; -static const uint8_t ADD_LAMPORTS = 17; -static const uint8_t TEST_RETURN_DATA_TOO_LARGE = 18; -static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER = 19; -static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE = 20; -static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED = 21; +static const uint8_t TEST_PPROGRAM_NOT_OWNED_BY_LOADER = 4; +static const uint8_t TEST_PPROGRAM_NOT_EXECUTABLE = 5; +static const uint8_t TEST_EMPTY_ACCOUNTS_SLICE = 6; +static const uint8_t TEST_CAP_SEEDS = 7; +static const uint8_t TEST_CAP_SIGNERS = 8; +static const uint8_t TEST_ALLOC_ACCESS_VIOLATION = 9; +static const uint8_t TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED = 10; +static const uint8_t TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED = 11; +static const uint8_t TEST_RETURN_ERROR = 12; +static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 13; +static const uint8_t TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 14; +static const uint8_t TEST_WRITABLE_DEESCALATION_WRITABLE = 15; +static const uint8_t TEST_NESTED_INVOKE_TOO_DEEP = 16; +static const uint8_t TEST_CALL_PRECOMPILE = 17; +static const uint8_t ADD_LAMPORTS = 18; +static const uint8_t TEST_RETURN_DATA_TOO_LARGE = 19; +static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER = 20; +static const uint8_t TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE = 21; +static const uint8_t TEST_MAX_ACCOUNT_INFOS_EXCEEDED = 22; // TEST_CPI_INVALID_* must match the definitions in // https://github.com/solana-labs/solana/blob/master/programs/sbf/rust/invoke/src/instructions.rs -static const uint8_t TEST_CPI_INVALID_KEY_POINTER = 34; -static const uint8_t TEST_CPI_INVALID_OWNER_POINTER = 35; -static const uint8_t TEST_CPI_INVALID_LAMPORTS_POINTER = 36; -static const uint8_t TEST_CPI_INVALID_DATA_POINTER = 37; +static const uint8_t TEST_CPI_INVALID_KEY_POINTER = 35; +static const uint8_t TEST_CPI_INVALID_OWNER_POINTER = 36; +static const uint8_t TEST_CPI_INVALID_LAMPORTS_POINTER = 37; +static const uint8_t TEST_CPI_INVALID_DATA_POINTER = 38; static const int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; @@ -51,6 +52,7 @@ static const int SYSTEM_PROGRAM_INDEX = 9; static const int FROM_INDEX = 10; static const int ED25519_PROGRAM_INDEX = 11; static const int INVOKE_PROGRAM_INDEX = 12; +static const int UNEXECUTABLE_PROGRAM_INDEX = 13; uint64_t do_nested_invokes(uint64_t num_nested_invokes, SolAccountInfo *accounts, uint64_t num_accounts) { @@ -84,7 +86,7 @@ uint64_t do_nested_invokes(uint64_t num_nested_invokes, extern uint64_t entrypoint(const uint8_t *input) { sol_log("invoke C program"); - SolAccountInfo accounts[13]; + SolAccountInfo accounts[14]; SolParameters params = (SolParameters){.ka = accounts}; if (!sol_deserialize(input, ¶ms, SOL_ARRAY_SIZE(accounts))) { @@ -374,12 +376,22 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)); break; } + case TEST_PPROGRAM_NOT_OWNED_BY_LOADER: { + sol_log("Test program not owned by loader"); + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {RETURN_OK}; + const SolInstruction instruction = {accounts[ARGUMENT_INDEX].key, arguments, + SOL_ARRAY_SIZE(arguments), data, + SOL_ARRAY_SIZE(data)}; + return sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)); + } case TEST_PPROGRAM_NOT_EXECUTABLE: { sol_log("Test program not executable"); SolAccountMeta arguments[] = { - {accounts[DERIVED_KEY3_INDEX].key, false, false}}; - uint8_t data[] = {VERIFY_PRIVILEGE_ESCALATION}; - const SolInstruction instruction = {accounts[ARGUMENT_INDEX].key, arguments, + {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {RETURN_OK}; + const SolInstruction instruction = {accounts[UNEXECUTABLE_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), data, SOL_ARRAY_SIZE(data)}; return sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)); diff --git a/programs/sbf/rust/deprecated_loader/src/lib.rs b/programs/sbf/rust/deprecated_loader/src/lib.rs index c2573fdcc771e6..ec7719f559412e 100644 --- a/programs/sbf/rust/deprecated_loader/src/lib.rs +++ b/programs/sbf/rust/deprecated_loader/src/lib.rs @@ -17,8 +17,8 @@ use solana_program::{ pub const REALLOC: u8 = 1; pub const REALLOC_EXTEND_FROM_SLICE: u8 = 12; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS: u8 = 28; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED: u8 = 29; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS: u8 = 29; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED: u8 = 30; #[derive(Debug, PartialEq)] struct SStruct { diff --git a/programs/sbf/rust/invoke/src/lib.rs b/programs/sbf/rust/invoke/src/lib.rs index be29d6760bec25..64fccc2808965c 100644 --- a/programs/sbf/rust/invoke/src/lib.rs +++ b/programs/sbf/rust/invoke/src/lib.rs @@ -461,11 +461,20 @@ fn process_instruction<'a>( invoked_instruction.accounts[0].is_writable = true; invoke(&invoked_instruction, accounts)?; } + TEST_PPROGRAM_NOT_OWNED_BY_LOADER => { + msg!("Test program not owned by loader"); + let instruction = create_instruction( + *accounts[ARGUMENT_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], + vec![RETURN_OK], + ); + invoke(&instruction, accounts)?; + } TEST_PPROGRAM_NOT_EXECUTABLE => { msg!("Test program not executable"); let instruction = create_instruction( - *accounts[ARGUMENT_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, true, true)], + *accounts[UNEXECUTABLE_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], vec![RETURN_OK], ); invoke(&instruction, accounts)?; diff --git a/programs/sbf/rust/invoke_dep/src/lib.rs b/programs/sbf/rust/invoke_dep/src/lib.rs index 066e900b7f9d2e..19acfc1db6fcf9 100644 --- a/programs/sbf/rust/invoke_dep/src/lib.rs +++ b/programs/sbf/rust/invoke_dep/src/lib.rs @@ -3,44 +3,45 @@ pub const TEST_SUCCESS: u8 = 1; pub const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; pub const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; -pub const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; -pub const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; -pub const TEST_CAP_SEEDS: u8 = 6; -pub const TEST_CAP_SIGNERS: u8 = 7; -pub const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8; -pub const TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED: u8 = 9; -pub const TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED: u8 = 10; -pub const TEST_RETURN_ERROR: u8 = 11; -pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; -pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; -pub const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14; -pub const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15; -pub const TEST_CALL_PRECOMPILE: u8 = 16; -pub const ADD_LAMPORTS: u8 = 17; -pub const TEST_RETURN_DATA_TOO_LARGE: u8 = 18; -pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER: u8 = 19; -pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE: u8 = 20; -pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED: u8 = 21; -pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE: u8 = 22; -pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED: u8 = 23; -pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER: u8 = 24; -pub const TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER: u8 = 25; -pub const TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE: u8 = 26; -pub const TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER: u8 = 27; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS: u8 = 28; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED: u8 = 29; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS: u8 = 30; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN: u8 = 31; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS: u8 = 32; -pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED: u8 = 33; -pub const TEST_CPI_INVALID_KEY_POINTER: u8 = 34; -pub const TEST_CPI_INVALID_OWNER_POINTER: u8 = 35; -pub const TEST_CPI_INVALID_LAMPORTS_POINTER: u8 = 36; -pub const TEST_CPI_INVALID_DATA_POINTER: u8 = 37; -pub const TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION: u8 = 38; -pub const TEST_WRITE_ACCOUNT: u8 = 39; -pub const TEST_CALLEE_ACCOUNT_UPDATES: u8 = 40; -pub const TEST_STACK_HEAP_ZEROED: u8 = 41; +pub const TEST_PPROGRAM_NOT_OWNED_BY_LOADER: u8 = 4; +pub const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 5; +pub const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 6; +pub const TEST_CAP_SEEDS: u8 = 7; +pub const TEST_CAP_SIGNERS: u8 = 8; +pub const TEST_ALLOC_ACCESS_VIOLATION: u8 = 9; +pub const TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED: u8 = 10; +pub const TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED: u8 = 11; +pub const TEST_RETURN_ERROR: u8 = 12; +pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 13; +pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 14; +pub const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 15; +pub const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 16; +pub const TEST_CALL_PRECOMPILE: u8 = 17; +pub const ADD_LAMPORTS: u8 = 18; +pub const TEST_RETURN_DATA_TOO_LARGE: u8 = 19; +pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER: u8 = 20; +pub const TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE: u8 = 21; +pub const TEST_MAX_ACCOUNT_INFOS_EXCEEDED: u8 = 22; +pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE: u8 = 23; +pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED: u8 = 24; +pub const TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER: u8 = 25; +pub const TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER: u8 = 26; +pub const TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE: u8 = 27; +pub const TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER: u8 = 28; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS: u8 = 29; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED: u8 = 30; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS: u8 = 31; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN: u8 = 32; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS: u8 = 33; +pub const TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED: u8 = 34; +pub const TEST_CPI_INVALID_KEY_POINTER: u8 = 35; +pub const TEST_CPI_INVALID_OWNER_POINTER: u8 = 36; +pub const TEST_CPI_INVALID_LAMPORTS_POINTER: u8 = 37; +pub const TEST_CPI_INVALID_DATA_POINTER: u8 = 38; +pub const TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION: u8 = 39; +pub const TEST_WRITE_ACCOUNT: u8 = 40; +pub const TEST_CALLEE_ACCOUNT_UPDATES: u8 = 41; +pub const TEST_STACK_HEAP_ZEROED: u8 = 42; pub const MINT_INDEX: usize = 0; pub const ARGUMENT_INDEX: usize = 1; @@ -55,3 +56,4 @@ pub const SYSTEM_PROGRAM_INDEX: usize = 9; pub const FROM_INDEX: usize = 10; pub const ED25519_PROGRAM_INDEX: usize = 11; pub const INVOKE_PROGRAM_INDEX: usize = 12; +pub const UNEXECUTABLE_PROGRAM_INDEX: usize = 13; diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 55802a89dcbc33..e10a7cde0e0c8f 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -747,6 +747,10 @@ fn test_program_sbf_invoke_sanity() { let account = AccountSharedData::new(84, 0, &system_program::id()); bank.store_account(&from_keypair.pubkey(), &account); + let unexecutable_program_keypair = Keypair::new(); + let account = AccountSharedData::new(1, 0, &bpf_loader::id()); + bank.store_account(&unexecutable_program_keypair.pubkey(), &account); + let (derived_key1, bump_seed1) = Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id); let (derived_key2, bump_seed2) = @@ -769,6 +773,7 @@ fn test_program_sbf_invoke_sanity() { AccountMeta::new(from_keypair.pubkey(), true), AccountMeta::new_readonly(solana_sdk::ed25519_program::id(), false), AccountMeta::new_readonly(invoke_program_id, false), + AccountMeta::new_readonly(unexecutable_program_keypair.pubkey(), false), ]; // success cases @@ -920,10 +925,17 @@ fn test_program_sbf_invoke_sanity() { None, ); + do_invoke_failure_test_local( + TEST_PPROGRAM_NOT_OWNED_BY_LOADER, + TransactionError::InstructionError(0, InstructionError::UnsupportedProgramId), + &[argument_keypair.pubkey()], + None, + ); + do_invoke_failure_test_local( TEST_PPROGRAM_NOT_EXECUTABLE, - TransactionError::InstructionError(0, InstructionError::AccountNotExecutable), - &[], + TransactionError::InstructionError(0, InstructionError::InvalidAccountData), + &[unexecutable_program_keypair.pubkey()], None, ); @@ -4641,7 +4653,7 @@ fn test_deny_executable_write() { let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::ExecutableDataModified) + TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified) ); } } diff --git a/svm/src/account_loader.rs b/svm/src/account_loader.rs index 65bbc8e3d41c01..71632e3d882299 100644 --- a/svm/src/account_loader.rs +++ b/svm/src/account_loader.rs @@ -701,7 +701,15 @@ mod tests { instructions, ); - let loaded_accounts = load_accounts_aux_test(tx, &accounts, &mut error_metrics); + let mut feature_set = FeatureSet::all_enabled(); + feature_set.deactivate(&feature_set::infer_account_is_executable_flag::id()); + let loaded_accounts = load_accounts_with_features_and_rent( + tx, + &accounts, + &RentCollector::default(), + &mut error_metrics, + &mut feature_set, + ); assert_eq!(error_metrics.invalid_program_for_execution, 1); assert_eq!(loaded_accounts.len(), 1);