diff --git a/Cargo.lock b/Cargo.lock index 2e38b21024d79c..be772cd601463e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4733,6 +4733,7 @@ dependencies = [ "solana-sdk 1.11.4", "solana-zk-token-sdk 1.11.4", "solana_rbpf", + "static_assertions", "thiserror", ] diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 3ed95533d460d3..a527eaad210a4c 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -18,8 +18,8 @@ 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_INSTRUCTION_DATA_TOO_LARGE = 9; -static const uint8_t TEST_INSTRUCTION_META_TOO_LARGE = 10; +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; @@ -31,6 +31,7 @@ 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; static const int MINT_INDEX = 0; static const int ARGUMENT_INDEX = 1; @@ -76,7 +77,7 @@ uint64_t do_nested_invokes(uint64_t num_nested_invokes, } extern uint64_t entrypoint(const uint8_t *input) { - sol_log("Invoke C program"); + sol_log("invoke C program"); SolAccountInfo accounts[13]; SolParameters params = (SolParameters){.ka = accounts}; @@ -480,13 +481,14 @@ extern uint64_t entrypoint(const uint8_t *input) { signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); break; } - case TEST_INSTRUCTION_DATA_TOO_LARGE: { - sol_log("Test instruction data too large"); + case TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED: { + sol_log("Test max instruction data len exceeded"); SolAccountMeta arguments[] = {}; - uint8_t *data = sol_calloc(1500, 1); + uint64_t data_len = 10 * 1024 + 1; + uint8_t *data = sol_calloc(data_len, 1); const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), - data, 1500}; + data, data_len}; const SolSignerSeeds signers_seeds[] = {}; sol_assert(SUCCESS == sol_invoke_signed( &instruction, accounts, SOL_ARRAY_SIZE(accounts), @@ -494,14 +496,14 @@ extern uint64_t entrypoint(const uint8_t *input) { break; } - case TEST_INSTRUCTION_META_TOO_LARGE: { - sol_log("Test instruction meta too large"); - SolAccountMeta *arguments = sol_calloc(40, sizeof(SolAccountMeta)); - sol_log_64(0, 0, 0, 0, (uint64_t)arguments); + case TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED: { + sol_log("Test max instruction accounts exceeded"); + uint64_t accounts_len = 256; + SolAccountMeta *arguments = sol_calloc(accounts_len, sizeof(SolAccountMeta)); sol_assert(0 != arguments); uint8_t data[] = {}; const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, - arguments, 40, data, + arguments, accounts_len, data, SOL_ARRAY_SIZE(data)}; const SolSignerSeeds signers_seeds[] = {}; sol_assert(SUCCESS == sol_invoke_signed( @@ -510,6 +512,23 @@ extern uint64_t entrypoint(const uint8_t *input) { break; } + case TEST_MAX_ACCOUNT_INFOS_EXCEEDED: { + sol_log("Test max account infos exceeded"); + SolAccountMeta arguments[] = {}; + uint64_t account_infos_len = 65; + SolAccountInfo *account_infos = sol_calloc(account_infos_len, sizeof(SolAccountInfo)); + sol_assert(0 != account_infos); + uint8_t data[] = {}; + const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + const SolSignerSeeds signers_seeds[] = {}; + sol_assert(SUCCESS == sol_invoke_signed( + &instruction, account_infos, account_infos_len, + signers_seeds, SOL_ARRAY_SIZE(signers_seeds))); + + break; + } case TEST_RETURN_ERROR: { sol_log("Test return error"); SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, false, true}}; diff --git a/programs/bpf/rust/invoke/src/instructions.rs b/programs/bpf/rust/invoke/src/instructions.rs index 97c1b75317e697..08a1fa3216413a 100644 --- a/programs/bpf/rust/invoke/src/instructions.rs +++ b/programs/bpf/rust/invoke/src/instructions.rs @@ -8,8 +8,8 @@ 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_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; -pub const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; +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; @@ -21,6 +21,7 @@ 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 MINT_INDEX: usize = 0; pub const ARGUMENT_INDEX: usize = 1; diff --git a/programs/bpf/rust/invoke/src/processor.rs b/programs/bpf/rust/invoke/src/processor.rs index 6a6b1c1f0d2bbc..c38661f8f25d9c 100644 --- a/programs/bpf/rust/invoke/src/processor.rs +++ b/programs/bpf/rust/invoke/src/processor.rs @@ -554,21 +554,27 @@ fn process_instruction( &[&[b"You pass butter", &[bump_seed1]]], )?; } - TEST_INSTRUCTION_DATA_TOO_LARGE => { - msg!("Test instruction data too large"); + TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED => { + msg!("Test max instruction data len exceeded"); + let data_len = 10usize.saturating_mul(1024).saturating_add(1); let instruction = - create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; 1500]); + create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; data_len]); invoke_signed(&instruction, &[], &[])?; } - TEST_INSTRUCTION_META_TOO_LARGE => { - msg!("Test instruction metas too large"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(&Pubkey::default(), false, false); 40], - vec![], - ); + TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED => { + msg!("Test max instruction accounts exceeded"); + let default_key = Pubkey::default(); + let account_metas = vec![(&default_key, false, false); 256]; + let instruction = + create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &account_metas, vec![]); invoke_signed(&instruction, &[], &[])?; } + TEST_MAX_ACCOUNT_INFOS_EXCEEDED => { + msg!("Test max account infos exceeded"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + let account_infos = vec![accounts[0].clone(); 65]; + invoke_signed(&instruction, &account_infos, &[])?; + } TEST_RETURN_ERROR => { msg!("Test return error"); let instruction = create_instruction( diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index aba2e85aed5692..3814d0450ff3cd 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -346,7 +346,11 @@ fn run_program(name: &str) -> u64 { fn process_transaction_and_record_inner( bank: &Bank, tx: Transaction, -) -> (Result<(), TransactionError>, Vec>) { +) -> ( + Result<(), TransactionError>, + Vec>, + Vec, +) { let signature = tx.signatures.get(0).unwrap().clone(); let txs = vec![tx]; let tx_batch = bank.prepare_batch_for_tests(txs); @@ -356,7 +360,7 @@ fn process_transaction_and_record_inner( MAX_PROCESSING_AGE, false, true, - false, + true, false, &mut ExecuteTimings::default(), None, @@ -366,15 +370,19 @@ fn process_transaction_and_record_inner( .fee_collection_results .swap_remove(0) .and_then(|_| bank.get_signature_status(&signature).unwrap()); - let inner_instructions = results + let execution_details = results .execution_results .swap_remove(0) .details() .expect("tx should be executed") - .clone() + .clone(); + let inner_instructions = execution_details .inner_instructions .expect("cpi recording should be enabled"); - (result, inner_instructions) + let log_messages = execution_details + .log_messages + .expect("log recording should be enabled"); + (result, inner_instructions, log_messages) } fn execute_transactions( @@ -1063,7 +1071,8 @@ fn test_program_bpf_invoke_sanity() { message.clone(), bank.last_blockhash(), ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); + let (result, inner_instructions, _log_messages) = + process_transaction_and_record_inner(&bank, tx); assert_eq!(result, Ok(())); let invoked_programs: Vec = inner_instructions[0] @@ -1129,7 +1138,10 @@ fn test_program_bpf_invoke_sanity() { // failure cases let do_invoke_failure_test_local = - |test: u8, expected_error: TransactionError, expected_invoked_programs: &[Pubkey]| { + |test: u8, + expected_error: TransactionError, + expected_invoked_programs: &[Pubkey], + expected_log_messages: Option>| { println!("Running failure test #{:?}", test); let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3]; let signers = vec![ @@ -1145,85 +1157,141 @@ fn test_program_bpf_invoke_sanity() { ); 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 (result, inner_instructions, log_messages) = + 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, Err(expected_error)); assert_eq!(invoked_programs, expected_invoked_programs); + if let Some(expected_log_messages) = expected_log_messages { + expected_log_messages + .into_iter() + .zip(log_messages) + .for_each(|(expected_log_message, log_message)| { + if expected_log_message != String::from("skip") { + assert_eq!(log_message, expected_log_message); + } + }); + } }; + let program_lang = match program.0 { + Languages::Rust => "Rust", + Languages::C => "C", + }; + do_invoke_failure_test_local( TEST_PRIVILEGE_ESCALATION_SIGNER, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_PRIVILEGE_ESCALATION_WRITABLE, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_PPROGRAM_NOT_EXECUTABLE, TransactionError::InstructionError(0, InstructionError::AccountNotExecutable), &[], + None, ); do_invoke_failure_test_local( TEST_EMPTY_ACCOUNTS_SLICE, TransactionError::InstructionError(0, InstructionError::MissingAccount), &[], + None, ); do_invoke_failure_test_local( TEST_CAP_SEEDS, TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded), &[], + None, ); do_invoke_failure_test_local( TEST_CAP_SIGNERS, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], + None, ); do_invoke_failure_test_local( - TEST_INSTRUCTION_DATA_TOO_LARGE, + TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], + Some(vec![ + format!("Program {invoke_program_id} invoke [1]"), + format!("Program log: invoke {program_lang} program"), + "Program log: Test max instruction data len exceeded".into(), + "skip".into(), // don't compare compute consumption logs + "Program failed to complete: Invoked an instruction with data that is too large (10241 > 10240)".into(), + format!("Program {invoke_program_id} failed: Program failed to complete"), + ]), ); do_invoke_failure_test_local( - TEST_INSTRUCTION_META_TOO_LARGE, + TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], + Some(vec![ + format!("Program {invoke_program_id} invoke [1]"), + format!("Program log: invoke {program_lang} program"), + "Program log: Test max instruction accounts exceeded".into(), + "skip".into(), // don't compare compute consumption logs + "Program failed to complete: Invoked an instruction with too many accounts (256 > 255)".into(), + format!("Program {invoke_program_id} failed: Program failed to complete"), + ]), + ); + + do_invoke_failure_test_local( + TEST_MAX_ACCOUNT_INFOS_EXCEEDED, + TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), + &[], + Some(vec![ + format!("Program {invoke_program_id} invoke [1]"), + format!("Program log: invoke {program_lang} program"), + "Program log: Test max account infos exceeded".into(), + "skip".into(), // don't compare compute consumption logs + "Program failed to complete: Invoked an instruction with too many account info's (65 > 64)".into(), + format!("Program {invoke_program_id} failed: Program failed to complete"), + ]), ); do_invoke_failure_test_local( TEST_RETURN_ERROR, TransactionError::InstructionError(0, InstructionError::Custom(42)), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_WRITABLE_DEESCALATION_WRITABLE, TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( @@ -1236,36 +1304,42 @@ fn test_program_bpf_invoke_sanity() { invoked_program_id.clone(), invoked_program_id.clone(), ], + None, ); do_invoke_failure_test_local( TEST_EXECUTABLE_LAMPORTS, TransactionError::InstructionError(0, InstructionError::ExecutableLamportChange), &[invoke_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_CALL_PRECOMPILE, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], + None, ); do_invoke_failure_test_local( TEST_RETURN_DATA_TOO_LARGE, TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), &[], + None, ); do_invoke_failure_test_local( TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); do_invoke_failure_test_local( TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE, TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation), &[invoked_program_id.clone()], + None, ); // Check resulting state @@ -1306,7 +1380,8 @@ fn test_program_bpf_invoke_sanity() { message.clone(), bank.last_blockhash(), ); - let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx); + let (result, inner_instructions, _log_messages) = + 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()) @@ -2041,7 +2116,7 @@ fn test_program_bpf_upgrade_and_invoke_in_same_tx() { message.clone(), bank.last_blockhash(), ); - let (result, _) = process_transaction_and_record_inner(&bank, tx); + let (result, _, _) = process_transaction_and_record_inner(&bank, tx); assert_eq!( result.unwrap_err(), TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete) @@ -2414,7 +2489,7 @@ fn test_program_bpf_upgrade_self_via_cpi() { message.clone(), bank.last_blockhash(), ); - let (result, _) = process_transaction_and_record_inner(&bank, tx); + let (result, _, _) = process_transaction_and_record_inner(&bank, tx); assert_eq!( result.unwrap_err(), TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete) diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index ffa96accfa68d3..e61ed2fb563168 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -25,6 +25,7 @@ thiserror = "1.0" [dev-dependencies] rand = "0.7.3" solana-runtime = { path = "../../runtime", version = "=1.11.4" } +static_assertions = "1.1.0" [lib] crate-type = ["lib"] diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs index 676f6c12d5e0a4..6797eaf2c72221 100644 --- a/programs/bpf_loader/src/syscalls/cpi.rs +++ b/programs/bpf_loader/src/syscalls/cpi.rs @@ -733,41 +733,104 @@ where Ok(accounts) } +#[cfg(test)] +static_assertions::const_assert_eq!(ACCOUNT_META_SIZE, 34); +const ACCOUNT_META_SIZE: usize = size_of::(); + +/// Maximum CPI instruction data size. 10 KiB was chosen to ensure that CPI +/// instructions are not more limited than transaction instructions if the size +/// of transactions is doubled in the future. +const MAX_CPI_INSTRUCTION_DATA_LEN: u64 = 10 * 1024; + +/// Maximum CPI instruction accounts. 255 was chosen to ensure that instruction +/// accounts are always within the maximum instruction account limit for BPF +/// program instructions. +const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX; + fn check_instruction_size( num_accounts: usize, data_len: usize, invoke_context: &mut InvokeContext, ) -> Result<(), EbpfError> { - let size = num_accounts - .saturating_mul(size_of::()) - .saturating_add(data_len); - let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; - if size > max_size { - return Err(SyscallError::InstructionTooLarge(size, max_size).into()); + if invoke_context + .feature_set + .is_active(&feature_set::loosen_cpi_size_restriction::ID) + { + let data_len = u64::try_from(data_len).unwrap(); + let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN; + if data_len > max_data_len { + return Err(SyscallError::MaxInstructionDataLenExceeded { + data_len, + max_data_len, + } + .into()); + } + + let num_accounts = u64::try_from(num_accounts).unwrap(); + let max_accounts = u64::from(MAX_CPI_INSTRUCTION_ACCOUNTS); + if num_accounts > max_accounts { + return Err(SyscallError::MaxInstructionAccountsExceeded { + num_accounts, + max_accounts, + } + .into()); + } + + invoke_context.get_compute_meter().consume( + (data_len).saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), + )?; + } else { + let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; + let size = num_accounts + .saturating_mul(ACCOUNT_META_SIZE) + .saturating_add(data_len); + if size > max_size { + return Err(SyscallError::InstructionTooLarge(size, max_size).into()); + } } Ok(()) } +/// Maximum number of account info structs that can be used in a single CPI +/// invocation. A limit on account info structs is effectively the same as +/// limiting the number of unique accounts. +const MAX_CPI_ACCOUNT_INFOS: usize = MAX_TX_ACCOUNT_LOCKS; + fn check_account_infos( - len: usize, + num_account_infos: usize, invoke_context: &mut InvokeContext, ) -> Result<(), EbpfError> { - let adjusted_len = if invoke_context + if invoke_context .feature_set - .is_active(&syscall_saturated_math::id()) + .is_active(&feature_set::loosen_cpi_size_restriction::ID) { - len.saturating_mul(size_of::()) + let num_account_infos = u64::try_from(num_account_infos).unwrap(); + let max_account_infos = u64::try_from(MAX_CPI_ACCOUNT_INFOS).unwrap(); + if num_account_infos > max_account_infos { + return Err(SyscallError::MaxInstructionAccountInfosExceeded { + num_account_infos, + max_account_infos, + } + .into()); + } } else { - #[allow(clippy::integer_arithmetic)] + let adjusted_len = if invoke_context + .feature_set + .is_active(&syscall_saturated_math::id()) { - len * size_of::() - } - }; - if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size { - // Cap the number of account_infos a caller can pass to approximate - // maximum that accounts that could be passed in an instruction - return Err(SyscallError::TooManyAccounts.into()); - }; + num_account_infos.saturating_mul(size_of::()) + } else { + #[allow(clippy::integer_arithmetic)] + { + num_account_infos * size_of::() + } + }; + if adjusted_len > invoke_context.get_compute_budget().max_cpi_instruction_size { + // Cap the number of account_infos a caller can pass to approximate + // maximum that accounts that could be passed in an instruction + return Err(SyscallError::TooManyAccounts.into()); + }; + } Ok(()) } diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index d96345c98deb5f..1af046cbe34558 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -33,7 +33,7 @@ use { blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ - blake3_syscall_enabled, check_physical_overlapping, check_slice_translation_size, + self, blake3_syscall_enabled, check_physical_overlapping, check_slice_translation_size, curve25519_syscall_enabled, disable_fees_sysvar, enable_early_verification_of_account_modifications, libsecp256k1_0_5_upgrade_enabled, limit_secp256k1_recovery_id, prevent_calling_precompiles_as_programs, @@ -53,6 +53,7 @@ use { Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH, }, sysvar::{Sysvar, SysvarId}, + transaction::MAX_TX_ACCOUNT_LOCKS, transaction_context::InstructionAccount, }, std::{ @@ -110,6 +111,18 @@ pub enum SyscallError { TooManySlices, #[error("InvalidLength")] InvalidLength, + #[error("Invoked an instruction with data that is too large ({data_len} > {max_data_len})")] + MaxInstructionDataLenExceeded { data_len: u64, max_data_len: u64 }, + #[error("Invoked an instruction with too many accounts ({num_accounts} > {max_accounts})")] + MaxInstructionAccountsExceeded { + num_accounts: u64, + max_accounts: u64, + }, + #[error("Invoked an instruction with too many account info's ({num_account_infos} > {max_account_infos})")] + MaxInstructionAccountInfosExceeded { + num_account_infos: u64, + max_account_infos: u64, + }, } impl From for EbpfError { fn from(error: SyscallError) -> Self { diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 96a90e83f1263c..21ddcbd85d5cbf 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -464,6 +464,10 @@ pub mod prevent_crediting_accounts_that_end_rent_paying { solana_sdk::declare_id!("812kqX67odAp5NFwM8D2N24cku7WTm9CHUTFUXaDkWPn"); } +pub mod loosen_cpi_size_restriction { + solana_sdk::declare_id!("GDH5TVdbTPUpRnXaRyQqiKUa7uZAbZ28Q2N9bhbKoMLm"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -574,6 +578,7 @@ lazy_static! { (enable_bpf_loader_extend_program_data_ix::id(), "enable bpf upgradeable loader ExtendProgramData instruction #25234"), (enable_early_verification_of_account_modifications::id(), "enable early verification of account modifications #25899"), (prevent_crediting_accounts_that_end_rent_paying::id(), "prevent crediting rent paying accounts #26606"), + (loosen_cpi_size_restriction::id(), "loosen cpi size restrictions #26641"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()