2019-05-24 16:21:42 -07:00
|
|
|
pub mod alloc;
|
|
|
|
pub mod allocator_bump;
|
2018-10-16 16:33:31 -07:00
|
|
|
pub mod bpf_verifier;
|
2020-04-30 01:43:11 -07:00
|
|
|
pub mod syscalls;
|
2018-10-16 16:33:31 -07:00
|
|
|
|
2020-04-30 01:43:11 -07:00
|
|
|
use crate::{bpf_verifier::VerifierError, syscalls::SyscallError};
|
2018-10-16 09:43:49 -07:00
|
|
|
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
2020-02-14 13:59:03 -08:00
|
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
2020-03-26 14:00:26 -07:00
|
|
|
use solana_rbpf::{
|
|
|
|
ebpf::{EbpfError, UserDefinedError},
|
|
|
|
memory_region::MemoryRegion,
|
|
|
|
EbpfVm,
|
|
|
|
};
|
2020-01-02 18:18:56 -08:00
|
|
|
use solana_sdk::{
|
2020-06-17 10:39:14 -07:00
|
|
|
account::{is_executable, next_keyed_account, KeyedAccount},
|
2020-03-05 10:57:35 -08:00
|
|
|
bpf_loader,
|
2020-06-17 10:39:14 -07:00
|
|
|
decode_error::DecodeError,
|
2020-01-31 10:58:07 -08:00
|
|
|
entrypoint::SUCCESS,
|
2020-04-28 14:33:56 -07:00
|
|
|
entrypoint_native::InvokeContext,
|
2020-01-02 18:18:56 -08:00
|
|
|
instruction::InstructionError,
|
|
|
|
loader_instruction::LoaderInstruction,
|
2020-06-17 10:39:14 -07:00
|
|
|
program_utils::limited_deserialize,
|
2020-01-02 18:18:56 -08:00
|
|
|
pubkey::Pubkey,
|
|
|
|
};
|
2020-03-26 14:00:26 -07:00
|
|
|
use std::{io::prelude::*, mem};
|
2020-02-14 13:59:03 -08:00
|
|
|
use thiserror::Error;
|
2019-05-24 16:21:42 -07:00
|
|
|
|
2020-04-15 09:41:29 -07:00
|
|
|
solana_sdk::declare_loader!(
|
2019-12-03 17:55:18 -08:00
|
|
|
solana_sdk::bpf_loader::ID,
|
2019-11-20 16:32:19 -08:00
|
|
|
solana_bpf_loader_program,
|
|
|
|
process_instruction
|
|
|
|
);
|
|
|
|
|
2020-02-14 13:59:03 -08:00
|
|
|
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
|
|
|
pub enum BPFLoaderError {
|
2020-03-26 14:00:26 -07:00
|
|
|
#[error("failed to create virtual machine")]
|
|
|
|
VirtualMachineCreationFailed = 0x0b9f_0001,
|
|
|
|
#[error("virtual machine failed to run the program to completion")]
|
|
|
|
VirtualMachineFailedToRunProgram = 0x0b9f_0002,
|
2020-02-14 13:59:03 -08:00
|
|
|
}
|
|
|
|
impl<E> DecodeError<E> for BPFLoaderError {
|
|
|
|
fn type_of() -> &'static str {
|
|
|
|
"BPFLoaderError"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 14:00:26 -07:00
|
|
|
/// Errors returned by functions the BPF Loader registers with the vM
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum BPFError {
|
|
|
|
#[error("{0}")]
|
|
|
|
VerifierError(#[from] VerifierError),
|
|
|
|
#[error("{0}")]
|
2020-04-30 01:43:11 -07:00
|
|
|
SyscallError(#[from] SyscallError),
|
2020-03-26 14:00:26 -07:00
|
|
|
}
|
|
|
|
impl UserDefinedError for BPFError {}
|
|
|
|
|
2020-04-28 14:33:56 -07:00
|
|
|
pub fn create_vm<'a>(
|
|
|
|
prog: &'a [u8],
|
2020-05-26 01:02:31 -07:00
|
|
|
parameter_accounts: &'a [KeyedAccount<'a>],
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context: &'a mut dyn InvokeContext,
|
|
|
|
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
|
2019-08-23 11:03:53 -07:00
|
|
|
let mut vm = EbpfVm::new(None)?;
|
2018-10-26 19:38:07 -07:00
|
|
|
vm.set_verifier(bpf_verifier::check)?;
|
2019-10-02 10:07:44 -07:00
|
|
|
vm.set_max_instruction_count(100_000)?;
|
2018-11-06 14:28:46 -08:00
|
|
|
vm.set_elf(&prog)?;
|
2019-05-24 16:21:42 -07:00
|
|
|
|
2020-05-26 01:02:31 -07:00
|
|
|
let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?;
|
2019-05-24 16:21:42 -07:00
|
|
|
|
|
|
|
Ok((vm, heap_region))
|
2018-10-26 19:38:07 -07:00
|
|
|
}
|
|
|
|
|
2020-03-26 14:00:26 -07:00
|
|
|
pub fn check_elf(prog: &[u8]) -> Result<(), EbpfError<BPFError>> {
|
2019-12-04 12:03:29 -08:00
|
|
|
let mut vm = EbpfVm::new(None)?;
|
|
|
|
vm.set_verifier(bpf_verifier::check)?;
|
|
|
|
vm.set_elf(&prog)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-01-24 10:54:26 -08:00
|
|
|
/// 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)
|
|
|
|
}
|
|
|
|
|
2020-01-02 18:18:56 -08:00
|
|
|
pub fn serialize_parameters(
|
2018-11-17 17:02:14 -08:00
|
|
|
program_id: &Pubkey,
|
2020-01-22 17:54:06 -08:00
|
|
|
keyed_accounts: &[KeyedAccount],
|
2018-11-13 19:54:41 -08:00
|
|
|
data: &[u8],
|
2020-01-22 09:11:56 -08:00
|
|
|
) -> Result<Vec<u8>, InstructionError> {
|
2018-10-16 09:43:49 -07:00
|
|
|
assert_eq!(32, mem::size_of::<Pubkey>());
|
|
|
|
|
|
|
|
let mut v: Vec<u8> = Vec::new();
|
|
|
|
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
|
|
|
.unwrap();
|
2020-01-24 10:54:26 -08:00
|
|
|
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 {
|
2020-02-11 10:03:28 -08:00
|
|
|
v.write_u8(std::u8::MAX).unwrap();
|
2020-02-10 21:33:29 -08:00
|
|
|
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
2020-01-24 10:54:26 -08:00
|
|
|
.unwrap();
|
2020-02-10 21:33:29 -08:00
|
|
|
v.write_u8(keyed_account.is_writable() as u8).unwrap();
|
2020-01-24 10:54:26 -08:00
|
|
|
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();
|
2020-03-04 10:52:09 -08:00
|
|
|
v.write_u8(keyed_account.executable()? as u8).unwrap();
|
|
|
|
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
|
|
|
.unwrap();
|
2020-01-24 10:54:26 -08:00
|
|
|
}
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
v.write_u64::<LittleEndian>(data.len() as u64).unwrap();
|
|
|
|
v.write_all(data).unwrap();
|
2018-11-17 17:02:14 -08:00
|
|
|
v.write_all(program_id.as_ref()).unwrap();
|
2020-01-22 09:11:56 -08:00
|
|
|
Ok(v)
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
pub fn deserialize_parameters(
|
2020-01-22 17:54:06 -08:00
|
|
|
keyed_accounts: &[KeyedAccount],
|
2020-01-22 09:11:56 -08:00
|
|
|
buffer: &[u8],
|
|
|
|
) -> Result<(), InstructionError> {
|
2018-10-16 09:43:49 -07:00
|
|
|
assert_eq!(32, mem::size_of::<Pubkey>());
|
|
|
|
|
2020-01-24 10:54:26 -08:00
|
|
|
let mut start = mem::size_of::<u64>(); // number of accounts
|
2020-01-29 21:49:42 -08:00
|
|
|
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 {
|
2020-02-10 21:33:29 -08:00
|
|
|
start += mem::size_of::<u8>(); // is_signer
|
|
|
|
start += mem::size_of::<u8>(); // is_writable
|
2020-01-24 10:54:26 -08:00
|
|
|
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()?;
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_account
|
|
|
|
.try_account_ref_mut()?
|
|
|
|
.data
|
2020-01-24 10:54:26 -08:00
|
|
|
.clone_from_slice(&buffer[start..end]);
|
|
|
|
start += keyed_account.data_len()? // data
|
2020-03-04 10:52:09 -08:00
|
|
|
+ mem::size_of::<Pubkey>() // owner
|
|
|
|
+ mem::size_of::<u8>() // executable
|
|
|
|
+ mem::size_of::<u64>(); // rent_epoch
|
2020-01-22 09:11:56 -08:00
|
|
|
}
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
2020-01-22 09:11:56 -08:00
|
|
|
Ok(())
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
macro_rules! log{
|
|
|
|
($logger:ident, $message:expr) => {
|
|
|
|
if let Ok(mut logger) = $logger.try_borrow_mut() {
|
|
|
|
if logger.log_enabled() {
|
|
|
|
logger.log($message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
($logger:ident, $fmt:expr, $($arg:tt)*) => {
|
|
|
|
if let Ok(mut logger) = $logger.try_borrow_mut() {
|
|
|
|
logger.log(&format!($fmt, $($arg)*));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-06-12 08:49:59 -07:00
|
|
|
pub fn process_instruction(
|
2018-11-17 17:02:14 -08:00
|
|
|
program_id: &Pubkey,
|
2020-01-22 17:54:06 -08:00
|
|
|
keyed_accounts: &[KeyedAccount],
|
2020-01-10 13:20:15 -08:00
|
|
|
instruction_data: &[u8],
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2019-03-18 09:05:03 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-03-05 10:57:35 -08:00
|
|
|
debug_assert!(bpf_loader::check_id(program_id));
|
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
|
2020-01-10 13:20:15 -08:00
|
|
|
if keyed_accounts.is_empty() {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "No account keys");
|
2020-01-10 13:20:15 -08:00
|
|
|
return Err(InstructionError::NotEnoughAccountKeys);
|
|
|
|
}
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
if is_executable(keyed_accounts)? {
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
2020-01-10 13:20:15 -08:00
|
|
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
2020-03-17 12:06:15 -07:00
|
|
|
|
2020-01-22 17:54:06 -08:00
|
|
|
let parameter_accounts = keyed_accounts_iter.as_slice();
|
2020-03-05 10:57:35 -08:00
|
|
|
let parameter_bytes = serialize_parameters(
|
|
|
|
program.unsigned_key(),
|
|
|
|
parameter_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
)?;
|
2020-03-17 12:06:15 -07:00
|
|
|
{
|
|
|
|
let program_account = program.try_account_ref_mut()?;
|
2020-05-26 01:02:31 -07:00
|
|
|
let (mut vm, heap_region) =
|
|
|
|
match create_vm(&program_account.data, ¶meter_accounts, invoke_context) {
|
|
|
|
Ok(info) => info,
|
|
|
|
Err(e) => {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "Failed to create BPF VM: {}", e);
|
2020-05-26 01:02:31 -07:00
|
|
|
return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
|
|
|
|
}
|
|
|
|
};
|
2020-03-17 12:06:15 -07:00
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "Call BPF program {}", program.unsigned_key());
|
2020-03-17 12:06:15 -07:00
|
|
|
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
|
|
|
|
Ok(status) => {
|
|
|
|
if status != SUCCESS {
|
|
|
|
let error: InstructionError = status.into();
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(
|
|
|
|
logger,
|
|
|
|
"BPF program {} failed: {}",
|
|
|
|
program.unsigned_key(),
|
|
|
|
error
|
|
|
|
);
|
2020-03-17 12:06:15 -07:00
|
|
|
return Err(error);
|
|
|
|
}
|
|
|
|
}
|
2020-03-26 14:00:26 -07:00
|
|
|
Err(error) => {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(
|
|
|
|
logger,
|
|
|
|
"BPF program {} failed: {}",
|
|
|
|
program.unsigned_key(),
|
|
|
|
error
|
|
|
|
);
|
2020-03-26 14:00:26 -07:00
|
|
|
return match error {
|
2020-04-30 01:43:11 -07:00
|
|
|
EbpfError::UserError(BPFError::SyscallError(
|
|
|
|
SyscallError::InstructionError(error),
|
2020-03-26 14:00:26 -07:00
|
|
|
)) => Err(error),
|
|
|
|
_ => Err(BPFLoaderError::VirtualMachineFailedToRunProgram.into()),
|
|
|
|
};
|
2020-01-10 13:20:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-22 09:11:56 -08:00
|
|
|
deserialize_parameters(parameter_accounts, ¶meter_bytes)?;
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "BPF program {} success", program.unsigned_key());
|
2020-01-10 13:20:15 -08:00
|
|
|
} else if !keyed_accounts.is_empty() {
|
|
|
|
match limited_deserialize(instruction_data)? {
|
2018-10-16 09:43:49 -07:00
|
|
|
LoaderInstruction::Write { offset, bytes } => {
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
2019-11-08 09:19:19 -08:00
|
|
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
|
|
if program.signer_key().is_none() {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "key[0] did not sign the transaction");
|
2019-11-08 09:19:19 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2019-07-16 09:45:32 -07:00
|
|
|
}
|
2018-10-16 09:43:49 -07:00
|
|
|
let offset = offset as usize;
|
2018-10-19 18:28:38 -07:00
|
|
|
let len = bytes.len();
|
2020-01-22 09:11:56 -08:00
|
|
|
if program.data_len()? < offset + len {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(
|
|
|
|
logger,
|
|
|
|
"Write overflow: {} < {}",
|
|
|
|
program.data_len()?,
|
|
|
|
offset + len
|
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
2020-01-22 09:11:56 -08:00
|
|
|
program.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes);
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
LoaderInstruction::Finalize => {
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
2019-11-08 09:19:19 -08:00
|
|
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
|
|
|
|
|
|
if program.signer_key().is_none() {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "key[0] did not sign the transaction");
|
2019-11-08 09:19:19 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2019-07-16 09:45:32 -07:00
|
|
|
}
|
2019-10-03 14:22:48 -07:00
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
if let Err(e) = check_elf(&program.try_account_ref()?.data) {
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(logger, "{}", e);
|
2019-12-04 12:03:29 -08:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
program.try_account_ref_mut()?.executable = true;
|
2020-06-13 13:20:08 -07:00
|
|
|
log!(
|
|
|
|
logger,
|
|
|
|
"Finalized account {:?}",
|
|
|
|
program.signer_key().unwrap()
|
|
|
|
);
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-03 13:32:31 -08:00
|
|
|
Ok(())
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-05-08 12:37:04 -07:00
|
|
|
use rand::Rng;
|
2020-04-28 14:33:56 -07:00
|
|
|
use solana_sdk::{
|
2020-06-13 13:20:08 -07:00
|
|
|
account::Account,
|
|
|
|
entrypoint_native::{Logger, ProcessInstruction},
|
|
|
|
instruction::CompiledInstruction,
|
|
|
|
message::Message,
|
|
|
|
rent::Rent,
|
2020-04-28 14:33:56 -07:00
|
|
|
};
|
2020-05-08 12:37:04 -07:00
|
|
|
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc};
|
2020-04-28 14:33:56 -07:00
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
2020-05-08 12:24:36 -07:00
|
|
|
pub struct MockInvokeContext {
|
|
|
|
key: Pubkey,
|
2020-06-13 13:20:08 -07:00
|
|
|
mock_logger: MockLogger,
|
2020-05-08 12:24:36 -07:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
impl InvokeContext for MockInvokeContext {
|
|
|
|
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
fn pop(&mut self) {}
|
|
|
|
fn verify_and_update(
|
|
|
|
&mut self,
|
|
|
|
_message: &Message,
|
|
|
|
_instruction: &CompiledInstruction,
|
|
|
|
_accounts: &[Rc<RefCell<Account>>],
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-05-08 12:24:36 -07:00
|
|
|
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
|
|
|
Ok(&self.key)
|
|
|
|
}
|
2020-06-03 12:48:19 -07:00
|
|
|
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
|
|
|
|
&[]
|
|
|
|
}
|
2020-06-13 13:20:08 -07:00
|
|
|
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
|
|
|
|
Rc::new(RefCell::new(self.mock_logger.clone()))
|
|
|
|
}
|
2020-07-29 15:29:52 -07:00
|
|
|
fn is_cross_program_supported(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
2020-06-13 13:20:08 -07:00
|
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
pub struct MockLogger {
|
|
|
|
pub log: Rc<RefCell<Vec<String>>>,
|
|
|
|
}
|
|
|
|
impl Logger for MockLogger {
|
2020-06-06 10:18:28 -07:00
|
|
|
fn log_enabled(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
fn log(&mut self, message: &str) {
|
2020-06-13 13:20:08 -07:00
|
|
|
self.log.borrow_mut().push(message.to_string());
|
2020-06-06 10:18:28 -07:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
2018-11-06 14:28:46 -08:00
|
|
|
|
Fix hygiene issues in `declare_program!` and `declare_loader!`
The `declare_program!` and `declare_loader!` macros both expand to
new macro definitions (based on the `$name` argument). These 'inner'
macros make use of the special `$crate` metavariable to access items in
the crate where the 'inner' macros is defined.
However, this only works due to a bug in rustc. When a macro is
expanded, all `$crate` tokens in its output are 'marked' as being
resolved in the defining crate of that macro. An inner macro (including
the body of its arms) is 'just' another set of tokens that appears in
the body of the outer macro, so any `$crate` identifiers used there are
resolved relative to the 'outer' macro.
For example, consider the following code:
```rust
macro_rules! outer {
() => {
macro_rules! inner {
() => {
$crate::Foo
}
}
}
}
```
The path `$crate::Foo` will be resolved relative to the crate that defines `outer`,
**not** the crate which defines `inner`.
However, rustc currently loses this extra resolution information
(referred to as 'hygiene' information) when a crate is serialized.
In the above example, this means that the macro `inner` (which gets
defined in whatever crate invokes `outer!`) will behave differently
depending on which crate it is invoked from:
When `inner` is invoked from the same crate in which it is defined,
the hygiene information will still be available,
which will cause `$crate::Foo` to be resolved in the crate which defines 'outer'.
When `inner` is invoked from a different crate, it will be loaded from
the metadata of the crate which defines 'inner'. Since the hygiene
information is currently lost, rust will 'forget' that `$crate::Foo` is
supposed to be resolved in the context of 'outer'. Instead, it will be
resolved relative to the crate which defines 'inner', which can cause
incorrect code to compile.
This bug will soon be fixed in rust (see https://github.com/rust-lang/rust/pull/72121),
which will break `declare_program!` and `declare_loader!`. Fortunately,
it's possible to obtain the desired behavior (`$crate` resolving in the
context of the 'inner' macro) by use of a procedural macro.
This commit adds a `respan!` proc-macro to the `sdk/macro` crate.
Using the newly-stabilized (on Nightly) `Span::resolved_at` method,
the `$crate` identifier can be made to be resolved in the context of the
proper crate.
Since `Span::resolved_at` is only stable on the latest nightly,
referencing it on an earlier version of Rust will cause a compilation error.
This requires the `rustversion` crate to be used, which allows conditionally
compiling code epending on the Rust compiler version in use. Since this method is already
stabilized in the latest nightly, there will never be a situation where
the hygiene bug is fixed (e.g. https://github.com/rust-lang/rust/pull/72121)
is merged but we are unable to call `Span::resolved_at`.
2020-06-19 22:42:11 -07:00
|
|
|
#[rustversion::since(1.46.0)]
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_same_crate() {
|
|
|
|
// Ensure that we can invoke this macro from the same crate
|
|
|
|
// where it is defined.
|
|
|
|
solana_bpf_loader_program!();
|
|
|
|
}
|
|
|
|
|
2018-10-31 10:59:56 -07:00
|
|
|
#[test]
|
2020-03-26 14:00:26 -07:00
|
|
|
#[should_panic(expected = "ExceededMaxInstructions(10)")]
|
2019-11-08 09:19:19 -08:00
|
|
|
fn test_bpf_loader_non_terminating_program() {
|
2018-10-31 10:59:56 -07:00
|
|
|
#[rustfmt::skip]
|
2019-11-08 09:19:19 -08:00
|
|
|
let program = &[
|
2019-02-22 16:27:19 -08:00
|
|
|
0x07, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r6 + 1
|
|
|
|
0x05, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, // goto -2
|
2018-10-31 10:59:56 -07:00
|
|
|
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
|
|
|
|
];
|
|
|
|
let input = &mut [0x00];
|
2018-11-06 14:28:46 -08:00
|
|
|
|
2020-03-26 14:00:26 -07:00
|
|
|
let mut vm = EbpfVm::<BPFError>::new(None).unwrap();
|
2018-11-06 14:28:46 -08:00
|
|
|
vm.set_verifier(bpf_verifier::check).unwrap();
|
2019-02-22 16:27:19 -08:00
|
|
|
vm.set_max_instruction_count(10).unwrap();
|
2019-11-08 09:19:19 -08:00
|
|
|
vm.set_program(program).unwrap();
|
2019-05-24 16:21:42 -07:00
|
|
|
vm.execute_program(input, &[], &[]).unwrap();
|
2018-10-31 10:59:56 -07:00
|
|
|
}
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_write() {
|
|
|
|
let program_id = Pubkey::new_rand();
|
|
|
|
let program_key = Pubkey::new_rand();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-10 13:20:15 -08:00
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Write {
|
2019-11-08 09:19:19 -08:00
|
|
|
offset: 3,
|
|
|
|
bytes: vec![1, 2, 3],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write bytes to an offset
|
2020-07-10 13:02:55 -07:00
|
|
|
#[allow(unused_mut)]
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().data = vec![0; 6];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert_eq!(
|
|
|
|
vec![0, 0, 0, 1, 2, 3],
|
|
|
|
keyed_accounts[0].account.borrow().data
|
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Overflow
|
2020-07-10 13:02:55 -07:00
|
|
|
#[allow(unused_mut)]
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().data = vec![0; 5];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_finalize() {
|
|
|
|
let program_id = Pubkey::new_rand();
|
|
|
|
let program_key = Pubkey::new_rand();
|
2019-12-04 12:03:29 -08:00
|
|
|
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
2020-03-31 10:07:38 -07:00
|
|
|
let rent = Rent::default();
|
2019-12-04 12:03:29 -08:00
|
|
|
file.read_to_end(&mut elf).unwrap();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id);
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data = elf;
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-10 13:20:15 -08:00
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Finalize
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert!(keyed_accounts[0].account.borrow().executable);
|
|
|
|
|
|
|
|
program_account.borrow_mut().executable = false; // Un-finalize the account
|
2019-12-04 12:03:29 -08:00
|
|
|
|
|
|
|
// Case: Finalize
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data[0] = 0; // bad elf
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2019-12-04 12:03:29 -08:00
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-12-04 12:03:29 -08:00
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_invoke_main() {
|
|
|
|
let program_id = Pubkey::new_rand();
|
|
|
|
let program_key = Pubkey::new_rand();
|
|
|
|
|
|
|
|
// Create program account
|
|
|
|
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data = elf;
|
|
|
|
program_account.borrow_mut().executable = true;
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Only a program account
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Account not executable
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().executable = false;
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
2020-01-10 13:20:15 -08:00
|
|
|
Err(InstructionError::InvalidInstructionData),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().executable = true;
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: With program and parameter account
|
2020-01-22 17:54:06 -08:00
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
|
|
|
|
// Case: With duplicate accounts
|
|
|
|
let duplicate_key = Pubkey::new_rand();
|
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
2020-02-11 10:03:28 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2020-01-22 09:11:56 -08:00
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
2020-05-08 12:37:04 -07:00
|
|
|
|
|
|
|
/// fuzzing utility function
|
|
|
|
fn fuzz<F>(
|
|
|
|
bytes: &[u8],
|
|
|
|
outer_iters: usize,
|
|
|
|
inner_iters: usize,
|
|
|
|
offset: Range<usize>,
|
|
|
|
value: Range<u8>,
|
|
|
|
work: F,
|
|
|
|
) where
|
|
|
|
F: Fn(&mut [u8]),
|
|
|
|
{
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
for _ in 0..outer_iters {
|
|
|
|
let mut mangled_bytes = bytes.to_vec();
|
|
|
|
for _ in 0..inner_iters {
|
|
|
|
let offset = rng.gen_range(offset.start, offset.end);
|
|
|
|
let value = rng.gen_range(value.start, value.end);
|
|
|
|
mangled_bytes[offset] = value;
|
|
|
|
work(&mut mangled_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_fuzz() {
|
|
|
|
let program_id = Pubkey::new_rand();
|
|
|
|
let program_key = Pubkey::new_rand();
|
|
|
|
|
|
|
|
// Create program account
|
|
|
|
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
// Mangle the whole file
|
2020-05-08 12:37:04 -07:00
|
|
|
fuzz(
|
|
|
|
&elf,
|
|
|
|
1_000_000_000,
|
|
|
|
100,
|
|
|
|
0..elf.len(),
|
|
|
|
0..255,
|
|
|
|
|bytes: &mut [u8]| {
|
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
program_account.borrow_mut().data = bytes.to_vec();
|
|
|
|
program_account.borrow_mut().executable = true;
|
|
|
|
|
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let keyed_accounts = vec![
|
|
|
|
KeyedAccount::new(&program_key, false, &program_account),
|
|
|
|
KeyedAccount::new(&program_key, false, ¶meter_account),
|
|
|
|
];
|
|
|
|
|
|
|
|
let _result = process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-05-08 12:37:04 -07:00
|
|
|
&mut MockInvokeContext::default(),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
}
|