diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 82e168242e..d61b1d8edf 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -8,6 +8,7 @@ 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 int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; @@ -270,6 +271,17 @@ extern uint64_t entrypoint(const uint8_t *input) { SOL_ARRAY_SIZE(data)}; return sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)); } + case TEST_EMPTY_ACCOUNTS_SLICE: { + sol_log("Empty accounts slice"); + + SolAccountMeta arguments[] = {}; + uint8_t data[] = {}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + + sol_assert(SUCCESS == sol_invoke(&instruction, 0, 0)); + } default: sol_panic(); } diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 5488c5cb94..d0d7993ede 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -20,6 +20,7 @@ const TEST_SUCCESS: u8 = 1; const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; +const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; // const MINT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 1; @@ -366,6 +367,11 @@ fn process_instruction( ); invoke(&instruction, accounts)?; } + TEST_EMPTY_ACCOUNTS_SLICE => { + msg!("Empty accounts slice"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + invoke(&instruction, &[])?; + } _ => panic!(), } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 4e95d92f83..a9959b328c 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -490,6 +490,7 @@ fn test_program_bpf_invoke() { const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; + const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; #[allow(dead_code)] #[derive(Debug)] @@ -732,6 +733,38 @@ fn test_program_bpf_invoke() { TransactionError::InstructionError(0, InstructionError::AccountNotExecutable) ); + 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) + ); + // Check final state assert_eq!(43, bank.get_balance(&derived_key1)); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 21a9772b4d..181668235c 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -1092,7 +1092,6 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { account_infos_len, self.loader_id, )?; - let first_info_addr = &account_infos[0] as *const _ as u64; let mut accounts = Vec::with_capacity(message.account_keys.len()); let mut refs = Vec::with_capacity(message.account_keys.len()); 'root: for account_key in message.account_keys.iter() { @@ -1120,6 +1119,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { self.loader_id, )?; + let first_info_addr = &account_infos[0] as *const _ as u64; let addr = &account_info.data_len as *const u64 as u64; let vm_addr = account_infos_addr + (addr - first_info_addr); let _ = translate(