Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions feature-set/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,11 +768,11 @@ pub mod apply_cost_tracker_during_replay {
}

pub mod stricter_abi_and_runtime_constraints {
solana_pubkey::declare_id!("sD3uVpaavUXQRvDXrMFCQ2CqLqnbz5mK8ttWNXbtD3r");
solana_pubkey::declare_id!("Eoh7e1sDqtyPtuiWAhBNSJinvtJWTTDgeUMRi3RF8zWS");
}

pub mod account_data_direct_mapping {
solana_pubkey::declare_id!("DFN8MyKpQqFW31qczcahgnnxcAHQc6P94wtTEX5EP1RA");
solana_pubkey::declare_id!("6f2qai82RU7Dutj1WJfRzLJKYA36QWvTa89CR1imgj7N");
}

pub mod add_set_tx_loaded_accounts_data_size_instruction {
Expand Down
59 changes: 57 additions & 2 deletions program-runtime/src/cpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ pub fn cpi_common<S: SyscallInvokeSigned>(
let callee_account = instruction_context
.try_borrow_instruction_account(translated_account.index_in_caller)?;
let update_caller = update_callee_account(
memory_mapping,
check_aligned,
&translated_account.caller_account,
callee_account,
Expand Down Expand Up @@ -1097,6 +1098,7 @@ where
// BorrowedAccount (callee_account) so the callee can see the
// changes.
update_callee_account(
memory_mapping,
check_aligned,
&caller_account,
callee_account,
Expand Down Expand Up @@ -1141,6 +1143,7 @@ fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<
// When true is returned, the caller account must be updated after CPI. This
// is only set for stricter_abi_and_runtime_constraints when the pointer may have changed.
fn update_callee_account(
memory_mapping: &MemoryMapping,
check_aligned: bool,
caller_account: &CallerAccount,
mut callee_account: BorrowedInstructionAccount<'_, '_>,
Expand Down Expand Up @@ -1168,6 +1171,21 @@ fn update_callee_account(
if post_len > address_space_reserved_for_account {
return Err(InstructionError::InvalidRealloc.into());
}
if !account_data_direct_mapping && post_len < prev_len {
// If the account has been shrunk, we're going to zero the unused memory
// *that was previously used*.
let serialized_data = CallerAccount::get_serialized_data(
memory_mapping,
caller_account.vm_data_addr,
prev_len as u64,
stricter_abi_and_runtime_constraints,
account_data_direct_mapping,
)?;
serialized_data
.get_mut(post_len..)
.ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
.fill(0);
}
callee_account.set_data_length(post_len)?;
// pointer to data may have changed, so caller must be updated
must_update_caller = true;
Expand Down Expand Up @@ -2180,7 +2198,16 @@ mod tests {

let mut mock_caller_account =
MockCallerAccount::new(1234, *account.owner(), account.data(), false);

let config = Config {
aligned_memory_mapping: false,
..Config::default()
};
let memory_mapping = MemoryMapping::new(
mock_caller_account.regions.clone(),
&config,
SBPFVersion::V3,
)
.unwrap();
let caller_account = mock_caller_account.caller_account();

borrow_instruction_account!(callee_account, invoke_context, 0);
Expand All @@ -2189,6 +2216,7 @@ mod tests {
*caller_account.owner = Pubkey::new_unique();

update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand Down Expand Up @@ -2219,13 +2247,23 @@ mod tests {

let mut mock_caller_account =
MockCallerAccount::new(1234, *account.owner(), account.data(), false);

let config = Config {
aligned_memory_mapping: false,
..Config::default()
};
let memory_mapping = MemoryMapping::new(
mock_caller_account.regions.clone(),
&config,
SBPFVersion::V3,
)
.unwrap();
let mut caller_account = mock_caller_account.caller_account();
borrow_instruction_account!(callee_account, invoke_context, 0);

// stricter_abi_and_runtime_constraints does not copy data in update_callee_account()
caller_account.serialized_data[0] = b'b';
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2242,6 +2280,7 @@ mod tests {
caller_account.serialized_data = &mut data;
assert_eq!(
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2259,6 +2298,7 @@ mod tests {
borrow_instruction_account!(callee_account, invoke_context, 0);
assert_eq!(
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2277,6 +2317,7 @@ mod tests {
caller_account.owner = &mut owner;
borrow_instruction_account!(callee_account, invoke_context, 0);
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2290,6 +2331,7 @@ mod tests {
// growing beyond address_space_reserved_for_account
*caller_account.ref_to_len_in_vm = (7 + MAX_PERMITTED_DATA_INCREASE) as u64;
let result = update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand Down Expand Up @@ -2323,13 +2365,24 @@ mod tests {

let mut mock_caller_account =
MockCallerAccount::new(1234, *account.owner(), account.data(), false);
let config = Config {
aligned_memory_mapping: false,
..Config::default()
};
let memory_mapping = MemoryMapping::new(
mock_caller_account.regions.clone(),
&config,
SBPFVersion::V3,
)
.unwrap();
let mut caller_account = mock_caller_account.caller_account();
borrow_instruction_account!(callee_account, invoke_context, 0);

// stricter_abi_and_runtime_constraints does not copy data in update_callee_account()
caller_account.serialized_data[0] = b'b';
assert_matches!(
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2346,6 +2399,7 @@ mod tests {
borrow_instruction_account!(callee_account, invoke_context, 0);
assert_matches!(
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand All @@ -2362,6 +2416,7 @@ mod tests {
borrow_instruction_account!(callee_account, invoke_context, 0);
assert_matches!(
update_callee_account(
&memory_mapping,
true, // check_aligned
&caller_account,
callee_account,
Expand Down
40 changes: 40 additions & 0 deletions programs/sbf/rust/realloc_invoke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,46 @@ fn process_instruction(
account.resize(new_len.saturating_add(extend_len))?;
assert_eq!(new_len.saturating_add(extend_len), account.data_len());
}
INVOKE_REALLOC_SHRINK_THEN_CPI_THEN_REALLOC_EXTEND => {
let (bytes, remaining_data) =
instruction_data[2..].split_at(std::mem::size_of::<usize>());
let cpi_len = usize::from_le_bytes(bytes.try_into().unwrap());
msg!("realloc to {} byte(s)", cpi_len);
account.resize(cpi_len)?;
msg!("invoke");
let realloc_to_ix = {
let mut instruction_data = vec![INVOKE_REALLOC_TO, 1];
instruction_data.extend_from_slice(&cpi_len.to_le_bytes());

Instruction::new_with_bytes(
*invoke_program_id,
&instruction_data,
vec![
AccountMeta::new(*account.key, false),
AccountMeta::new_readonly(*invoke_program_id, false),
],
)
};
invoke(&realloc_to_ix, accounts)?;
assert_eq!(cpi_len, account.data_len());
let (bytes, _) = remaining_data.split_at(std::mem::size_of::<usize>());
let final_len = usize::from_le_bytes(bytes.try_into().unwrap());
msg!("realloc to {} byte(s)", final_len.saturating_add(1));
unsafe {
*account
.try_borrow_mut_data()?
.as_mut_ptr()
.add(final_len.saturating_add(1)) = 0xAA;
}
msg!("realloc to {} byte(s)", final_len);
// We want to see the program runtime handling the zeroing of the extension.
// Thus we manually overwrite the length instead of calling account.resize()
// which would zero extend inside the program.
unsafe {
*(account.try_borrow_mut_data()?.as_mut_ptr().offset(-8) as *mut u64) =
final_len as u64;
}
}
INVOKE_REALLOC_MAX_TWICE => {
msg!("invoke realloc max twice");
invoke(
Expand Down
1 change: 1 addition & 0 deletions programs/sbf/rust/realloc_invoke_dep/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pub const INVOKE_REALLOC_MAX_TWICE: u8 = 13;
pub const INVOKE_REALLOC_MAX_INVOKE_MAX: u8 = 14;
pub const INVOKE_INVOKE_MAX_TWICE: u8 = 15;
pub const INVOKE_REALLOC_TO_THEN_LOCAL_REALLOC_EXTEND: u8 = 16;
pub const INVOKE_REALLOC_SHRINK_THEN_CPI_THEN_REALLOC_EXTEND: u8 = 17;
34 changes: 34 additions & 0 deletions programs/sbf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3547,6 +3547,40 @@ fn test_program_sbf_realloc_invoke() {
}
}

// Realloc shrink, then CPI, then realloc extend
let mut invoke_account = AccountSharedData::new(100_000_000, 10, &realloc_invoke_program_id);
invoke_account.set_data(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
bank.store_account(&invoke_pubkey, &invoke_account);
let mut instruction_data = vec![];
instruction_data.extend_from_slice(&[INVOKE_REALLOC_SHRINK_THEN_CPI_THEN_REALLOC_EXTEND, 1]);
instruction_data.extend_from_slice(&5_u64.to_le_bytes());
instruction_data.extend_from_slice(&10_u64.to_le_bytes());
let result = bank_client.send_and_confirm_message(
signer,
Message::new(
&[
Instruction::new_with_bytes(
realloc_invoke_program_id,
&instruction_data,
vec![
AccountMeta::new(invoke_pubkey, false),
AccountMeta::new_readonly(realloc_invoke_program_id, false),
],
),
ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(
LOADED_ACCOUNTS_DATA_SIZE_LIMIT_FOR_TEST,
),
],
Some(&mint_pubkey),
),
);
assert!(result.is_ok());
let data = bank_client
.get_account_data(&invoke_pubkey)
.unwrap()
.unwrap();
assert_eq!(data, &[0, 1, 2, 3, 4, 0, 0, 0, 0, 0]);

// Realloc invoke max twice
let invoke_account = AccountSharedData::new(42, 0, &realloc_program_id);
bank.store_account(&invoke_pubkey, &invoke_account);
Expand Down
Loading