Translate data length and owner as writable (#13914)
This commit is contained in:
parent
f751a5d4e2
commit
85bec37be4
|
@ -2067,6 +2067,13 @@ dependencies = [
|
|||
"solana-program",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-ro-modify"
|
||||
version = "1.5.0"
|
||||
dependencies = [
|
||||
"solana-program",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-sanity"
|
||||
version = "1.5.0"
|
||||
|
|
|
@ -58,6 +58,7 @@ members = [
|
|||
"rust/param_passing_dep",
|
||||
"rust/rand",
|
||||
"rust/ristretto",
|
||||
"rust/ro_modify",
|
||||
"rust/sanity",
|
||||
"rust/sha256",
|
||||
"rust/spoof1",
|
||||
|
|
|
@ -79,6 +79,7 @@ fn main() {
|
|||
"param_passing",
|
||||
"rand",
|
||||
"ristretto",
|
||||
"ro_modify",
|
||||
"sanity",
|
||||
"sha256",
|
||||
"spoof1",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "solana-bpf-rust-ro-modify"
|
||||
version = "1.5.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "1.5.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,222 @@
|
|||
use solana_program::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, program::invoke,
|
||||
program_error::ProgramError, pubkey::Pubkey, system_instruction,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SolInstruction {
|
||||
program_id_addr: u64,
|
||||
accounts_addr: u64,
|
||||
accounts_len: usize,
|
||||
data_addr: u64,
|
||||
data_len: usize,
|
||||
}
|
||||
|
||||
/// Rust representation of C's SolAccountMeta
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SolAccountMeta {
|
||||
pubkey_addr: u64,
|
||||
is_writable: bool,
|
||||
is_signer: bool,
|
||||
}
|
||||
|
||||
/// Rust representation of C's SolAccountInfo
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
struct SolAccountInfo {
|
||||
key_addr: u64,
|
||||
lamports_addr: u64,
|
||||
data_len: u64,
|
||||
data_addr: u64,
|
||||
owner_addr: u64,
|
||||
rent_epoch: u64,
|
||||
is_signer: bool,
|
||||
is_writable: bool,
|
||||
executable: bool,
|
||||
}
|
||||
|
||||
/// Rust representation of C's SolSignerSeed
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SolSignerSeedC {
|
||||
addr: u64,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
/// Rust representation of C's SolSignerSeeds
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct SolSignerSeedsC {
|
||||
addr: u64,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn sol_invoke_signed_c(
|
||||
instruction_addr: *const SolInstruction,
|
||||
account_infos_addr: *const SolAccountInfo,
|
||||
account_infos_len: u64,
|
||||
signers_seeds_addr: *const SolSignerSeedsC,
|
||||
signers_seeds_len: u64,
|
||||
) -> u64;
|
||||
}
|
||||
|
||||
const READONLY_ACCOUNTS: &[SolAccountInfo] = &[
|
||||
SolAccountInfo {
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
executable: true,
|
||||
key_addr: 0x400000010,
|
||||
owner_addr: 0x400000030,
|
||||
lamports_addr: 0x400000050,
|
||||
rent_epoch: 0,
|
||||
data_addr: 0x400000060,
|
||||
data_len: 14,
|
||||
},
|
||||
SolAccountInfo {
|
||||
is_signer: true,
|
||||
is_writable: true,
|
||||
executable: false,
|
||||
key_addr: 0x400002880,
|
||||
owner_addr: 0x4000028A0,
|
||||
lamports_addr: 0x4000028c0,
|
||||
rent_epoch: 0,
|
||||
data_addr: 0x4000028d0,
|
||||
data_len: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const PUBKEY: Pubkey = Pubkey::new_from_array([
|
||||
0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
]);
|
||||
|
||||
fn check_preconditions(
|
||||
in_infos: &[AccountInfo],
|
||||
static_infos: &[SolAccountInfo],
|
||||
) -> Result<(), ProgramError> {
|
||||
for (in_info, static_info) in in_infos.iter().zip(static_infos) {
|
||||
check!(in_info.key.as_ref().as_ptr() as u64, static_info.key_addr);
|
||||
check!(
|
||||
in_info.owner.as_ref().as_ptr() as u64,
|
||||
static_info.owner_addr
|
||||
);
|
||||
check!(
|
||||
unsafe { *in_info.lamports.as_ptr() as *const u64 as u64 },
|
||||
static_info.lamports_addr
|
||||
);
|
||||
check!(
|
||||
in_info.try_borrow_data()?.as_ptr() as u64,
|
||||
static_info.data_addr
|
||||
);
|
||||
check!(in_info.data_len() as u64, static_info.data_len);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
check_preconditions(accounts, READONLY_ACCOUNTS)?;
|
||||
|
||||
match instruction_data[0] {
|
||||
1 => {
|
||||
let system_instruction = system_instruction::allocate(accounts[1].key, 42);
|
||||
let metas = &[SolAccountMeta {
|
||||
is_signer: true,
|
||||
is_writable: true,
|
||||
pubkey_addr: accounts[1].key as *const _ as u64,
|
||||
}];
|
||||
let instruction = SolInstruction {
|
||||
accounts_addr: metas.as_ptr() as u64,
|
||||
accounts_len: metas.len(),
|
||||
data_addr: system_instruction.data.as_ptr() as u64,
|
||||
data_len: system_instruction.data.len(),
|
||||
program_id_addr: accounts[0].key as *const Pubkey as u64,
|
||||
};
|
||||
unsafe {
|
||||
check!(
|
||||
0,
|
||||
sol_invoke_signed_c(
|
||||
&instruction as *const _,
|
||||
READONLY_ACCOUNTS.as_ptr(),
|
||||
READONLY_ACCOUNTS.len() as u64,
|
||||
std::ptr::null::<SolSignerSeedsC>(),
|
||||
0,
|
||||
)
|
||||
);
|
||||
}
|
||||
let ptr = &READONLY_ACCOUNTS[1].data_len as *const _ as u64 as *mut u64;
|
||||
check!(42, unsafe { read_val(ptr) });
|
||||
}
|
||||
2 => {
|
||||
// Not sure how to get a const data length in an Rc<RefCell<&mut [u8]>>
|
||||
}
|
||||
3 => {
|
||||
let mut new_accounts =
|
||||
&mut [READONLY_ACCOUNTS[0].clone(), READONLY_ACCOUNTS[1].clone()];
|
||||
new_accounts[1].owner_addr = &PUBKEY as *const _ as u64;
|
||||
let system_instruction = system_instruction::assign(accounts[1].key, program_id);
|
||||
let metas = &[SolAccountMeta {
|
||||
is_signer: true,
|
||||
is_writable: true,
|
||||
pubkey_addr: accounts[1].key as *const _ as u64,
|
||||
}];
|
||||
let instruction = SolInstruction {
|
||||
accounts_addr: metas.as_ptr() as u64,
|
||||
accounts_len: metas.len(),
|
||||
data_addr: system_instruction.data.as_ptr() as u64,
|
||||
data_len: system_instruction.data.len(),
|
||||
program_id_addr: accounts[0].key as *const Pubkey as u64,
|
||||
};
|
||||
unsafe {
|
||||
check!(
|
||||
0,
|
||||
sol_invoke_signed_c(
|
||||
&instruction as *const _,
|
||||
new_accounts.as_ptr(),
|
||||
new_accounts.len() as u64,
|
||||
std::ptr::null::<SolSignerSeedsC>(),
|
||||
0,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
4 => {
|
||||
let mut new_account = accounts[1].clone();
|
||||
new_account.owner = &PUBKEY;
|
||||
let instruction = system_instruction::assign(accounts[1].key, program_id);
|
||||
invoke(&instruction, &[accounts[0].clone(), new_account])?;
|
||||
}
|
||||
_ => check!(0, 1),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! check {
|
||||
($left:expr, $right:expr) => {
|
||||
if $left != $right {
|
||||
msg!(
|
||||
"Condition failure: {:?} != {:?} at line {:?}",
|
||||
$left,
|
||||
$right,
|
||||
line!()
|
||||
);
|
||||
return Err(ProgramError::Custom(0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Skirt the compiler and force a read from a const value
|
||||
/// # Safety
|
||||
#[inline(never)]
|
||||
pub unsafe fn read_val<T: Copy>(ptr: *mut T) -> T {
|
||||
*ptr
|
||||
}
|
|
@ -835,6 +835,65 @@ fn test_program_bpf_invoke() {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
#[test]
|
||||
fn test_program_bpf_ro_modify() {
|
||||
solana_logger::setup();
|
||||
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(50);
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
let (name, id, entrypoint) = solana_bpf_loader_program!();
|
||||
bank.add_builtin(&name, id, entrypoint);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
let program_pubkey = load_bpf_program(
|
||||
&bank_client,
|
||||
&bpf_loader::id(),
|
||||
&mint_keypair,
|
||||
"solana_bpf_rust_ro_modify",
|
||||
);
|
||||
|
||||
let test_keypair = Keypair::new();
|
||||
let account = Account::new(10, 0, &solana_sdk::system_program::id());
|
||||
bank.store_account(&test_keypair.pubkey(), &account);
|
||||
|
||||
let account_metas = vec![
|
||||
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
|
||||
AccountMeta::new(test_keypair.pubkey(), true),
|
||||
];
|
||||
|
||||
let instruction = Instruction::new(program_pubkey, &[1_u8], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
|
||||
println!("result {:?}", result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(0xb9f0002))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_pubkey, &[3_u8], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
|
||||
println!("result {:?}", result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(0xb9f0002))
|
||||
);
|
||||
|
||||
let instruction = Instruction::new(program_pubkey, &[4_u8], account_metas.clone());
|
||||
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
|
||||
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(0xb9f0002))
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
#[test]
|
||||
fn test_program_bpf_call_depth() {
|
||||
|
|
|
@ -905,7 +905,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||
};
|
||||
let owner = translate_type_mut::<Pubkey>(
|
||||
memory_mapping,
|
||||
AccessType::Load,
|
||||
AccessType::Store,
|
||||
account_info.owner as *const _ as u64,
|
||||
self.loader_id,
|
||||
)?;
|
||||
|
@ -920,11 +920,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||
let translated = translate(
|
||||
memory_mapping,
|
||||
AccessType::Load,
|
||||
account_info.data.as_ptr() as *const _ as u64,
|
||||
unsafe { (account_info.data.as_ptr() as *const u64).offset(1) as u64 },
|
||||
8,
|
||||
self.loader_id,
|
||||
)? as *mut u64;
|
||||
let ref_to_len_in_vm = unsafe { &mut *translated.offset(1) };
|
||||
let ref_to_len_in_vm = unsafe { &mut *translated };
|
||||
let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) };
|
||||
let serialized_len_ptr = translate_type_mut::<u64>(
|
||||
memory_mapping,
|
||||
|
@ -1168,6 +1168,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||
account_infos_len,
|
||||
self.loader_id,
|
||||
)?;
|
||||
let first_info_addr = &account_infos[0] as *const _ as u64;
|
||||
let mut accounts = Vec::with_capacity(message.account_keys.len());
|
||||
let mut refs = Vec::with_capacity(message.account_keys.len());
|
||||
'root: for account_key in message.account_keys.iter() {
|
||||
|
@ -1187,7 +1188,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||
)?;
|
||||
let owner = translate_type_mut::<Pubkey>(
|
||||
memory_mapping,
|
||||
AccessType::Load,
|
||||
AccessType::Store,
|
||||
account_info.owner_addr,
|
||||
self.loader_id,
|
||||
)?;
|
||||
|
@ -1198,8 +1199,18 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||
account_info.data_len,
|
||||
self.loader_id,
|
||||
)?;
|
||||
let ref_to_len_in_vm =
|
||||
unsafe { &mut *(&account_info.data_len as *const u64 as u64 as *mut u64) };
|
||||
|
||||
let addr = &account_info.data_len as *const u64 as u64;
|
||||
let vm_addr = account_infos_addr + (addr - first_info_addr);
|
||||
let _ = translate(
|
||||
memory_mapping,
|
||||
AccessType::Store,
|
||||
vm_addr,
|
||||
size_of::<u64>() as u64,
|
||||
self.loader_id,
|
||||
)?;
|
||||
let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) };
|
||||
|
||||
let ref_of_len_in_input_buffer =
|
||||
unsafe { (account_info.data_addr as *mut u8).offset(-8) };
|
||||
let serialized_len_ptr = translate_type_mut::<u64>(
|
||||
|
|
Loading…
Reference in New Issue