Translate data length and owner as writable (#13914)

This commit is contained in:
Jack May 2020-12-02 09:05:42 -08:00 committed by GitHub
parent f751a5d4e2
commit 85bec37be4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 327 additions and 6 deletions

View File

@ -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"

View File

@ -58,6 +58,7 @@ members = [
"rust/param_passing_dep",
"rust/rand",
"rust/ristretto",
"rust/ro_modify",
"rust/sanity",
"rust/sha256",
"rust/spoof1",

View File

@ -79,6 +79,7 @@ fn main() {
"param_passing",
"rand",
"ristretto",
"ro_modify",
"sanity",
"sha256",
"spoof1",

View 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"]

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View 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
}

View File

@ -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() {

View File

@ -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>(