Align host addresses (#11384)

* Align host addresses

* support new program abi

* update epoch rollout

* Enforce aligned pointers in cross-program invocations
This commit is contained in:
Jack May 2020-08-11 16:11:52 -07:00 committed by GitHub
parent 697a0e2947
commit 9290e561e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 655 additions and 163 deletions

View File

@ -871,6 +871,9 @@ impl TestValidator {
genesis_config
.native_instruction_processors
.push(solana_bpf_loader_program!());
genesis_config
.native_instruction_processors
.push(solana_bpf_loader_deprecated_program!());
genesis_config.rent.lamports_per_byte_year = 1;
genesis_config.rent.exemption_threshold = 1.0;

View File

@ -36,9 +36,8 @@ pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option<Infl
OperatingMode::Stable => match epoch {
// No inflation at epoch 0
0 => Some(Inflation::new_disabled()),
// Inflation starts
// The epoch of Epoch::MAX is a placeholder and is expected to be reduced in
// a future hard fork.
// Inflation starts The epoch of Epoch::MAX is a placeholder and is
// expected to be reduced in a future hard fork.
Epoch::MAX => Some(Inflation::default()),
_ => None,
},
@ -49,38 +48,36 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
match operating_mode {
OperatingMode::Development => {
if epoch == 0 {
// Programs used for testing
Some(vec![
// Enable all Stable programs
solana_bpf_loader_program!(),
solana_bpf_loader_deprecated_program!(),
solana_vest_program!(),
// Programs that are only available in Development mode
solana_budget_program!(),
solana_exchange_program!(),
])
} else if epoch == std::u64::MAX {
// The epoch of std::u64::MAX is a placeholder and is expected
// to be reduced in a future network update.
Some(vec![solana_bpf_loader_program!()])
} else {
None
}
}
OperatingMode::Stable => {
if epoch == std::u64::MAX - 1 {
// The epoch of std::u64::MAX - 1 is a placeholder and is expected to be reduced in
// a future hard fork.
Some(vec![solana_bpf_loader_program!()])
} else if epoch == std::u64::MAX {
// The epoch of std::u64::MAX is a placeholder and is expected to be reduced in a
// future hard fork.
Some(vec![solana_vest_program!()])
if epoch == std::u64::MAX {
// The epoch of std::u64::MAX is a placeholder and is expected
// to be reduced in a future network update.
Some(vec![solana_bpf_loader_program!(), solana_vest_program!()])
} else {
None
}
}
OperatingMode::Preview => {
if epoch == 0 {
Some(vec![solana_bpf_loader_program!()])
} else if epoch == std::u64::MAX {
// The epoch of std::u64::MAX is a placeholder and is expected to be reduced in a
// future hard fork.
Some(vec![solana_vest_program!()])
if epoch == std::u64::MAX {
// The epoch of std::u64::MAX is a placeholder and is expected
// to be reduced in a future network update.
Some(vec![solana_bpf_loader_program!(), solana_vest_program!()])
} else {
None
}
@ -138,7 +135,7 @@ mod tests {
fn test_development_programs() {
assert_eq!(
get_programs(OperatingMode::Development, 0).unwrap().len(),
4
5
);
assert_eq!(get_programs(OperatingMode::Development, 1), None);
}
@ -159,7 +156,6 @@ mod tests {
#[test]
fn test_softlaunch_programs() {
assert_eq!(get_programs(OperatingMode::Stable, 1), None);
assert!(get_programs(OperatingMode::Stable, std::u64::MAX - 1).is_some());
assert!(get_programs(OperatingMode::Stable, std::u64::MAX).is_some());
}
}

View File

@ -705,7 +705,13 @@ fn test_stable_operating_mode() {
}
// Programs that are not available at epoch 0
for program_id in [&solana_sdk::bpf_loader::id(), &solana_vest_program::id()].iter() {
for program_id in [
&solana_sdk::bpf_loader::id(),
&solana_sdk::bpf_loader_deprecated::id(),
&solana_vest_program::id(),
]
.iter()
{
assert_eq!(
(
program_id,

View File

@ -23,6 +23,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_assert(sol_deserialize(input, &params, 4));
SolPubkey bpf_loader_id =
(SolPubkey){.x = {2, 168, 246, 145, 78, 136, 161, 110, 57, 90, 225,
40, 148, 143, 250, 105, 86, 147, 55, 104, 24, 221,
71, 67, 82, 33, 243, 198, 0, 0, 0, 0}};
SolPubkey bpf_loader_deprecated_id =
(SolPubkey){.x = {2, 168, 246, 145, 78, 136, 161, 107, 189, 35, 149,
133, 95, 100, 4, 217, 180, 244, 86, 183, 130, 27,
176, 20, 87, 73, 66, 140, 0, 0, 0, 0}};

View File

@ -0,0 +1,9 @@
use crate::process_instruction;
solana_sdk::declare_loader!(
solana_sdk::bpf_loader_deprecated::ID,
solana_bpf_loader_deprecated_program,
process_instruction,
solana_bpf_loader_program,
deprecated::id
);

View File

@ -1,10 +1,15 @@
pub mod alloc;
pub mod allocator_bump;
pub mod bpf_verifier;
pub mod deprecated;
pub mod serialization;
pub mod syscalls;
use crate::{bpf_verifier::VerifierError, syscalls::SyscallError};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use crate::{
bpf_verifier::VerifierError,
serialization::{deserialize_parameters, serialize_parameters},
syscalls::SyscallError,
};
use num_derive::{FromPrimitive, ToPrimitive};
use solana_rbpf::{
ebpf::{EbpfError, UserDefinedError},
@ -13,7 +18,7 @@ use solana_rbpf::{
};
use solana_sdk::{
account::{is_executable, next_keyed_account, KeyedAccount},
bpf_loader,
bpf_loader, bpf_loader_deprecated,
decode_error::DecodeError,
entrypoint::SUCCESS,
entrypoint_native::InvokeContext,
@ -22,13 +27,13 @@ use solana_sdk::{
program_utils::limited_deserialize,
pubkey::Pubkey,
};
use std::{io::prelude::*, mem};
use thiserror::Error;
solana_sdk::declare_loader!(
solana_sdk::bpf_loader::ID,
solana_bpf_loader_program,
process_instruction
process_instruction,
solana_bpf_loader_program
);
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
@ -86,75 +91,6 @@ pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool,
(false, 0)
}
pub fn serialize_parameters(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
data: &[u8],
) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
.unwrap();
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
v.write_u8(position as u8).unwrap();
} else {
v.write_u8(std::u8::MAX).unwrap();
v.write_u8(keyed_account.signer_key().is_some() as u8)
.unwrap();
v.write_u8(keyed_account.is_writable() as u8).unwrap();
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
.unwrap();
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
.unwrap();
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
v.write_u8(keyed_account.executable()? as u8).unwrap();
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
.unwrap();
}
}
v.write_u64::<LittleEndian>(data.len() as u64).unwrap();
v.write_all(data).unwrap();
v.write_all(program_id.as_ref()).unwrap();
Ok(v)
}
pub fn deserialize_parameters(
keyed_accounts: &[KeyedAccount],
buffer: &[u8],
) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup
if !is_dup {
start += mem::size_of::<u8>(); // is_signer
start += mem::size_of::<u8>(); // is_writable
start += mem::size_of::<Pubkey>(); // pubkey
keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.data
.clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()? // data
+ mem::size_of::<Pubkey>() // owner
+ mem::size_of::<u8>() // executable
+ mem::size_of::<u64>(); // rent_epoch
}
}
Ok(())
}
macro_rules! log{
($logger:ident, $message:expr) => {
if let Ok(mut logger) = $logger.try_borrow_mut() {
@ -176,7 +112,7 @@ pub fn process_instruction(
instruction_data: &[u8],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
debug_assert!(bpf_loader::check_id(program_id));
debug_assert!(bpf_loader::check_id(program_id) || bpf_loader_deprecated::check_id(program_id));
let logger = invoke_context.get_logger();
@ -191,6 +127,7 @@ pub fn process_instruction(
let parameter_accounts = keyed_accounts_iter.as_slice();
let parameter_bytes = serialize_parameters(
program_id,
program.unsigned_key(),
parameter_accounts,
&instruction_data,
@ -236,7 +173,7 @@ pub fn process_instruction(
}
}
}
deserialize_parameters(parameter_accounts, &parameter_bytes)?;
deserialize_parameters(program_id, parameter_accounts, &parameter_bytes)?;
log!(logger, "BPF program {} success", program.unsigned_key());
} else if !keyed_accounts.is_empty() {
match limited_deserialize(instruction_data)? {
@ -349,6 +286,7 @@ mod tests {
// Ensure that we can invoke this macro from the same crate
// where it is defined.
solana_bpf_loader_program!();
solana_bpf_loader_deprecated_program!();
}
#[test]
@ -440,7 +378,7 @@ mod tests {
fn test_bpf_loader_finalize() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
let mut elf = Vec::new();
let rent = Rent::default();
file.read_to_end(&mut elf).unwrap();
@ -506,7 +444,7 @@ mod tests {
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let program_account = Account::new_ref(1, 0, &program_id);
@ -580,6 +518,94 @@ mod tests {
);
}
#[test]
fn test_bpf_loader_serialize_unaligned() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop_unaligned.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let program_account = Account::new_ref(1, 0, &program_id);
program_account.borrow_mut().data = elf;
program_account.borrow_mut().executable = true;
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
// Case: With program and parameter account
let parameter_account = Account::new_ref(1, 0, &program_id);
keyed_accounts.push(KeyedAccount::new(&program_key, false, &parameter_account));
assert_eq!(
Ok(()),
process_instruction(
&bpf_loader_deprecated::id(),
&keyed_accounts,
&[],
&mut MockInvokeContext::default()
)
);
// Case: With duplicate accounts
let duplicate_key = Pubkey::new_rand();
let parameter_account = Account::new_ref(1, 0, &program_id);
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
assert_eq!(
Ok(()),
process_instruction(
&bpf_loader_deprecated::id(),
&keyed_accounts,
&[],
&mut MockInvokeContext::default()
)
);
}
#[test]
fn test_bpf_loader_serialize_aligned() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let program_account = Account::new_ref(1, 0, &program_id);
program_account.borrow_mut().data = elf;
program_account.borrow_mut().executable = true;
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
// Case: With program and parameter account
let parameter_account = Account::new_ref(1, 0, &program_id);
keyed_accounts.push(KeyedAccount::new(&program_key, false, &parameter_account));
assert_eq!(
Ok(()),
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
&mut MockInvokeContext::default()
)
);
// Case: With duplicate accounts
let duplicate_key = Pubkey::new_rand();
let parameter_account = Account::new_ref(1, 0, &program_id);
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
assert_eq!(
Ok(()),
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
&mut MockInvokeContext::default()
)
);
}
/// fuzzing utility function
fn fuzz<F>(
bytes: &[u8],
@ -610,7 +636,7 @@ mod tests {
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();

View File

@ -0,0 +1,410 @@
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use solana_sdk::{
account::KeyedAccount, bpf_loader_deprecated, instruction::InstructionError, pubkey::Pubkey,
};
use std::{
io::prelude::*,
mem::{self, align_of},
};
/// Look for a duplicate account and return its position if found
pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
for (i, account) in accounts.iter().enumerate() {
if account == keyed_account {
return (true, i);
}
}
(false, 0)
}
pub fn serialize_parameters(
loader_id: &Pubkey,
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
data: &[u8],
) -> Result<Vec<u8>, InstructionError> {
if *loader_id == bpf_loader_deprecated::id() {
serialize_parameters_unaligned(program_id, keyed_accounts, data)
} else {
serialize_parameters_aligned(program_id, keyed_accounts, data)
}
}
pub fn deserialize_parameters(
loader_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
buffer: &[u8],
) -> Result<(), InstructionError> {
if *loader_id == bpf_loader_deprecated::id() {
deserialize_parameters_unaligned(keyed_accounts, buffer)
} else {
deserialize_parameters_aligned(keyed_accounts, buffer)
}
}
pub fn serialize_parameters_unaligned(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
.unwrap();
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
v.write_u8(position as u8).unwrap();
} else {
v.write_u8(std::u8::MAX).unwrap();
v.write_u8(keyed_account.signer_key().is_some() as u8)
.unwrap();
v.write_u8(keyed_account.is_writable() as u8).unwrap();
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
.unwrap();
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
.unwrap();
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
v.write_u8(keyed_account.executable()? as u8).unwrap();
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
.unwrap();
}
}
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
.unwrap();
v.write_all(instruction_data).unwrap();
v.write_all(program_id.as_ref()).unwrap();
Ok(v)
}
pub fn deserialize_parameters_unaligned(
keyed_accounts: &[KeyedAccount],
buffer: &[u8],
) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup
if !is_dup {
start += mem::size_of::<u8>(); // is_signer
start += mem::size_of::<u8>(); // is_writable
start += mem::size_of::<Pubkey>(); // pubkey
keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.data
.clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()? // data
+ mem::size_of::<Pubkey>() // owner
+ mem::size_of::<u8>() // executable
+ mem::size_of::<u64>(); // rent_epoch
}
}
Ok(())
}
pub fn serialize_parameters_aligned(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
// TODO use with capacity would be nice, but don't know account data sizes...
let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
.unwrap();
// TODO panic?
if v.as_ptr().align_offset(align_of::<u128>()) != 0 {
panic!();
}
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
v.write_u8(position as u8).unwrap();
v.write_all(&[0u8, 0, 0, 0, 0, 0, 0]).unwrap(); // 7 bytes of padding to make 64-bit aligned
} else {
v.write_u8(std::u8::MAX).unwrap();
v.write_u8(keyed_account.signer_key().is_some() as u8)
.unwrap();
v.write_u8(keyed_account.is_writable() as u8).unwrap();
v.write_u8(keyed_account.executable()? as u8).unwrap();
v.write_all(&[0u8, 0, 0, 0]).unwrap(); // 4 bytes of padding to make 128-bit aligned
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
.unwrap();
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
.unwrap();
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
for _ in 0..16 - (v.len() % 16) {
v.write_u8(0).unwrap(); // 128 bit aligned again
}
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
.unwrap();
}
}
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
.unwrap();
v.write_all(instruction_data).unwrap();
v.write_all(program_id.as_ref()).unwrap();
Ok(v)
}
pub fn deserialize_parameters_aligned(
keyed_accounts: &[KeyedAccount],
buffer: &[u8],
) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup
if !is_dup {
start += mem::size_of::<u8>() // is_signer
+ mem::size_of::<u8>() // is_writable
+ mem::size_of::<u8>() // executable
+ 4 // padding
+ mem::size_of::<Pubkey>() // pubkey
+ mem::size_of::<Pubkey>(); // owner
keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.data
.clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()?; // data
start += 16 - (start % 16); // padding
start += mem::size_of::<u64>(); // rent_epoch
} else {
start += 7; // padding
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::{
account::Account, account_info::AccountInfo, bpf_loader, entrypoint::deserialize,
};
use std::{
cell::RefCell,
mem::size_of,
rc::Rc,
// Hide Result from bindgen gets confused about generics in non-generic type declarations
slice::{from_raw_parts, from_raw_parts_mut},
};
#[test]
fn test_serialize_parameters() {
let program_id = Pubkey::new_rand();
let dup_key = Pubkey::new_rand();
let keys = vec![dup_key, dup_key, Pubkey::new_rand(), Pubkey::new_rand()];
let accounts = [
RefCell::new(Account {
lamports: 1,
data: vec![1u8, 2, 3, 4, 5],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
}),
// dup of first
RefCell::new(Account {
lamports: 1,
data: vec![1u8, 2, 3, 4, 5],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 100,
}),
RefCell::new(Account {
lamports: 2,
data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
owner: bpf_loader::id(),
executable: true,
rent_epoch: 200,
}),
RefCell::new(Account {
lamports: 3,
data: vec![],
owner: bpf_loader::id(),
executable: false,
rent_epoch: 3100,
}),
];
let keyed_accounts: Vec<_> = keys
.iter()
.zip(&accounts)
.map(|(key, account)| KeyedAccount::new(&key, false, &account))
.collect();
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
// check serialize_parameters_aligned
let mut serialized = serialize_parameters(
&bpf_loader::id(),
&program_id,
&keyed_accounts,
&instruction_data,
)
.unwrap();
let (de_program_id, de_accounts, de_instruction_data) =
unsafe { deserialize(&mut serialized[0] as *mut u8) };
assert_eq!(&program_id, de_program_id);
assert_eq!(instruction_data, de_instruction_data);
assert_eq!(
(&de_instruction_data[0] as *const u8).align_offset(align_of::<u128>()),
0
);
for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys.clone()) {
assert_eq!(key, *account_info.key);
let account = account.borrow();
assert_eq!(account.lamports, account_info.lamports());
assert_eq!(&account.data[..], &account_info.data.borrow()[..]);
assert_eq!(&account.owner, account_info.owner);
assert_eq!(account.executable, account_info.executable);
assert_eq!(account.rent_epoch, account_info.rent_epoch);
assert_eq!(
(*account_info.lamports.borrow() as *const u64).align_offset(align_of::<u64>()),
0
);
assert_eq!(
account_info
.data
.borrow()
.as_ptr()
.align_offset(align_of::<u128>()),
0
);
}
// check serialize_parameters_unaligned
let mut serialized = serialize_parameters(
&bpf_loader_deprecated::id(),
&program_id,
&keyed_accounts,
&instruction_data,
)
.unwrap();
let (de_program_id, de_accounts, de_instruction_data) =
unsafe { deserialize_unaligned(&mut serialized[0] as *mut u8) };
assert_eq!(&program_id, de_program_id);
assert_eq!(instruction_data, de_instruction_data);
for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys) {
assert_eq!(key, *account_info.key);
let account = account.borrow();
assert_eq!(account.lamports, account_info.lamports());
assert_eq!(&account.data[..], &account_info.data.borrow()[..]);
assert_eq!(&account.owner, account_info.owner);
assert_eq!(account.executable, account_info.executable);
assert_eq!(account.rent_epoch, account_info.rent_epoch);
}
}
// the old bpf_loader in-program deserializer bpf_loader::id()
#[allow(clippy::type_complexity)]
pub unsafe fn deserialize_unaligned<'a>(
input: *mut u8,
) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
let mut offset: usize = 0;
// number of accounts present
#[allow(clippy::cast_ptr_alignment)]
let num_accounts = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
// account Infos
let mut accounts = Vec::with_capacity(num_accounts);
for _ in 0..num_accounts {
let dup_info = *(input.add(offset) as *const u8);
offset += size_of::<u8>();
if dup_info == std::u8::MAX {
#[allow(clippy::cast_ptr_alignment)]
let is_signer = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let is_writable = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
offset += size_of::<Pubkey>();
#[allow(clippy::cast_ptr_alignment)]
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
offset += size_of::<u64>();
#[allow(clippy::cast_ptr_alignment)]
let data_len = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let data = Rc::new(RefCell::new({
from_raw_parts_mut(input.add(offset), data_len)
}));
offset += data_len;
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
offset += size_of::<Pubkey>();
#[allow(clippy::cast_ptr_alignment)]
let executable = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let rent_epoch = *(input.add(offset) as *const u64);
offset += size_of::<u64>();
accounts.push(AccountInfo {
is_signer,
is_writable,
key,
lamports,
data,
owner,
executable,
rent_epoch,
});
} else {
// duplicate account, clone the original
accounts.push(accounts[dup_info as usize].clone());
}
}
// instruction data
#[allow(clippy::cast_ptr_alignment)]
let instruction_data_len = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
offset += instruction_data_len;
// program Id
let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
(program_id, accounts, instruction_data)
}
}

