Loosen CPI restrictions and charge compute for ix data len (#26653)

* Loosen CPI restrictions and charge compute for ix data len

* Address feedback

* use explicit casting

* more feedback
This commit is contained in:
Justin Starry 2022-07-24 20:20:16 +02:00 committed by GitHub
parent d9c7bc7e78
commit 2335f6908a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 297 additions and 62 deletions

View File

@ -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 = MAX_CPI_INSTRUCTION_DATA_LEN + 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 = MAX_CPI_INSTRUCTION_ACCOUNTS + 1;
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 = MAX_CPI_ACCOUNT_INFOS + 1;
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}};

View File

@ -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;

View File

@ -14,6 +14,9 @@ use {
program::{get_return_data, invoke, invoke_signed, set_return_data},
program_error::ProgramError,
pubkey::{Pubkey, PubkeyError},
syscalls::{
MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
},
system_instruction,
},
};
@ -554,21 +557,29 @@ 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 = MAX_CPI_INSTRUCTION_DATA_LEN.saturating_add(1) as usize;
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_len = (MAX_CPI_INSTRUCTION_ACCOUNTS as usize).saturating_add(1);
let account_metas = vec![(&default_key, false, false); account_metas_len];
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_len = (MAX_CPI_ACCOUNT_INFOS as usize).saturating_add(1);
let account_infos = vec![accounts[0].clone(); account_infos_len];
invoke_signed(&instruction, &account_infos, &[])?;
}
TEST_RETURN_ERROR => {
msg!("Test return error");
let instruction = create_instruction(

View File

@ -347,7 +347,11 @@ fn run_program(name: &str) -> u64 {
fn process_transaction_and_record_inner(
bank: &Bank,
tx: Transaction,
) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
) -> (
Result<(), TransactionError>,
Vec<Vec<CompiledInstruction>>,
Vec<String>,
) {
let signature = tx.signatures.get(0).unwrap().clone();
let txs = vec![tx];
let tx_batch = bank.prepare_batch_for_tests(txs);
@ -357,7 +361,7 @@ fn process_transaction_and_record_inner(
MAX_PROCESSING_AGE,
false,
true,
false,
true,
false,
&mut ExecuteTimings::default(),
None,
@ -367,15 +371,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(
@ -1064,7 +1072,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<Pubkey> = inner_instructions[0]
@ -1130,7 +1139,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<Vec<String>>| {
println!("Running failure test #{:?}", test);
let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3];
let signers = vec![
@ -1146,85 +1158,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<Pubkey> = 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(
@ -1237,36 +1305,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
@ -1307,7 +1381,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<Pubkey> = inner_instructions[0]
.iter()
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
@ -2042,7 +2117,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)
@ -2415,7 +2490,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)

View File

@ -1,4 +1,10 @@
use {super::*, crate::declare_syscall};
use {
super::*,
crate::declare_syscall,
solana_sdk::syscalls::{
MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
},
};
struct CallerAccount<'a> {
lamports: &'a mut u64,
@ -94,10 +100,22 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> {
invoke_context.get_check_size(),
)?
.to_vec();
let ix_data_len = ix.data.len() as u64;
if invoke_context
.feature_set
.is_active(&feature_set::loosen_cpi_size_restriction::id())
{
invoke_context.get_compute_meter().consume(
(ix_data_len)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
}
let data = translate_slice::<u8>(
memory_mapping,
ix.data.as_ptr() as u64,
ix.data.len() as u64,
ix_data_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?
@ -383,10 +401,22 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> {
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let ix_data_len = ix_c.data_len as u64;
if invoke_context
.feature_set
.is_active(&feature_set::loosen_cpi_size_restriction::id())
{
invoke_context.get_compute_meter().consume(
(ix_data_len)
.saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit),
)?;
}
let data = translate_slice::<u8>(
memory_mapping,
ix_c.data_addr,
ix_c.data_len as u64,
ix_data_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?
@ -719,36 +749,76 @@ fn check_instruction_size(
data_len: usize,
invoke_context: &mut InvokeContext,
) -> Result<(), EbpfError<BpfError>> {
let size = num_accounts
.saturating_mul(size_of::<AccountMeta>())
.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 = data_len as u64;
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 = num_accounts as u64;
let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64;
if num_accounts > max_accounts {
return Err(SyscallError::MaxInstructionAccountsExceeded {
num_accounts,
max_accounts,
}
.into());
}
} else {
let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size;
let size = num_accounts
.saturating_mul(size_of::<AccountMeta>())
.saturating_add(data_len);
if size > max_size {
return Err(SyscallError::InstructionTooLarge(size, max_size).into());
}
}
Ok(())
}
fn check_account_infos(
len: usize,
num_account_infos: usize,
invoke_context: &mut InvokeContext,
) -> Result<(), EbpfError<BpfError>> {
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::<Pubkey>())
} else {
#[allow(clippy::integer_arithmetic)]
{
len * size_of::<Pubkey>()
let num_account_infos = num_account_infos as u64;
let max_account_infos = MAX_CPI_ACCOUNT_INFOS as u64;
if num_account_infos > max_account_infos {
return Err(SyscallError::MaxInstructionAccountInfosExceeded {
num_account_infos,
max_account_infos,
}
.into());
}
};
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());
};
} else {
let adjusted_len = if invoke_context
.feature_set
.is_active(&syscall_saturated_math::id())
{
num_account_infos.saturating_mul(size_of::<Pubkey>())
} else {
#[allow(clippy::integer_arithmetic)]
{
num_account_infos * size_of::<Pubkey>()
}
};
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(())
}

View File

@ -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,
@ -110,6 +110,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<SyscallError> for EbpfError<BpfError> {
fn from(error: SyscallError) -> Self {

View File

@ -11,6 +11,28 @@
extern "C" {
#endif
/**
* 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.
*/
static const uint64_t MAX_CPI_INSTRUCTION_DATA_LEN = 10240;
/**
* Maximum CPI instruction accounts. 255 was chosen to ensure that instruction
* accounts are always within the maximum instruction account limit for BPF
* program instructions.
*/
static const uint8_t MAX_CPI_INSTRUCTION_ACCOUNTS = 255;
/**
* 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. 64 was chosen to match the max
* number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS).
*/
static const uint8_t MAX_CPI_ACCOUNT_INFOS = 64;
/**
* Account Meta
*/

View File

@ -603,7 +603,6 @@ pub mod slot_hashes;
pub mod slot_history;
pub mod stake;
pub mod stake_history;
#[cfg(target_os = "solana")]
pub mod syscalls;
pub mod system_instruction;
pub mod system_program;

View File

@ -0,0 +1,21 @@
#[cfg(target_os = "solana")]
mod definitions;
#[cfg(target_os = "solana")]
pub use definitions::*;
/// 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.
pub 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.
pub const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX;
/// 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. 64 was chosen to match the max
/// number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS).
pub const MAX_CPI_ACCOUNT_INFOS: usize = 64;

View File

@ -468,6 +468,10 @@ pub mod cap_bpf_program_instruction_accounts {
solana_sdk::declare_id!("9k5ijzTbYPtjzu8wj2ErH9v45xecHzQ1x4PMYMMxFgdM");
}
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<Pubkey, &'static str> = [
@ -579,6 +583,7 @@ lazy_static! {
(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"),
(cap_bpf_program_instruction_accounts::id(), "enforce max number of accounts per bpf program instruction #26628"),
(loosen_cpi_size_restriction::id(), "loosen cpi size restrictions #26641"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()