check for resize access violations (#14142)
This commit is contained in:
@ -11,6 +11,7 @@ static const uint8_t TEST_PPROGRAM_NOT_EXECUTABLE = 4;
|
|||||||
static const uint8_t TEST_EMPTY_ACCOUNTS_SLICE = 5;
|
static const uint8_t TEST_EMPTY_ACCOUNTS_SLICE = 5;
|
||||||
static const uint8_t TEST_CAP_SEEDS = 6;
|
static const uint8_t TEST_CAP_SEEDS = 6;
|
||||||
static const uint8_t TEST_CAP_SIGNERS = 7;
|
static const uint8_t TEST_CAP_SIGNERS = 7;
|
||||||
|
static const uint8_t TEST_ALLOC_ACCESS_VIOLATION = 8;
|
||||||
|
|
||||||
static const int MINT_INDEX = 0;
|
static const int MINT_INDEX = 0;
|
||||||
static const int ARGUMENT_INDEX = 1;
|
static const int ARGUMENT_INDEX = 1;
|
||||||
@ -297,6 +298,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
data, SOL_ARRAY_SIZE(data)};
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
|
||||||
sol_assert(SUCCESS == sol_invoke(&instruction, 0, 0));
|
sol_assert(SUCCESS == sol_invoke(&instruction, 0, 0));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case TEST_CAP_SEEDS: {
|
case TEST_CAP_SEEDS: {
|
||||||
sol_log("Test cap seeds");
|
sol_log("Test cap seeds");
|
||||||
@ -321,6 +323,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
sol_assert(SUCCESS == sol_invoke_signed(
|
sol_assert(SUCCESS == sol_invoke_signed(
|
||||||
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
||||||
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case TEST_CAP_SIGNERS: {
|
case TEST_CAP_SIGNERS: {
|
||||||
sol_log("Test cap signers");
|
sol_log("Test cap signers");
|
||||||
@ -360,6 +363,46 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
sol_assert(SUCCESS == sol_invoke_signed(
|
sol_assert(SUCCESS == sol_invoke_signed(
|
||||||
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
||||||
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TEST_ALLOC_ACCESS_VIOLATION: {
|
||||||
|
sol_log("Test resize violation");
|
||||||
|
SolAccountMeta arguments[] = {
|
||||||
|
{accounts[FROM_INDEX].key, true, true},
|
||||||
|
{accounts[DERIVED_KEY1_INDEX].key, true, true}};
|
||||||
|
uint8_t data[4 + 8 + 8 + 32];
|
||||||
|
*(uint64_t *)(data + 4) = 42;
|
||||||
|
*(uint64_t *)(data + 4 + 8) = MAX_PERMITTED_DATA_INCREASE;
|
||||||
|
sol_memcpy(data + 4 + 8 + 8, params.program_id, SIZE_PUBKEY);
|
||||||
|
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
|
||||||
|
arguments, SOL_ARRAY_SIZE(arguments),
|
||||||
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
|
||||||
|
' ', 'b', 'u', 't', 't', 'e', 'r'};
|
||||||
|
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
||||||
|
{&bump_seed1, 1}};
|
||||||
|
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}};
|
||||||
|
|
||||||
|
SolAccountInfo derived_account = {
|
||||||
|
.key = accounts[DERIVED_KEY1_INDEX].key,
|
||||||
|
.lamports = accounts[DERIVED_KEY1_INDEX].lamports,
|
||||||
|
.data_len = accounts[DERIVED_KEY1_INDEX].data_len,
|
||||||
|
// Point to top edge of heap, attempt to allocate into unprivileged
|
||||||
|
// memory
|
||||||
|
.data = (uint8_t *)0x300007ff8,
|
||||||
|
.owner = accounts[DERIVED_KEY1_INDEX].owner,
|
||||||
|
.rent_epoch = accounts[DERIVED_KEY1_INDEX].rent_epoch,
|
||||||
|
.is_signer = accounts[DERIVED_KEY1_INDEX].is_signer,
|
||||||
|
.is_writable = accounts[DERIVED_KEY1_INDEX].is_writable,
|
||||||
|
.executable = accounts[DERIVED_KEY1_INDEX].executable,
|
||||||
|
};
|
||||||
|
const SolAccountInfo invoke_accounts[] = {
|
||||||
|
accounts[FROM_INDEX], accounts[SYSTEM_PROGRAM_INDEX], derived_account};
|
||||||
|
sol_assert(SUCCESS ==
|
||||||
|
sol_invoke_signed(&instruction,
|
||||||
|
(const SolAccountInfo *)invoke_accounts, 3,
|
||||||
|
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
sol_panic();
|
sol_panic();
|
||||||
|
@ -23,6 +23,7 @@ const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4;
|
|||||||
const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
|
const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
|
||||||
const TEST_CAP_SEEDS: u8 = 6;
|
const TEST_CAP_SEEDS: u8 = 6;
|
||||||
const TEST_CAP_SIGNERS: u8 = 7;
|
const TEST_CAP_SIGNERS: u8 = 7;
|
||||||
|
const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8;
|
||||||
|
|
||||||
// const MINT_INDEX: usize = 0;
|
// const MINT_INDEX: usize = 0;
|
||||||
const ARGUMENT_INDEX: usize = 1;
|
const ARGUMENT_INDEX: usize = 1;
|
||||||
@ -33,7 +34,7 @@ const INVOKED_PROGRAM_DUP_INDEX: usize = 4;
|
|||||||
const DERIVED_KEY1_INDEX: usize = 6;
|
const DERIVED_KEY1_INDEX: usize = 6;
|
||||||
const DERIVED_KEY2_INDEX: usize = 7;
|
const DERIVED_KEY2_INDEX: usize = 7;
|
||||||
const DERIVED_KEY3_INDEX: usize = 8;
|
const DERIVED_KEY3_INDEX: usize = 8;
|
||||||
// const SYSTEM_PROGRAM_INDEX: usize = 9;
|
const SYSTEM_PROGRAM_INDEX: usize = 9;
|
||||||
const FROM_INDEX: usize = 10;
|
const FROM_INDEX: usize = 10;
|
||||||
|
|
||||||
entrypoint!(process_instruction);
|
entrypoint!(process_instruction);
|
||||||
@ -426,6 +427,72 @@ fn process_instruction(
|
|||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
TEST_ALLOC_ACCESS_VIOLATION => {
|
||||||
|
msg!("Test resize violation");
|
||||||
|
let pubkey = *accounts[FROM_INDEX].key;
|
||||||
|
let owner = *accounts[FROM_INDEX].owner;
|
||||||
|
let ptr = accounts[FROM_INDEX].data.borrow().as_ptr() as u64 as *mut _;
|
||||||
|
let len = accounts[FROM_INDEX].data_len();
|
||||||
|
let mut data = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
|
||||||
|
let mut lamports = accounts[FROM_INDEX].lamports();
|
||||||
|
let from_info = AccountInfo::new(
|
||||||
|
&pubkey,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
&mut lamports,
|
||||||
|
&mut data,
|
||||||
|
&owner,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pubkey = *accounts[DERIVED_KEY1_INDEX].key;
|
||||||
|
let owner = *accounts[DERIVED_KEY1_INDEX].owner;
|
||||||
|
// Point to top edge of heap, attempt to allocate into unprivileged memory
|
||||||
|
let mut data = unsafe { std::slice::from_raw_parts_mut(0x300007ff8 as *mut _, 0) };
|
||||||
|
let mut lamports = accounts[DERIVED_KEY1_INDEX].lamports();
|
||||||
|
let derived_info = AccountInfo::new(
|
||||||
|
&pubkey,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
&mut lamports,
|
||||||
|
&mut data,
|
||||||
|
&owner,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pubkey = *accounts[SYSTEM_PROGRAM_INDEX].key;
|
||||||
|
let owner = *accounts[SYSTEM_PROGRAM_INDEX].owner;
|
||||||
|
let ptr = accounts[SYSTEM_PROGRAM_INDEX].data.borrow().as_ptr() as u64 as *mut _;
|
||||||
|
let len = accounts[SYSTEM_PROGRAM_INDEX].data_len();
|
||||||
|
let mut data = unsafe { std::slice::from_raw_parts_mut(ptr, len) };
|
||||||
|
let mut lamports = accounts[SYSTEM_PROGRAM_INDEX].lamports();
|
||||||
|
let system_info = AccountInfo::new(
|
||||||
|
&pubkey,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
&mut lamports,
|
||||||
|
&mut data,
|
||||||
|
&owner,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let instruction = system_instruction::create_account(
|
||||||
|
accounts[FROM_INDEX].key,
|
||||||
|
accounts[DERIVED_KEY1_INDEX].key,
|
||||||
|
42,
|
||||||
|
MAX_PERMITTED_DATA_INCREASE as u64,
|
||||||
|
program_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
invoke_signed(
|
||||||
|
&instruction,
|
||||||
|
&[system_info.clone(), from_info.clone(), derived_info.clone()],
|
||||||
|
&[&[b"You pass butter", &[bump_seed1]]],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,6 +565,7 @@ fn test_program_bpf_invoke() {
|
|||||||
const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
|
const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
|
||||||
const TEST_CAP_SEEDS: u8 = 6;
|
const TEST_CAP_SEEDS: u8 = 6;
|
||||||
const TEST_CAP_SIGNERS: u8 = 7;
|
const TEST_CAP_SIGNERS: u8 = 7;
|
||||||
|
const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -905,6 +906,42 @@ fn test_program_bpf_invoke() {
|
|||||||
for i in 0..20 {
|
for i in 0..20 {
|
||||||
assert_eq!(i as u8, account.data[i]);
|
assert_eq!(i as u8, account.data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempt to realloc into unauthorized address space
|
||||||
|
let account = Account::new(84, 0, &solana_sdk::system_program::id());
|
||||||
|
bank.store_account(&from_keypair.pubkey(), &account);
|
||||||
|
bank.store_account(&derived_key1, &Account::default());
|
||||||
|
let instruction = Instruction::new(
|
||||||
|
invoke_program_id,
|
||||||
|
&[
|
||||||
|
TEST_ALLOC_ACCESS_VIOLATION,
|
||||||
|
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<Pubkey> = inner_instructions[0]
|
||||||
|
.iter()
|
||||||
|
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
||||||
|
.collect();
|
||||||
|
assert_eq!(invoked_programs, vec![solana_sdk::system_program::id()]);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err(),
|
||||||
|
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for program id spoofing
|
// Check for program id spoofing
|
||||||
|
@ -827,6 +827,7 @@ struct AccountReferences<'a> {
|
|||||||
lamports: &'a mut u64,
|
lamports: &'a mut u64,
|
||||||
owner: &'a mut Pubkey,
|
owner: &'a mut Pubkey,
|
||||||
data: &'a mut [u8],
|
data: &'a mut [u8],
|
||||||
|
vm_data_addr: u64,
|
||||||
ref_to_len_in_vm: &'a mut u64,
|
ref_to_len_in_vm: &'a mut u64,
|
||||||
serialized_len_ptr: &'a mut u64,
|
serialized_len_ptr: &'a mut u64,
|
||||||
}
|
}
|
||||||
@ -941,7 +942,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
account_info.owner as *const _ as u64,
|
account_info.owner as *const _ as u64,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
let (data, ref_to_len_in_vm, serialized_len_ptr) = {
|
let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = {
|
||||||
// Double translate data out of RefCell
|
// Double translate data out of RefCell
|
||||||
let data = *translate_type::<&[u8]>(
|
let data = *translate_type::<&[u8]>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
@ -961,13 +962,15 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
ref_of_len_in_input_buffer as *const _ as u64,
|
ref_of_len_in_input_buffer as *const _ as u64,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
|
let vm_data_addr = data.as_ptr() as u64;
|
||||||
(
|
(
|
||||||
translate_slice_mut::<u8>(
|
translate_slice_mut::<u8>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
data.as_ptr() as u64,
|
vm_data_addr,
|
||||||
data.len() as u64,
|
data.len() as u64,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?,
|
)?,
|
||||||
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
)
|
)
|
||||||
@ -984,6 +987,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
});
|
});
|
||||||
@ -1206,9 +1210,10 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
account_info.owner_addr,
|
account_info.owner_addr,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
|
let vm_data_addr = account_info.data_addr;
|
||||||
let data = translate_slice_mut::<u8>(
|
let data = translate_slice_mut::<u8>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
account_info.data_addr,
|
vm_data_addr,
|
||||||
account_info.data_len,
|
account_info.data_len,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
@ -1243,6 +1248,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
});
|
});
|
||||||
@ -1408,8 +1414,6 @@ fn call<'a>(
|
|||||||
*account_ref.lamports = account.lamports;
|
*account_ref.lamports = account.lamports;
|
||||||
*account_ref.owner = account.owner;
|
*account_ref.owner = account.owner;
|
||||||
if account_ref.data.len() != account.data.len() {
|
if account_ref.data.len() != account.data.len() {
|
||||||
*account_ref.ref_to_len_in_vm = account.data.len() as u64;
|
|
||||||
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
|
||||||
if !account_ref.data.is_empty() {
|
if !account_ref.data.is_empty() {
|
||||||
// Only support for `CreateAccount` at this time.
|
// Only support for `CreateAccount` at this time.
|
||||||
// Need a way to limit total realloc size across multiple CPI calls
|
// Need a way to limit total realloc size across multiple CPI calls
|
||||||
@ -1422,6 +1426,14 @@ fn call<'a>(
|
|||||||
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
let _ = translate(
|
||||||
|
memory_mapping,
|
||||||
|
AccessType::Store,
|
||||||
|
account_ref.vm_data_addr,
|
||||||
|
account.data.len() as u64,
|
||||||
|
)?;
|
||||||
|
*account_ref.ref_to_len_in_vm = account.data.len() as u64;
|
||||||
|
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
||||||
}
|
}
|
||||||
account_ref
|
account_ref
|
||||||
.data
|
.data
|
||||||
|
Reference in New Issue
Block a user