diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 2eaef8386a..0fa0ab1397 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -15,6 +15,8 @@ static const uint8_t TEST_ALLOC_ACCESS_VIOLATION = 8; static const uint8_t TEST_INSTRUCTION_DATA_TOO_LARGE = 9; static const uint8_t TEST_INSTRUCTION_META_TOO_LARGE = 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 int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; @@ -228,6 +230,20 @@ extern uint64_t entrypoint(const uint8_t *input) { 10 + 5 - 1 - 1 - 1 - 1); } + sol_log("Test privilege deescalation"); + { + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + } + sol_log("Verify data values are retained and updated"); for (int i = 0; i < accounts[ARGUMENT_INDEX].data_len; i++) { sol_assert(accounts[ARGUMENT_INDEX].data[i] == i); @@ -436,6 +452,38 @@ extern uint64_t entrypoint(const uint8_t *input) { sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)); break; } + + case TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: { + sol_log("Test privilege deescalation escalation signer"); + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + SolAccountMeta arguments[] = { + {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, + {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; + } + case TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: { + sol_log("Test privilege deescalation escalation writable"); + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(true == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + SolAccountMeta arguments[] = { + {accounts[INVOKED_PROGRAM_INDEX].key, false, false}, + {accounts[INVOKED_ARGUMENT_INDEX].key, false, false}}; + uint8_t data[] = {VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; + } + default: sol_panic(); } diff --git a/programs/bpf/c/src/invoked/instruction.h b/programs/bpf/c/src/invoked/instruction.h index 69e0dfa1c1..4863a70e5b 100644 --- a/programs/bpf/c/src/invoked/instruction.h +++ b/programs/bpf/c/src/invoked/instruction.h @@ -12,3 +12,6 @@ const uint8_t VERIFY_WRITER = 4; const uint8_t VERIFY_PRIVILEGE_ESCALATION = 5; const uint8_t NESTED_INVOKE = 6; const uint8_t RETURN_OK = 7; +const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8; +const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9; +const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10; diff --git a/programs/bpf/c/src/invoked/invoked.c b/programs/bpf/c/src/invoked/invoked.c index 4da30a9add..4bde1f2341 100644 --- a/programs/bpf/c/src/invoked/invoked.c +++ b/programs/bpf/c/src/invoked/invoked.c @@ -5,6 +5,8 @@ #include extern uint64_t entrypoint(const uint8_t *input) { + sol_log("Invoked C program"); + SolAccountInfo accounts[4]; SolParameters params = (SolParameters){.ka = accounts}; @@ -157,9 +159,54 @@ extern uint64_t entrypoint(const uint8_t *input) { break; } case VERIFY_PRIVILEGE_ESCALATION: { - sol_log("Success"); + sol_log("Should never get here!"); break; } + + case VERIFY_PRIVILEGE_DEESCALATION: { + sol_log("verify privilege deescalation"); + static const int INVOKED_ARGUMENT_INDEX = 0; + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + break; + } + case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: { + sol_log("verify privilege deescalation escalation signer"); + static const int INVOKED_PROGRAM_INDEX = 0; + static const int INVOKED_ARGUMENT_INDEX = 1; + sol_assert(sol_deserialize(input, ¶ms, 2)); + + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, true, false}}; + uint8_t data[] = {VERIFY_PRIVILEGE_ESCALATION}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; + } + case VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: { + sol_log("verify privilege deescalation escalation writable"); + static const int INVOKED_PROGRAM_INDEX = 0; + static const int INVOKED_ARGUMENT_INDEX = 1; + sol_assert(sol_deserialize(input, ¶ms, 2)); + + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_signer); + sol_assert(false == accounts[INVOKED_ARGUMENT_INDEX].is_writable); + SolAccountMeta arguments[] = { + {accounts[INVOKED_ARGUMENT_INDEX].key, false, true}}; + uint8_t data[] = {VERIFY_PRIVILEGE_ESCALATION}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + sol_assert(SUCCESS == + sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); + break; + } + case NESTED_INVOKE: { sol_log("invoke"); diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 81613f4470..d6594d69ce 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -27,6 +27,8 @@ const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8; const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; const TEST_RETURN_ERROR: u8 = 11; +const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; +const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; // const MINT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 1; @@ -306,6 +308,18 @@ fn process_instruction( ); } + msg!("Test privilege deescalation"); + { + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], + vec![VERIFY_PRIVILEGE_DEESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + } + msg!("Verify data values are retained and updated"); { let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; @@ -492,6 +506,34 @@ fn process_instruction( ); let _ = invoke(&instruction, accounts); } + TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { + msg!("Test privilege deescalation escalation signer"); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER], + ); + invoke(&invoked_instruction, accounts)?; + } + TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { + msg!("Test privilege deescalation escalation writable"); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE], + ); + invoke(&invoked_instruction, accounts)?; + } _ => panic!(), } diff --git a/programs/bpf/rust/invoked/src/instruction.rs b/programs/bpf/rust/invoked/src/instruction.rs index 0236f08bb2..328dcd3c83 100644 --- a/programs/bpf/rust/invoked/src/instruction.rs +++ b/programs/bpf/rust/invoked/src/instruction.rs @@ -13,6 +13,9 @@ pub const VERIFY_WRITER: u8 = 4; pub const VERIFY_PRIVILEGE_ESCALATION: u8 = 5; pub const NESTED_INVOKE: u8 = 6; pub const RETURN_OK: u8 = 7; +pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8; +pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9; +pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10; pub fn create_instruction( program_id: Pubkey, diff --git a/programs/bpf/rust/invoked/src/processor.rs b/programs/bpf/rust/invoked/src/processor.rs index 77b1c9eee2..e17d4b6b34 100644 --- a/programs/bpf/rust/invoked/src/processor.rs +++ b/programs/bpf/rust/invoked/src/processor.rs @@ -161,7 +161,41 @@ fn process_instruction( assert!(!accounts[ARGUMENT_INDEX].is_writable); } VERIFY_PRIVILEGE_ESCALATION => { - msg!("Success"); + msg!("Should never get here!"); + } + VERIFY_PRIVILEGE_DEESCALATION => { + msg!("verify privilege deescalation"); + const INVOKED_ARGUMENT_INDEX: usize = 0; + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + } + VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { + msg!("verify privilege deescalation escalation signer"); + const INVOKED_PROGRAM_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, true, false)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + } + VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { + msg!("verify privilege deescalation escalation writable"); + const INVOKED_PROGRAM_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; } NESTED_INVOKE => { msg!("nested invoke"); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 3c9784c4b4..465a51d25d 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -249,7 +249,11 @@ fn process_transaction_and_record_inner( false, &mut ExecuteTimings::default(), ); - let inner_instructions = inner.swap_remove(0); + let inner_instructions = if inner.is_empty() { + Some(vec![vec![]]) + } else { + inner.swap_remove(0) + }; let result = results .fee_collection_results .swap_remove(0) @@ -676,7 +680,7 @@ fn test_program_bpf_error_handling() { } #[test] -fn test_program_bpf_invoke() { +fn test_program_bpf_invoke_sanity() { solana_logger::setup(); const TEST_SUCCESS: u8 = 1; @@ -690,6 +694,8 @@ fn test_program_bpf_invoke() { const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; const TEST_RETURN_ERROR: u8 = 11; + const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; + const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; #[allow(dead_code)] #[derive(Debug)] @@ -787,11 +793,11 @@ fn test_program_bpf_invoke() { ); let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); assert!(result.is_ok()); + let invoked_programs: Vec = inner_instructions[0] .iter() .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) .collect(); - let expected_invoked_programs = match program.0 { Languages::C => vec![ solana_sdk::system_program::id(), @@ -805,6 +811,7 @@ fn test_program_bpf_invoke() { invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), + invoked_program_id.clone(), ], Languages::Rust => vec![ solana_sdk::system_program::id(), @@ -821,11 +828,11 @@ fn test_program_bpf_invoke() { invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), + invoked_program_id.clone(), ], }; assert_eq!(invoked_programs.len(), expected_invoked_programs.len()); assert_eq!(invoked_programs, expected_invoked_programs); - let no_invoked_programs: Vec = inner_instructions[1] .iter() .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) @@ -834,281 +841,96 @@ fn test_program_bpf_invoke() { // failure cases - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_PRIVILEGE_ESCALATION_SIGNER, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), + let do_invoke_failure_test_local = + |test: u8, expected_error: TransactionError, expected_invoked_programs: &[Pubkey]| { + println!("Running failure test #{:?}", test); + let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3]; + let signers = vec![ + &mint_keypair, + &argument_keypair, + &invoked_argument_keypair, + &from_keypair, + ]; + let instruction = + Instruction::new(invoke_program_id, instruction_data, account_metas.clone()); + let message = Message::new(&[instruction], Some(&mint_pubkey)); + let tx = Transaction::new(&signers, message.clone(), bank.last_blockhash()); + let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); + let invoked_programs: Vec = inner_instructions[0] + .iter() + .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) + .collect(); + assert_eq!(result.unwrap_err(), expected_error); + assert_eq!(invoked_programs, expected_invoked_programs); + }; + + do_invoke_failure_test_local( + TEST_PRIVILEGE_ESCALATION_SIGNER, + TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), + &[invoked_program_id.clone()], ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![invoked_program_id.clone()]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation) + do_invoke_failure_test_local( + TEST_PRIVILEGE_ESCALATION_WRITABLE, + TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), + &[invoked_program_id.clone()], ); - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_PRIVILEGE_ESCALATION_WRITABLE, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![invoked_program_id.clone()]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation) + do_invoke_failure_test_local( + TEST_PPROGRAM_NOT_EXECUTABLE, + TransactionError::InstructionError(0, InstructionError::AccountNotExecutable), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_PPROGRAM_NOT_EXECUTABLE, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::AccountNotExecutable) + do_invoke_failure_test_local( + TEST_EMPTY_ACCOUNTS_SLICE, + TransactionError::InstructionError(0, InstructionError::MissingAccount), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_EMPTY_ACCOUNTS_SLICE, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::MissingAccount) + do_invoke_failure_test_local( + TEST_CAP_SEEDS, + TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[TEST_CAP_SEEDS, bump_seed1, bump_seed2, bump_seed3], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded) + do_invoke_failure_test_local( + TEST_CAP_SIGNERS, + TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[TEST_CAP_SIGNERS, bump_seed1, bump_seed2, bump_seed3], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) + do_invoke_failure_test_local( + TEST_INSTRUCTION_DATA_TOO_LARGE, + TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_INSTRUCTION_DATA_TOO_LARGE, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded) + do_invoke_failure_test_local( + TEST_INSTRUCTION_META_TOO_LARGE, + TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded), + &[], ); - let instruction = Instruction::new( - invoke_program_id, - &[ - TEST_INSTRUCTION_META_TOO_LARGE, - bump_seed1, - bump_seed2, - bump_seed3, - ], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::ComputationalBudgetExceeded) + do_invoke_failure_test_local( + TEST_RETURN_ERROR, + TransactionError::InstructionError(0, InstructionError::Custom(42)), + &[invoked_program_id.clone()], ); - let instruction = Instruction::new( - invoke_program_id, - &[TEST_RETURN_ERROR, bump_seed1, bump_seed2, bump_seed3], - account_metas.clone(), - ); - let message = Message::new(&[instruction], Some(&mint_pubkey)); - let tx = Transaction::new( - &[ - &mint_keypair, - &argument_keypair, - &invoked_argument_keypair, - &from_keypair, - ], - message.clone(), - bank.last_blockhash(), - ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); - let invoked_programs: Vec = inner_instructions[0] - .iter() - .map(|ix| message.account_keys[ix.program_id_index as usize].clone()) - .collect(); - assert_eq!(invoked_programs, vec![invoked_program_id.clone()]); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(0, InstructionError::Custom(42)) + do_invoke_failure_test_local( + TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER, + TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), + &[invoked_program_id.clone()], ); - // Check final state + do_invoke_failure_test_local( + TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE, + TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), + &[invoked_program_id.clone()], + ); + + // Check resulting state assert_eq!(43, bank.get_balance(&derived_key1)); let account = bank.get_account(&derived_key1).unwrap(); @@ -1157,94 +979,96 @@ fn test_program_bpf_invoke() { TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) ); } +} - // Check for program id spoofing - { - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin(&name, id, entrypoint); - let bank = Arc::new(bank); - let bank_client = BankClient::new_shared(&bank); +#[cfg(feature = "bpf_rust")] +#[test] +fn test_program_bpf_program_id_spoofing() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + let mut bank = Bank::new(&genesis_config); + let (name, id, entrypoint) = solana_bpf_loader_program!(); + bank.add_builtin(&name, id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); - let malicious_swap_pubkey = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_spoof1", - ); - let malicious_system_pubkey = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_spoof1_system", - ); + let malicious_swap_pubkey = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_spoof1", + ); + let malicious_system_pubkey = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_spoof1_system", + ); - let from_pubkey = Pubkey::new_unique(); - let account = Account::new(10, 0, &solana_sdk::system_program::id()); - bank.store_account(&from_pubkey, &account); + let from_pubkey = Pubkey::new_unique(); + let account = Account::new(10, 0, &solana_sdk::system_program::id()); + bank.store_account(&from_pubkey, &account); - let to_pubkey = Pubkey::new_unique(); - let account = Account::new(0, 0, &solana_sdk::system_program::id()); - bank.store_account(&to_pubkey, &account); + let to_pubkey = Pubkey::new_unique(); + let account = Account::new(0, 0, &solana_sdk::system_program::id()); + bank.store_account(&to_pubkey, &account); - let account_metas = vec![ - AccountMeta::new_readonly(solana_sdk::system_program::id(), false), - AccountMeta::new_readonly(malicious_system_pubkey, false), - AccountMeta::new(from_pubkey, false), - AccountMeta::new(to_pubkey, false), - ]; + let account_metas = vec![ + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(malicious_system_pubkey, false), + AccountMeta::new(from_pubkey, false), + AccountMeta::new(to_pubkey, false), + ]; - let instruction = Instruction::new(malicious_swap_pubkey, &(), account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) - ); - assert_eq!(10, bank.get_balance(&from_pubkey)); - assert_eq!(0, bank.get_balance(&to_pubkey)); - } + let instruction = Instruction::new(malicious_swap_pubkey, &(), account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) + ); + assert_eq!(10, bank.get_balance(&from_pubkey)); + assert_eq!(0, bank.get_balance(&to_pubkey)); +} - // Check the caller has access to cpi program - { - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin(&name, id, entrypoint); - let bank = Arc::new(bank); - let bank_client = BankClient::new_shared(&bank); +#[cfg(feature = "bpf_rust")] +#[test] +fn test_program_bpf_caller_has_access_to_cpi_program() { + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + let mut bank = Bank::new(&genesis_config); + let (name, id, entrypoint) = solana_bpf_loader_program!(); + bank.add_builtin(&name, id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); - let caller_pubkey = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_caller_access", - ); - let caller2_pubkey = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_caller_access", - ); - let account_metas = vec![ - AccountMeta::new_readonly(caller_pubkey, false), - AccountMeta::new_readonly(caller2_pubkey, false), - ]; - let instruction = Instruction::new(caller_pubkey, &[1_u8], account_metas.clone()); - let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); - assert_eq!( - result.unwrap_err().unwrap(), - TransactionError::InstructionError(0, InstructionError::MissingAccount) - ); - } + let caller_pubkey = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_caller_access", + ); + let caller2_pubkey = load_bpf_program( + &bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_bpf_rust_caller_access", + ); + let account_metas = vec![ + AccountMeta::new_readonly(caller_pubkey, false), + AccountMeta::new_readonly(caller2_pubkey, false), + ]; + let instruction = Instruction::new(caller_pubkey, &[1_u8], account_metas.clone()); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::MissingAccount) + ); } #[cfg(feature = "bpf_rust")]