Load executable accounts from invoke context (#14574)
This commit is contained in:
@ -127,9 +127,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
|
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)},
|
||||||
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
|
{seeds2, SOL_ARRAY_SIZE(seeds2)}};
|
||||||
|
|
||||||
sol_assert(SUCCESS == sol_invoke_signed(
|
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
|
||||||
&instruction, accounts, SOL_ARRAY_SIZE(accounts),
|
params.ka_num, signers_seeds,
|
||||||
signers_seeds, SOL_ARRAY_SIZE(signers_seeds)));
|
SOL_ARRAY_SIZE(signers_seeds)));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -189,8 +189,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||||||
data, SOL_ARRAY_SIZE(data)};
|
data, SOL_ARRAY_SIZE(data)};
|
||||||
|
|
||||||
sol_log("Invoke again");
|
sol_log("Invoke again");
|
||||||
sol_assert(SUCCESS ==
|
sol_assert(SUCCESS == sol_invoke(&instruction, accounts, params.ka_num));
|
||||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
|
||||||
} else {
|
} else {
|
||||||
sol_log("Last invoked");
|
sol_log("Last invoked");
|
||||||
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
|
for (int i = 0; i < accounts[INVOKED_ARGUMENT_INDEX].data_len; i++) {
|
||||||
|
@ -804,7 +804,7 @@ fn test_program_bpf_invoke() {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(invoked_programs, vec![argument_keypair.pubkey().clone()]);
|
assert_eq!(invoked_programs, vec![]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err(),
|
result.unwrap_err(),
|
||||||
TransactionError::InstructionError(0, InstructionError::AccountNotExecutable)
|
TransactionError::InstructionError(0, InstructionError::AccountNotExecutable)
|
||||||
|
@ -19,7 +19,8 @@ use solana_sdk::{
|
|||||||
feature_set::{
|
feature_set::{
|
||||||
abort_on_all_cpi_failures, limit_cpi_loader_invoke, pubkey_log_syscall_enabled,
|
abort_on_all_cpi_failures, limit_cpi_loader_invoke, pubkey_log_syscall_enabled,
|
||||||
ristretto_mul_syscall_enabled, sha256_syscall_enabled, sol_log_compute_units_syscall,
|
ristretto_mul_syscall_enabled, sha256_syscall_enabled, sol_log_compute_units_syscall,
|
||||||
try_find_program_address_syscall_enabled, use_loaded_program_accounts,
|
try_find_program_address_syscall_enabled, use_loaded_executables,
|
||||||
|
use_loaded_program_accounts,
|
||||||
},
|
},
|
||||||
hash::{Hasher, HASH_BYTES},
|
hash::{Hasher, HASH_BYTES},
|
||||||
instruction::{AccountMeta, Instruction, InstructionError},
|
instruction::{AccountMeta, Instruction, InstructionError},
|
||||||
@ -31,7 +32,7 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
alloc::Layout,
|
alloc::Layout,
|
||||||
cell::{RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
mem::{align_of, size_of},
|
mem::{align_of, size_of},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@ -834,6 +835,7 @@ struct AccountReferences<'a> {
|
|||||||
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,
|
||||||
}
|
}
|
||||||
|
type TranslatedAccount<'a> = (Rc<RefCell<Account>>, Option<AccountReferences<'a>>);
|
||||||
type TranslatedAccounts<'a> = (
|
type TranslatedAccounts<'a> = (
|
||||||
Vec<Rc<RefCell<Account>>>,
|
Vec<Rc<RefCell<Account>>>,
|
||||||
Vec<Option<AccountReferences<'a>>>,
|
Vec<Option<AccountReferences<'a>>>,
|
||||||
@ -842,16 +844,15 @@ type TranslatedAccounts<'a> = (
|
|||||||
/// Implemented by language specific data structure translators
|
/// Implemented by language specific data structure translators
|
||||||
trait SyscallInvokeSigned<'a> {
|
trait SyscallInvokeSigned<'a> {
|
||||||
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
||||||
|
fn get_context(&self) -> Result<Ref<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
||||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>];
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>];
|
||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
max_size: usize,
|
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<Instruction, EbpfError<BPFError>>;
|
) -> Result<Instruction, EbpfError<BPFError>>;
|
||||||
fn translate_accounts(
|
fn translate_accounts(
|
||||||
&self,
|
&self,
|
||||||
skip_program: bool,
|
|
||||||
account_keys: &[Pubkey],
|
account_keys: &[Pubkey],
|
||||||
program_account_index: usize,
|
program_account_index: usize,
|
||||||
account_infos_addr: u64,
|
account_infos_addr: u64,
|
||||||
@ -879,17 +880,29 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
}
|
}
|
||||||
|
fn get_context(&self) -> Result<Ref<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
|
||||||
|
self.invoke_context
|
||||||
|
.try_borrow()
|
||||||
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
|
}
|
||||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||||
self.callers_keyed_accounts
|
self.callers_keyed_accounts
|
||||||
}
|
}
|
||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
max_size: usize,
|
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<Instruction, EbpfError<BPFError>> {
|
) -> Result<Instruction, EbpfError<BPFError>> {
|
||||||
let ix = translate_type::<Instruction>(memory_mapping, addr, self.loader_id)?;
|
let ix = translate_type::<Instruction>(memory_mapping, addr, self.loader_id)?;
|
||||||
check_instruction_size(ix.accounts.len(), ix.data.len(), max_size)?;
|
|
||||||
|
check_instruction_size(
|
||||||
|
ix.accounts.len(),
|
||||||
|
ix.data.len(),
|
||||||
|
self.invoke_context
|
||||||
|
.borrow()
|
||||||
|
.get_bpf_compute_budget()
|
||||||
|
.max_cpi_instruction_size,
|
||||||
|
)?;
|
||||||
|
|
||||||
let accounts = translate_slice::<AccountMeta>(
|
let accounts = translate_slice::<AccountMeta>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
@ -914,38 +927,35 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
|
|
||||||
fn translate_accounts(
|
fn translate_accounts(
|
||||||
&self,
|
&self,
|
||||||
skip_program: bool,
|
|
||||||
account_keys: &[Pubkey],
|
account_keys: &[Pubkey],
|
||||||
program_account_index: usize,
|
program_account_index: usize,
|
||||||
account_infos_addr: u64,
|
account_infos_addr: u64,
|
||||||
account_infos_len: u64,
|
account_infos_len: u64,
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<TranslatedAccounts<'a>, EbpfError<BPFError>> {
|
) -> Result<TranslatedAccounts<'a>, EbpfError<BPFError>> {
|
||||||
let account_infos = if account_infos_len > 0 {
|
let invoke_context = self.invoke_context.borrow();
|
||||||
translate_slice::<AccountInfo>(
|
|
||||||
|
let account_infos = translate_slice::<AccountInfo>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
account_infos_len,
|
account_infos_len,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?
|
)?;
|
||||||
} else {
|
check_account_infos(account_infos.len(), &invoke_context)?;
|
||||||
&[]
|
let account_info_keys = account_infos
|
||||||
};
|
.iter()
|
||||||
|
.map(|account_info| {
|
||||||
let mut accounts = Vec::with_capacity(account_keys.len());
|
translate_type::<Pubkey>(
|
||||||
let mut refs = Vec::with_capacity(account_keys.len());
|
|
||||||
'root: for (i, account_key) in account_keys.iter().enumerate() {
|
|
||||||
if skip_program && i == program_account_index {
|
|
||||||
// Don't look for caller passed executable, runtime already has it
|
|
||||||
continue 'root;
|
|
||||||
}
|
|
||||||
for account_info in account_infos.iter() {
|
|
||||||
let key = translate_type::<Pubkey>(
|
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
account_info.key as *const _ as u64,
|
account_info.key as *const _ as u64,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)
|
||||||
if account_key == key {
|
})
|
||||||
|
.collect::<Result<Vec<_>, EbpfError<BPFError>>>()?;
|
||||||
|
|
||||||
|
let translate = |account_info: &AccountInfo| {
|
||||||
|
// Translate the account from user space
|
||||||
|
|
||||||
let lamports = {
|
let lamports = {
|
||||||
// Double translate lamports out of RefCell
|
// Double translate lamports out of RefCell
|
||||||
let ptr = translate_type::<u64>(
|
let ptr = translate_type::<u64>(
|
||||||
@ -994,28 +1004,33 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
accounts.push(Rc::new(RefCell::new(Account {
|
Ok((
|
||||||
|
Rc::new(RefCell::new(Account {
|
||||||
lamports: *lamports,
|
lamports: *lamports,
|
||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
executable: account_info.executable,
|
executable: account_info.executable,
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
rent_epoch: account_info.rent_epoch,
|
rent_epoch: account_info.rent_epoch,
|
||||||
})));
|
})),
|
||||||
refs.push(Some(AccountReferences {
|
Some(AccountReferences {
|
||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
vm_data_addr,
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
}));
|
}),
|
||||||
continue 'root;
|
))
|
||||||
}
|
};
|
||||||
}
|
|
||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((accounts, refs))
|
get_translated_accounts(
|
||||||
|
account_keys,
|
||||||
|
program_account_index,
|
||||||
|
&account_info_keys,
|
||||||
|
account_infos,
|
||||||
|
&invoke_context,
|
||||||
|
translate,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_signers(
|
fn translate_signers(
|
||||||
@ -1151,6 +1166,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
}
|
}
|
||||||
|
fn get_context(&self) -> Result<Ref<&'a mut dyn InvokeContext>, EbpfError<BPFError>> {
|
||||||
|
self.invoke_context
|
||||||
|
.try_borrow()
|
||||||
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||||
self.callers_keyed_accounts
|
self.callers_keyed_accounts
|
||||||
@ -1159,11 +1179,18 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
fn translate_instruction(
|
fn translate_instruction(
|
||||||
&self,
|
&self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
max_size: usize,
|
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<Instruction, EbpfError<BPFError>> {
|
) -> Result<Instruction, EbpfError<BPFError>> {
|
||||||
let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, self.loader_id)?;
|
let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, self.loader_id)?;
|
||||||
check_instruction_size(ix_c.accounts_len, ix_c.data_len, max_size)?;
|
|
||||||
|
check_instruction_size(
|
||||||
|
ix_c.accounts_len,
|
||||||
|
ix_c.data_len,
|
||||||
|
self.invoke_context
|
||||||
|
.borrow()
|
||||||
|
.get_bpf_compute_budget()
|
||||||
|
.max_cpi_instruction_size,
|
||||||
|
)?;
|
||||||
let program_id =
|
let program_id =
|
||||||
translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, self.loader_id)?;
|
translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, self.loader_id)?;
|
||||||
let meta_cs = translate_slice::<SolAccountMeta>(
|
let meta_cs = translate_slice::<SolAccountMeta>(
|
||||||
@ -1201,33 +1228,31 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
|
|
||||||
fn translate_accounts(
|
fn translate_accounts(
|
||||||
&self,
|
&self,
|
||||||
skip_program: bool,
|
|
||||||
account_keys: &[Pubkey],
|
account_keys: &[Pubkey],
|
||||||
program_account_index: usize,
|
program_account_index: usize,
|
||||||
account_infos_addr: u64,
|
account_infos_addr: u64,
|
||||||
account_infos_len: u64,
|
account_infos_len: u64,
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<TranslatedAccounts<'a>, EbpfError<BPFError>> {
|
) -> Result<TranslatedAccounts<'a>, EbpfError<BPFError>> {
|
||||||
|
let invoke_context = self.invoke_context.borrow();
|
||||||
|
|
||||||
let account_infos = translate_slice::<SolAccountInfo>(
|
let account_infos = translate_slice::<SolAccountInfo>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
account_infos_len,
|
account_infos_len,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
let mut accounts = Vec::with_capacity(account_keys.len());
|
check_account_infos(account_infos.len(), &invoke_context)?;
|
||||||
let mut refs = Vec::with_capacity(account_keys.len());
|
let account_info_keys = account_infos
|
||||||
'root: for (i, account_key) in account_keys.iter().enumerate() {
|
.iter()
|
||||||
if skip_program && i == program_account_index {
|
.map(|account_info| {
|
||||||
// Don't look for caller passed executable, runtime already has it
|
translate_type::<Pubkey>(memory_mapping, account_info.key_addr, self.loader_id)
|
||||||
continue 'root;
|
})
|
||||||
}
|
.collect::<Result<Vec<_>, EbpfError<BPFError>>>()?;
|
||||||
for account_info in account_infos.iter() {
|
|
||||||
let key = translate_type::<Pubkey>(
|
let translate = |account_info: &SolAccountInfo| {
|
||||||
memory_mapping,
|
// Translate the account from user space
|
||||||
account_info.key_addr,
|
|
||||||
self.loader_id,
|
|
||||||
)?;
|
|
||||||
if account_key == key {
|
|
||||||
let lamports = translate_type_mut::<u64>(
|
let lamports = translate_type_mut::<u64>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
account_info.lamports_addr,
|
account_info.lamports_addr,
|
||||||
@ -1265,28 +1290,33 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
accounts.push(Rc::new(RefCell::new(Account {
|
Ok((
|
||||||
|
Rc::new(RefCell::new(Account {
|
||||||
lamports: *lamports,
|
lamports: *lamports,
|
||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
executable: account_info.executable,
|
executable: account_info.executable,
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
rent_epoch: account_info.rent_epoch,
|
rent_epoch: account_info.rent_epoch,
|
||||||
})));
|
})),
|
||||||
refs.push(Some(AccountReferences {
|
Some(AccountReferences {
|
||||||
lamports,
|
lamports,
|
||||||
owner,
|
owner,
|
||||||
data,
|
data,
|
||||||
vm_data_addr,
|
vm_data_addr,
|
||||||
ref_to_len_in_vm,
|
ref_to_len_in_vm,
|
||||||
serialized_len_ptr,
|
serialized_len_ptr,
|
||||||
}));
|
}),
|
||||||
continue 'root;
|
))
|
||||||
}
|
};
|
||||||
}
|
|
||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((accounts, refs))
|
get_translated_accounts(
|
||||||
|
account_keys,
|
||||||
|
program_account_index,
|
||||||
|
&account_info_keys,
|
||||||
|
account_infos,
|
||||||
|
&invoke_context,
|
||||||
|
translate,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_signers(
|
fn translate_signers(
|
||||||
@ -1364,6 +1394,58 @@ impl<'a> SyscallObject<BPFError> for SyscallInvokeSignedC<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_translated_accounts<'a, T, F>(
|
||||||
|
account_keys: &[Pubkey],
|
||||||
|
program_account_index: usize,
|
||||||
|
account_info_keys: &[&Pubkey],
|
||||||
|
account_infos: &[T],
|
||||||
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
|
do_translate: F,
|
||||||
|
) -> Result<TranslatedAccounts<'a>, EbpfError<BPFError>>
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> Result<TranslatedAccount<'a>, EbpfError<BPFError>>,
|
||||||
|
{
|
||||||
|
let mut accounts = Vec::with_capacity(account_keys.len());
|
||||||
|
let mut refs = Vec::with_capacity(account_keys.len());
|
||||||
|
for (i, ref account_key) in account_keys.iter().enumerate() {
|
||||||
|
let account =
|
||||||
|
invoke_context
|
||||||
|
.get_account(&account_key)
|
||||||
|
.ok_or(SyscallError::InstructionError(
|
||||||
|
InstructionError::MissingAccount,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
if (invoke_context.is_feature_active(&use_loaded_program_accounts::id())
|
||||||
|
&& i == program_account_index)
|
||||||
|
|| (invoke_context.is_feature_active(&use_loaded_executables::id())
|
||||||
|
&& account.borrow().executable)
|
||||||
|
{
|
||||||
|
// Use the known executable
|
||||||
|
accounts.push(Rc::new(account));
|
||||||
|
refs.push(None);
|
||||||
|
} else if let Some(account_info) =
|
||||||
|
account_info_keys
|
||||||
|
.iter()
|
||||||
|
.zip(account_infos)
|
||||||
|
.find_map(|(key, account_info)| {
|
||||||
|
if key == account_key {
|
||||||
|
Some(account_info)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let (account, account_ref) = do_translate(account_info)?;
|
||||||
|
accounts.push(account);
|
||||||
|
refs.push(account_ref);
|
||||||
|
} else {
|
||||||
|
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((accounts, refs))
|
||||||
|
}
|
||||||
|
|
||||||
fn check_instruction_size(
|
fn check_instruction_size(
|
||||||
num_accounts: usize,
|
num_accounts: usize,
|
||||||
data_len: usize,
|
data_len: usize,
|
||||||
@ -1377,6 +1459,25 @@ fn check_instruction_size(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_account_infos(
|
||||||
|
len: usize,
|
||||||
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
|
) -> Result<(), EbpfError<BPFError>> {
|
||||||
|
if len * size_of::<Pubkey>()
|
||||||
|
> invoke_context
|
||||||
|
.get_bpf_compute_budget()
|
||||||
|
.max_cpi_instruction_size
|
||||||
|
&& invoke_context.is_feature_active(&use_loaded_program_accounts::id())
|
||||||
|
{
|
||||||
|
// 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::InstructionError(InstructionError::ComputationalBudgetExceeded).into(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn check_authorized_program(
|
fn check_authorized_program(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
instruction_data: &[u8],
|
instruction_data: &[u8],
|
||||||
@ -1392,6 +1493,31 @@ fn check_authorized_program(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_upgradeable_executable(
|
||||||
|
program_account: &RefCell<Account>,
|
||||||
|
invoke_context: &Ref<&mut dyn InvokeContext>,
|
||||||
|
) -> Result<Option<(Pubkey, RefCell<Account>)>, EbpfError<BPFError>> {
|
||||||
|
if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
||||||
|
if let UpgradeableLoaderState::Program {
|
||||||
|
programdata_address,
|
||||||
|
} = program_account
|
||||||
|
.borrow()
|
||||||
|
.state()
|
||||||
|
.map_err(SyscallError::InstructionError)?
|
||||||
|
{
|
||||||
|
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
||||||
|
Ok(Some((programdata_address, account)))
|
||||||
|
} else {
|
||||||
|
Err(SyscallError::InstructionError(InstructionError::MissingAccount).into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(SyscallError::InstructionError(InstructionError::MissingAccount).into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call process instruction, common to both Rust and C
|
/// Call process instruction, common to both Rust and C
|
||||||
fn call<'a>(
|
fn call<'a>(
|
||||||
syscall: &mut dyn SyscallInvokeSigned<'a>,
|
syscall: &mut dyn SyscallInvokeSigned<'a>,
|
||||||
@ -1402,26 +1528,20 @@ fn call<'a>(
|
|||||||
signers_seeds_len: u64,
|
signers_seeds_len: u64,
|
||||||
memory_mapping: &MemoryMapping,
|
memory_mapping: &MemoryMapping,
|
||||||
) -> Result<u64, EbpfError<BPFError>> {
|
) -> Result<u64, EbpfError<BPFError>> {
|
||||||
let mut invoke_context = syscall.get_context_mut()?;
|
let (message, executables, accounts, account_refs, abort_on_all_cpi_failures) = {
|
||||||
|
let invoke_context = syscall.get_context()?;
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
.consume(invoke_context.get_bpf_compute_budget().invoke_units)?;
|
.consume(invoke_context.get_bpf_compute_budget().invoke_units)?;
|
||||||
|
|
||||||
let use_loaded_program_accounts =
|
|
||||||
invoke_context.is_feature_active(&use_loaded_program_accounts::id());
|
|
||||||
|
|
||||||
// Translate and verify caller's data
|
|
||||||
|
|
||||||
let instruction = syscall.translate_instruction(
|
|
||||||
instruction_addr,
|
|
||||||
invoke_context
|
|
||||||
.get_bpf_compute_budget()
|
|
||||||
.max_cpi_instruction_size,
|
|
||||||
&memory_mapping,
|
|
||||||
)?;
|
|
||||||
let caller_program_id = invoke_context
|
let caller_program_id = invoke_context
|
||||||
.get_caller()
|
.get_caller()
|
||||||
.map_err(SyscallError::InstructionError)?;
|
.map_err(SyscallError::InstructionError)?;
|
||||||
|
|
||||||
|
// Translate and verify caller's data
|
||||||
|
|
||||||
|
let instruction = syscall.translate_instruction(instruction_addr, &memory_mapping)?;
|
||||||
let signers = syscall.translate_signers(
|
let signers = syscall.translate_signers(
|
||||||
caller_program_id,
|
caller_program_id,
|
||||||
signers_seeds_addr,
|
signers_seeds_addr,
|
||||||
@ -1438,8 +1558,7 @@ fn call<'a>(
|
|||||||
if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) {
|
if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) {
|
||||||
check_authorized_program(&callee_program_id, &instruction.data)?;
|
check_authorized_program(&callee_program_id, &instruction.data)?;
|
||||||
}
|
}
|
||||||
let (mut accounts, mut account_refs) = syscall.translate_accounts(
|
let (accounts, account_refs) = syscall.translate_accounts(
|
||||||
use_loaded_program_accounts,
|
|
||||||
&message.account_keys,
|
&message.account_keys,
|
||||||
callee_program_id_index,
|
callee_program_id_index,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
@ -1447,64 +1566,46 @@ fn call<'a>(
|
|||||||
memory_mapping,
|
memory_mapping,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Process instruction
|
// Construct executables
|
||||||
|
|
||||||
invoke_context.record_instruction(&instruction);
|
let program_account =
|
||||||
|
|
||||||
let program_account = if use_loaded_program_accounts {
|
|
||||||
let program_account = invoke_context.get_account(&callee_program_id).ok_or(
|
|
||||||
SyscallError::InstructionError(InstructionError::MissingAccount),
|
|
||||||
)?;
|
|
||||||
accounts.insert(callee_program_id_index, Rc::new(program_account.clone()));
|
|
||||||
account_refs.insert(callee_program_id_index, None);
|
|
||||||
program_account
|
|
||||||
} else {
|
|
||||||
(**accounts
|
(**accounts
|
||||||
.get(callee_program_id_index)
|
.get(callee_program_id_index)
|
||||||
.ok_or(SyscallError::InstructionError(
|
.ok_or(SyscallError::InstructionError(
|
||||||
InstructionError::MissingAccount,
|
InstructionError::MissingAccount,
|
||||||
))?)
|
))?)
|
||||||
.clone()
|
.clone();
|
||||||
|
let programdata_executable = get_upgradeable_executable(&program_account, &invoke_context)?;
|
||||||
|
let mut executables = vec![(callee_program_id, program_account)];
|
||||||
|
if let Some(executable) = programdata_executable {
|
||||||
|
executables.push(executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record the instruction
|
||||||
|
|
||||||
|
invoke_context.record_instruction(&instruction);
|
||||||
|
|
||||||
|
(
|
||||||
|
message,
|
||||||
|
executables,
|
||||||
|
accounts,
|
||||||
|
account_refs,
|
||||||
|
invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if !program_account.borrow().executable {
|
|
||||||
return Err(SyscallError::InstructionError(InstructionError::AccountNotExecutable).into());
|
// Process instruction
|
||||||
}
|
|
||||||
let programdata_executable = if program_account.borrow().owner == bpf_loader_upgradeable::id() {
|
|
||||||
if let UpgradeableLoaderState::Program {
|
|
||||||
programdata_address,
|
|
||||||
} = program_account
|
|
||||||
.borrow()
|
|
||||||
.state()
|
|
||||||
.map_err(SyscallError::InstructionError)?
|
|
||||||
{
|
|
||||||
if let Some(account) = invoke_context.get_account(&programdata_address) {
|
|
||||||
Some((programdata_address, account))
|
|
||||||
} else {
|
|
||||||
return Err(
|
|
||||||
SyscallError::InstructionError(InstructionError::MissingAccount).into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(SyscallError::InstructionError(InstructionError::MissingAccount).into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let mut executable_accounts = vec![(callee_program_id, program_account)];
|
|
||||||
if let Some(programdata) = programdata_executable {
|
|
||||||
executable_accounts.push(programdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::deref_addrof)]
|
#[allow(clippy::deref_addrof)]
|
||||||
match MessageProcessor::process_cross_program_instruction(
|
match MessageProcessor::process_cross_program_instruction(
|
||||||
&message,
|
&message,
|
||||||
&executable_accounts,
|
&executables,
|
||||||
&accounts,
|
&accounts,
|
||||||
*(&mut *invoke_context),
|
*(&mut *(syscall.get_context_mut()?)),
|
||||||
) {
|
) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if invoke_context.is_feature_active(&abort_on_all_cpi_failures::id()) {
|
if abort_on_all_cpi_failures {
|
||||||
return Err(SyscallError::InstructionError(err).into());
|
return Err(SyscallError::InstructionError(err).into());
|
||||||
} else {
|
} else {
|
||||||
match ProgramError::try_from(err) {
|
match ProgramError::try_from(err) {
|
||||||
@ -2118,7 +2219,7 @@ mod tests {
|
|||||||
addr: 8192,
|
addr: 8192,
|
||||||
len: bytes2.len(),
|
len: bytes2.len(),
|
||||||
};
|
};
|
||||||
let bytes_to_hash = [mock_slice1, mock_slice2]; // TODO
|
let bytes_to_hash = [mock_slice1, mock_slice2];
|
||||||
let hash_result = [0; HASH_BYTES];
|
let hash_result = [0; HASH_BYTES];
|
||||||
let ro_len = bytes_to_hash.len() as u64;
|
let ro_len = bytes_to_hash.len() as u64;
|
||||||
let ro_va = 96;
|
let ro_va = 96;
|
||||||
|
@ -541,18 +541,19 @@ impl MessageProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate the caller has access to the program account
|
// validate the caller has access to the program account and that it is executable
|
||||||
let program_id = instruction.program_id;
|
let program_id = instruction.program_id;
|
||||||
let _ = keyed_accounts
|
match keyed_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|keyed_account| {
|
.find(|keyed_account| &program_id == keyed_account.unsigned_key())
|
||||||
if &program_id == keyed_account.unsigned_key() {
|
{
|
||||||
Some(keyed_account)
|
Some(keyed_account) => {
|
||||||
} else {
|
if !keyed_account.executable()? {
|
||||||
None
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return Err(InstructionError::MissingAccount),
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.ok_or(InstructionError::MissingAccount)?;
|
|
||||||
|
|
||||||
let message = Message::new(&[instruction.clone()], None);
|
let message = Message::new(&[instruction.clone()], None);
|
||||||
let program_id_index = message.instructions[0].program_id_index as usize;
|
let program_id_index = message.instructions[0].program_id_index as usize;
|
||||||
|
@ -134,6 +134,10 @@ pub mod abort_on_all_cpi_failures {
|
|||||||
solana_sdk::declare_id!("ED5D5a2hQaECHaMmKpnU48GdsfafdCjkb3pgAw5RKbb2");
|
solana_sdk::declare_id!("ED5D5a2hQaECHaMmKpnU48GdsfafdCjkb3pgAw5RKbb2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod use_loaded_executables {
|
||||||
|
solana_sdk::declare_id!("2SLL2KLakB83YAnF1TwFb1hpycrWeHAfHYyLhwk2JRGn");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
@ -169,6 +173,7 @@ lazy_static! {
|
|||||||
(limit_cpi_loader_invoke::id(), "Loader not authorized via CPI"),
|
(limit_cpi_loader_invoke::id(), "Loader not authorized via CPI"),
|
||||||
(use_loaded_program_accounts::id(), "Use loaded program accounts"),
|
(use_loaded_program_accounts::id(), "Use loaded program accounts"),
|
||||||
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
|
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
|
||||||
|
(use_loaded_executables::id(), "Use loaded executable accounts"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
Reference in New Issue
Block a user