View File

@ -50,6 +50,8 @@ pub enum SyscallError {
InstructionError(InstructionError),
#[error("Cross-program invocation with unauthorized signer or writable account")]
PrivilegeEscalation,
#[error("Unaligned pointer")]
UnalignedPointer,
}
impl From<SyscallError> for EbpfError<BPFError> {
fn from(error: SyscallError) -> Self {
@ -141,20 +143,24 @@ macro_rules! translate {
#[macro_export]
macro_rules! translate_type_mut {
($t:ty, $vm_addr:expr, $regions:expr) => {
unsafe {
match translate_addr::<BPFError>(
$vm_addr as u64,
size_of::<$t>(),
file!(),
line!() as usize - ELF_INSN_DUMP_OFFSET + 1,
$regions,
) {
Ok(value) => Ok(&mut *(value as *mut $t)),
Err(e) => Err(e),
($t:ty, $vm_addr:expr, $regions:expr) => {{
if ($vm_addr as u64 as *mut $t).align_offset(align_of::<$t>()) != 0 {
Err(SyscallError::UnalignedPointer.into())
} else {
unsafe {
match translate_addr::<BPFError>(
$vm_addr as u64,
size_of::<$t>(),
file!(),
line!() as usize - ELF_INSN_DUMP_OFFSET + 1,
$regions,
) {
Ok(value) => Ok(&mut *(value as *mut $t)),
Err(e) => Err(e),
}
}
}
};
}};
}
#[macro_export]
macro_rules! translate_type {
@ -168,18 +174,22 @@ macro_rules! translate_type {
#[macro_export]
macro_rules! translate_slice_mut {
($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => {
match translate_addr::<BPFError>(
$vm_addr as u64,
$len as usize * size_of::<$t>(),
file!(),
line!() as usize - ELF_INSN_DUMP_OFFSET + 1,
$regions,
) {
Ok(value) => Ok(unsafe { from_raw_parts_mut(value as *mut $t, $len as usize) }),
Err(e) => Err(e),
($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => {{
if ($vm_addr as u64 as *mut $t).align_offset(align_of::<$t>()) != 0 {
Err(SyscallError::UnalignedPointer.into())
} else {
match translate_addr::<BPFError>(
$vm_addr as u64,
$len as usize * size_of::<$t>(),
file!(),
line!() as usize - ELF_INSN_DUMP_OFFSET + 1,
$regions,
) {
Ok(value) => Ok(unsafe { from_raw_parts_mut(value as *mut $t, $len as usize) }),
Err(e) => Err(e),
}
}
};
}};
}
#[macro_export]
macro_rules! translate_slice {
@ -456,7 +466,7 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
let lamports_ref = {
// Double translate lamports out of RefCell
let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_regions)?;
translate_type_mut!(u64, *(ptr as *const u64), rw_regions)?
translate_type_mut!(u64, *ptr, rw_regions)?
};
let data = {
// Double translate data out of RefCell
@ -918,13 +928,17 @@ mod tests {
vec![AccountMeta::new(Pubkey::new_rand(), false)],
);
let addr = &instruction as *const _ as u64;
let regions = vec![MemoryRegion {
let mut regions = vec![MemoryRegion {
addr_host: addr,
addr_vm: 100,
addr_vm: 96,
len: std::mem::size_of::<Instruction>() as u64,
}];
let translated_instruction = translate_type!(Instruction, 100, &regions).unwrap();
let translated_instruction = translate_type!(Instruction, 96, &regions).unwrap();
assert_eq!(instruction, *translated_instruction);
regions[0].len = 1;
assert!(translate_type!(Instruction, 100, &regions).is_err());
regions[0].len = std::mem::size_of::<Instruction>() as u64 * 2;
assert!(translate_type!(Instruction, 100, &regions).is_err());
}
#[test]

Binary file not shown.

View File

@ -289,12 +289,15 @@ static bool sol_deserialize(
if (dup_info == UINT8_MAX) {
input += sizeof(uint8_t);
input += sizeof(uint8_t);
input += sizeof(SolPubkey);
input += sizeof(uint64_t);
input += *(uint64_t *) input;
input += sizeof(uint64_t);
input += sizeof(SolPubkey);
input += sizeof(uint8_t);
input += 4; // padding
input += sizeof(SolPubkey);
input += sizeof(SolPubkey);
input += sizeof(uint64_t);
uint64_t data_len = *(uint64_t *) input;
input += sizeof(uint64_t);
input += data_len;
input += 16 - (data_len % 16); // padding
input += sizeof(uint64_t);
}
continue;
@ -308,10 +311,20 @@ static bool sol_deserialize(
params->ka[i].is_writable = *(uint8_t *) input != 0;
input += sizeof(uint8_t);
// executable?
params->ka[i].executable = *(uint8_t *) input;
input += sizeof(uint8_t);
input += 4; // padding
// key
params->ka[i].key = (SolPubkey *) input;
input += sizeof(SolPubkey);
// owner
params->ka[i].owner = (SolPubkey *) input;
input += sizeof(SolPubkey);
// lamports
params->ka[i].lamports = (uint64_t *) input;
input += sizeof(uint64_t);
@ -322,26 +335,22 @@ static bool sol_deserialize(
params->ka[i].data = (uint8_t *) input;
input += params->ka[i].data_len;
// owner
params->ka[i].owner = (SolPubkey *) input;
input += sizeof(SolPubkey);
// executable?
params->ka[i].executable = *(uint8_t *) input;
input += sizeof(uint8_t);
input += 16 - (params->ka[i].data_len % 16); // padding
// rent epoch
params->ka[i].rent_epoch = *(uint64_t *) input;
input += sizeof(uint64_t);
} else {
params->ka[i].is_signer = params->ka[dup_info].is_signer;
params->ka[i].is_writable = params->ka[dup_info].is_writable;
params->ka[i].executable = params->ka[dup_info].executable;
params->ka[i].key = params->ka[dup_info].key;
params->ka[i].owner = params->ka[dup_info].owner;
params->ka[i].lamports = params->ka[dup_info].lamports;
params->ka[i].data_len = params->ka[dup_info].data_len;
params->ka[i].data = params->ka[dup_info].data;
params->ka[i].owner = params->ka[dup_info].owner;
params->ka[i].executable = params->ka[dup_info].executable;
params->ka[i].rent_epoch = params->ka[dup_info].rent_epoch;
input += 7; // padding
}
}

View File

@ -1 +1 @@
crate::declare_id!("BPFLoader1111111111111111111111111111111111");
crate::declare_id!("BPFLoader2111111111111111111111111111111111");

View File

@ -0,0 +1 @@
crate::declare_id!("BPFLoader1111111111111111111111111111111111");

View File

@ -76,9 +76,18 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
let is_writable = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
#[allow(clippy::cast_ptr_alignment)]
let executable = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
offset += 4; // padding
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
offset += size_of::<Pubkey>();
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
offset += size_of::<Pubkey>();
#[allow(clippy::cast_ptr_alignment)]
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
offset += size_of::<u64>();
@ -92,12 +101,7 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
}));
offset += data_len;
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
offset += size_of::<Pubkey>();
#[allow(clippy::cast_ptr_alignment)]
let executable = *(input.add(offset) as *const u8) != 0;
offset += size_of::<u8>();
offset += 16 - (offset % 16); // padding
#[allow(clippy::cast_ptr_alignment)]
let rent_epoch = *(input.add(offset) as *const u64);
@ -114,6 +118,8 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
rent_epoch,
});
} else {
offset += 7; // padding
// Duplicate account, clone the original
accounts.push(accounts[dup_info as usize].clone());
}

View File

@ -33,7 +33,7 @@ pub type LoaderEntrypoint = unsafe extern "C" fn(
#[rustversion::since(1.46.0)]
#[macro_export]
macro_rules! declare_name {
($name:ident) => {
($name:ident, $filename:ident, $id:path) => {
#[macro_export]
macro_rules! $name {
() => {
@ -66,8 +66,8 @@ macro_rules! declare_name {
// `respan!` respans the path `$crate::id`, which we then call (hence the extra
// parens)
(
stringify!($name).to_string(),
::solana_sdk::respan!($crate::id, $name)(),
stringify!($filename).to_string(),
::solana_sdk::respan!($crate::$id, $name)(),
)
};
}
@ -77,11 +77,11 @@ macro_rules! declare_name {
#[rustversion::not(since(1.46.0))]
#[macro_export]
macro_rules! declare_name {
($name:ident) => {
($name:ident, $filename:ident, $id:path) => {
#[macro_export]
macro_rules! $name {
() => {
(stringify!($name).to_string(), $crate::id())
(stringify!($filename).to_string(), $crate::$id())
};
}
};
@ -90,8 +90,10 @@ macro_rules! declare_name {
/// Convenience macro to declare a native program
///
/// bs58_string: bs58 string representation the program's id
/// name: Name of the program, must match the library name in Cargo.toml
/// name: Name of the program
/// filename: must match the library name in Cargo.toml
/// entrypoint: Program's entrypoint, must be of `type Entrypoint`
/// id: Path to the program id access function, used if not called in `src/lib`
///
/// # Examples
///
@ -159,7 +161,7 @@ macro_rules! declare_name {
macro_rules! declare_program(
($bs58_string:expr, $name:ident, $entrypoint:expr) => (
$crate::declare_id!($bs58_string);
$crate::declare_name!($name);
$crate::declare_name!($name, $name, id);
#[no_mangle]
pub extern "C" fn $name(
@ -174,12 +176,16 @@ macro_rules! declare_program(
/// Same as declare_program but for native loaders
#[macro_export]
macro_rules! declare_loader(
($bs58_string:expr, $name:ident, $entrypoint:expr) => (
macro_rules! declare_loader {
($bs58_string:expr, $name:ident, $entrypoint:expr) => {
$crate::declare_loader!($bs58_string, $name, $entrypoint, $name, id);
};
($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident) => {
$crate::declare_loader!($bs58_string, $name, $entrypoint, $filename, id);
};
($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident, $id:path) => {
$crate::declare_id!($bs58_string);
$crate::declare_name!($name);
$crate::declare_name!($name, $filename, $id);
#[no_mangle]
pub extern "C" fn $name(
@ -190,8 +196,8 @@ macro_rules! declare_loader(
) -> Result<(), $crate::instruction::InstructionError> {
$entrypoint(program_id, keyed_accounts, instruction_data, invoke_context)
}
)
);
};
}
pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>;

View File

@ -12,6 +12,7 @@ pub mod abi_example;
pub mod account;
pub mod account_utils;
pub mod bpf_loader;
pub mod bpf_loader_deprecated;
pub mod clock;
pub mod commitment_config;
pub mod decode_error;