diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 4b0cbc802f..1e518160e6 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -10,6 +10,7 @@ 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 int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; @@ -285,30 +286,66 @@ extern uint64_t entrypoint(const uint8_t *input) { } case TEST_CAP_SEEDS: { sol_log("Test cap seeds"); - { - SolAccountMeta arguments[] = {}; - uint8_t data[] = {}; - const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, SOL_ARRAY_SIZE(arguments), - data, SOL_ARRAY_SIZE(data)}; - uint8_t seed[] = {"seed"}; - const SolSignerSeed seeds[] = { - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, - {seed, SOL_ARRAY_SIZE(seed)}, - }; - const SolSignerSeeds signers_seeds[] = {{seeds, SOL_ARRAY_SIZE(seeds)}}; - sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts, - SOL_ARRAY_SIZE(accounts), - signers_seeds, - SOL_ARRAY_SIZE(signers_seeds))); - } + SolAccountMeta arguments[] = {}; + uint8_t data[] = {}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + uint8_t seed[] = {"seed"}; + const SolSignerSeed seeds[] = { + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, {seed, SOL_ARRAY_SIZE(seed)}, + {seed, SOL_ARRAY_SIZE(seed)}, + }; + const SolSignerSeeds signers_seeds[] = {{seeds, SOL_ARRAY_SIZE(seeds)}}; + sol_assert(SUCCESS == sol_invoke_signed( + &instruction, accounts, SOL_ARRAY_SIZE(accounts), + signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); + } + case TEST_CAP_SIGNERS: { + sol_log("Test cap signers"); + SolAccountMeta arguments[] = {}; + uint8_t data[] = {}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + uint8_t seed[] = {"seed"}; + const SolSignerSeed seed1[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed2[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed3[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed4[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed5[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed6[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed7[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed8[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed9[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed10[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed11[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed12[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed13[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed14[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed15[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed16[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeed seed17[] = {{seed, SOL_ARRAY_SIZE(seed)}}; + const SolSignerSeeds signers_seeds[] = { + {seed1, SOL_ARRAY_SIZE(seed1)}, {seed2, SOL_ARRAY_SIZE(seed2)}, + {seed3, SOL_ARRAY_SIZE(seed3)}, {seed4, SOL_ARRAY_SIZE(seed4)}, + {seed5, SOL_ARRAY_SIZE(seed5)}, {seed6, SOL_ARRAY_SIZE(seed6)}, + {seed7, SOL_ARRAY_SIZE(seed7)}, {seed8, SOL_ARRAY_SIZE(seed8)}, + {seed9, SOL_ARRAY_SIZE(seed9)}, {seed10, SOL_ARRAY_SIZE(seed10)}, + {seed11, SOL_ARRAY_SIZE(seed11)}, {seed12, SOL_ARRAY_SIZE(seed12)}, + {seed13, SOL_ARRAY_SIZE(seed13)}, {seed14, SOL_ARRAY_SIZE(seed14)}, + {seed15, SOL_ARRAY_SIZE(seed15)}, {seed16, SOL_ARRAY_SIZE(seed16)}, + {seed17, SOL_ARRAY_SIZE(seed17)}}; + sol_assert(SUCCESS == sol_invoke_signed( + &instruction, accounts, SOL_ARRAY_SIZE(accounts), + signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); } default: sol_panic(); diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index a59c07abb7..bd97b4aa5f 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -22,6 +22,7 @@ const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; const TEST_CAP_SEEDS: u8 = 6; +const TEST_CAP_SIGNERS: u8 = 7; // const MINT_INDEX: usize = 0; const ARGUMENT_INDEX: usize = 1; @@ -375,6 +376,33 @@ fn process_instruction( ]], )?; } + TEST_CAP_SIGNERS => { + msg!("Test program max signers"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + invoke_signed( + &instruction, + accounts, + &[ + &[b"1"], + &[b"2"], + &[b"3"], + &[b"4"], + &[b"5"], + &[b"6"], + &[b"7"], + &[b"8"], + &[b"9"], + &[b"0"], + &[b"1"], + &[b"2"], + &[b"3"], + &[b"4"], + &[b"5"], + &[b"6"], + &[b"7"], + ], + )?; + } _ => panic!(), } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 5b1e4bf19f..f72b93b745 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -22,12 +22,10 @@ use solana_sdk::{ entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, keyed_account::KeyedAccount, - loader_instruction, message::Message, - process_instruction::{BpfComputeBudget, MockInvokeContext}, + process_instruction::MockInvokeContext, pubkey::Pubkey, signature::{Keypair, Signer}, - system_instruction, sysvar::{clock, fees, rent, slot_hashes, stake_history}, transaction::{Transaction, TransactionError}, }; @@ -69,6 +67,7 @@ fn read_bpf_program(name: &str) -> Vec { elf } +#[cfg(feature = "bpf_rust")] fn write_bpf_program( bank_client: &BankClient, loader_id: &Pubkey, @@ -76,6 +75,8 @@ fn write_bpf_program( program_keypair: &Keypair, elf: &[u8], ) { + use solana_sdk::loader_instruction; + let chunk_size = 256; // Size of chunk just needs to fit into tx let mut offset = 0; for chunk in elf.chunks(chunk_size) { @@ -477,6 +478,7 @@ fn test_program_bpf_invoke() { const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; const TEST_CAP_SEEDS: u8 = 6; + const TEST_CAP_SIGNERS: u8 = 7; #[allow(dead_code)] #[derive(Debug)] @@ -778,6 +780,33 @@ fn test_program_bpf_invoke() { TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded) ); + 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::Custom(194969602)) + ); + // Check final state assert_eq!(43, bank.get_balance(&derived_key1)); @@ -890,6 +919,8 @@ fn test_program_bpf_ro_modify() { #[cfg(feature = "bpf_rust")] #[test] fn test_program_bpf_call_depth() { + use solana_sdk::process_instruction::BpfComputeBudget; + solana_logger::setup(); println!("Test program: solana_bpf_rust_call_depth"); @@ -1043,6 +1074,8 @@ fn test_program_bpf_instruction_introspection() { #[cfg(feature = "bpf_rust")] #[test] fn test_program_bpf_test_use_latest_executor() { + use solana_sdk::{loader_instruction, system_instruction}; + solana_logger::setup(); let GenesisConfigInfo { @@ -1133,6 +1166,8 @@ fn test_program_bpf_test_use_latest_executor() { #[cfg(feature = "bpf_rust")] #[test] fn test_program_bpf_test_use_latest_executor2() { + use solana_sdk::{loader_instruction, system_instruction}; + solana_logger::setup(); let GenesisConfigInfo { diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index f8f7dcd22c..a286cadccd 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -36,6 +36,9 @@ use std::{ }; use thiserror::Error as ThisError; +/// Maximum signers +pub const MAX_SIGNERS: usize = 16; + /// Error definitions #[derive(Debug, ThisError, PartialEq)] pub enum SyscallError { @@ -59,6 +62,8 @@ pub enum SyscallError { PrivilegeEscalation, #[error("Unaligned pointer")] UnalignedPointer, + #[error("Too many signers")] + TooManySigners, } impl From for EbpfError { fn from(error: SyscallError) -> Self { @@ -829,6 +834,9 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> { ro_regions, self.loader_id )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } for signer_seeds in signers_seeds.iter() { let untranslated_seeds = translate_slice!( &[u8], @@ -1088,6 +1096,9 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> { ro_regions, self.loader_id )?; + if signers_seeds.len() > MAX_SIGNERS { + return Err(SyscallError::TooManySigners.into()); + } Ok(signers_seeds .iter() .map(|signer_seeds| {