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:
parent
d9c7bc7e78
commit
2335f6908a
|
@ -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}};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue