Translate data length and owner as writable (#13914)
This commit is contained in:
7
programs/bpf/Cargo.lock
generated
7
programs/bpf/Cargo.lock
generated
@ -2067,6 +2067,13 @@ dependencies = [
|
|||||||
"solana-program",
|
"solana-program",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solana-bpf-rust-ro-modify"
|
||||||
|
version = "1.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"solana-program",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "solana-bpf-rust-sanity"
|
name = "solana-bpf-rust-sanity"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -58,6 +58,7 @@ members = [
|
|||||||
"rust/param_passing_dep",
|
"rust/param_passing_dep",
|
||||||
"rust/rand",
|
"rust/rand",
|
||||||
"rust/ristretto",
|
"rust/ristretto",
|
||||||
|
"rust/ro_modify",
|
||||||
"rust/sanity",
|
"rust/sanity",
|
||||||
"rust/sha256",
|
"rust/sha256",
|
||||||
"rust/spoof1",
|
"rust/spoof1",
|
||||||
|
@ -79,6 +79,7 @@ fn main() {
|
|||||||
"param_passing",
|
"param_passing",
|
||||||
"rand",
|
"rand",
|
||||||
"ristretto",
|
"ristretto",
|
||||||
|
"ro_modify",
|
||||||
"sanity",
|
"sanity",
|
||||||
"sha256",
|
"sha256",
|
||||||
"spoof1",
|
"spoof1",
|
||||||
|
18
programs/bpf/rust/ro_modify/Cargo.toml
Normal file
18
programs/bpf/rust/ro_modify/Cargo.toml
Normal file
@ -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"]
|
2
programs/bpf/rust/ro_modify/Xargo.toml
Normal file
2
programs/bpf/rust/ro_modify/Xargo.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
222
programs/bpf/rust/ro_modify/src/lib.rs
Normal file
222
programs/bpf/rust/ro_modify/src/lib.rs
Normal file
@ -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")]
|
#[cfg(feature = "bpf_rust")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_program_bpf_call_depth() {
|
fn test_program_bpf_call_depth() {
|
||||||
|
@ -905,7 +905,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
};
|
};
|
||||||
let owner = translate_type_mut::<Pubkey>(
|
let owner = translate_type_mut::<Pubkey>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
AccessType::Load,
|
AccessType::Store,
|
||||||
account_info.owner as *const _ as u64,
|
account_info.owner as *const _ as u64,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
@ -920,11 +920,11 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedRust<'a> {
|
|||||||
let translated = translate(
|
let translated = translate(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
AccessType::Load,
|
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,
|
8,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)? as *mut u64;
|
)? 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 ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) };
|
||||||
let serialized_len_ptr = translate_type_mut::<u64>(
|
let serialized_len_ptr = translate_type_mut::<u64>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
@ -1168,6 +1168,7 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
account_infos_len,
|
account_infos_len,
|
||||||
self.loader_id,
|
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 accounts = Vec::with_capacity(message.account_keys.len());
|
||||||
let mut refs = 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() {
|
'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>(
|
let owner = translate_type_mut::<Pubkey>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
AccessType::Load,
|
AccessType::Store,
|
||||||
account_info.owner_addr,
|
account_info.owner_addr,
|
||||||
self.loader_id,
|
self.loader_id,
|
||||||
)?;
|
)?;
|
||||||
@ -1198,8 +1199,18 @@ impl<'a> SyscallInvokeSigned<'a> for SyscallInvokeSignedC<'a> {
|
|||||||
account_info.data_len,
|
account_info.data_len,
|
||||||
self.loader_id,
|
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 =
|
let ref_of_len_in_input_buffer =
|
||||||
unsafe { (account_info.data_addr as *mut u8).offset(-8) };
|
unsafe { (account_info.data_addr as *mut u8).offset(-8) };
|
||||||
let serialized_len_ptr = translate_type_mut::<u64>(
|
let serialized_len_ptr = translate_type_mut::<u64>(
|
||||||
|
Reference in New Issue
Block a user