Add SyscallStubs to enable syscall interception when building programs for non-BPF

This commit is contained in:
Michael Vines
2020-10-16 22:04:53 -07:00
committed by mergify[bot]
parent 75d62ca095
commit 9c53e1dfb2
9 changed files with 109 additions and 50 deletions

1
Cargo.lock generated
View File

@ -4440,6 +4440,7 @@ dependencies = [
"hex", "hex",
"hmac", "hmac",
"itertools 0.9.0", "itertools 0.9.0",
"lazy_static",
"libsecp256k1", "libsecp256k1",
"log 0.4.8", "log 0.4.8",
"memmap", "memmap",

View File

@ -2121,6 +2121,7 @@ dependencies = [
"hex", "hex",
"hmac", "hmac",
"itertools", "itertools",
"lazy_static",
"libsecp256k1", "libsecp256k1",
"log", "log",
"memmap", "memmap",

View File

@ -42,6 +42,7 @@ generic-array = { version = "0.14.3", default-features = false, features = ["ser
hex = "0.4.2" hex = "0.4.2"
hmac = "0.7.0" hmac = "0.7.0"
itertools = { version = "0.9.0" } itertools = { version = "0.9.0" }
lazy_static = "1.4.0"
log = { version = "0.4.8" } log = { version = "0.4.8" }
memmap = { version = "0.7.0", optional = true } memmap = { version = "0.7.0", optional = true }
num-derive = { version = "0.3" } num-derive = { version = "0.3" }

View File

@ -1,6 +1,6 @@
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
/// Allows customer errors to be decoded back to their original enum /// Allows custom errors to be decoded back to their original enum
pub trait DecodeError<E> { pub trait DecodeError<E> {
fn decode_custom_error_to_enum(custom: u32) -> Option<E> fn decode_custom_error_to_enum(custom: u32) -> Option<E>
where where

View File

@ -84,7 +84,23 @@ pub mod entrypoint_deprecated;
pub mod log; pub mod log;
pub mod program; pub mod program;
pub mod program_error; pub mod program_error;
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
extern crate lazy_static;
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
pub mod program_stubs; pub mod program_stubs;
// Unused `solana_sdk::program_stubs!()` macro retained for source backwards compatibility with v1.3.x programs
#[macro_export]
#[deprecated(
since = "1.4.2",
note = "program_stubs macro is obsolete and can be safely removed"
)]
macro_rules! program_stubs {
() => {};
}
pub mod serialize_utils; pub mod serialize_utils;
// Modules not usable by on-chain programs // Modules not usable by on-chain programs

View File

@ -31,10 +31,16 @@ macro_rules! info {
/// @param message - Message to print /// @param message - Message to print
#[inline] #[inline]
pub fn sol_log(message: &str) { pub fn sol_log(message: &str) {
#[cfg(target_arch = "bpf")]
unsafe { unsafe {
sol_log_(message.as_ptr(), message.len() as u64); sol_log_(message.as_ptr(), message.len() as u64);
} }
#[cfg(not(target_arch = "bpf"))]
crate::program_stubs::sol_log(message);
} }
#[cfg(target_arch = "bpf")]
extern "C" { extern "C" {
fn sol_log_(message: *const u8, len: u64); fn sol_log_(message: *const u8, len: u64);
} }
@ -45,10 +51,16 @@ extern "C" {
#[inline] #[inline]
pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
#[cfg(target_arch = "bpf")]
unsafe { unsafe {
sol_log_64_(arg1, arg2, arg3, arg4, arg5); sol_log_64_(arg1, arg2, arg3, arg4, arg5);
} }
#[cfg(not(target_arch = "bpf"))]
crate::program_stubs::sol_log_64(arg1, arg2, arg3, arg4, arg5);
} }
#[cfg(target_arch = "bpf")]
extern "C" { extern "C" {
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64);
} }

View File

@ -1,9 +1,6 @@
#![cfg(feature = "program")] #![cfg(feature = "program")]
use crate::{ use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
account_info::AccountInfo, entrypoint::ProgramResult, entrypoint::SUCCESS,
instruction::Instruction,
};
/// Invoke a cross-program instruction /// Invoke a cross-program instruction
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult { pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
@ -32,20 +29,28 @@ pub fn invoke_signed(
} }
} }
let result = unsafe { #[cfg(target_arch = "bpf")]
sol_invoke_signed_rust( {
instruction as *const _ as *const u8, let result = unsafe {
account_infos as *const _ as *const u8, sol_invoke_signed_rust(
account_infos.len() as u64, instruction as *const _ as *const u8,
signers_seeds as *const _ as *const u8, account_infos as *const _ as *const u8,
signers_seeds.len() as u64, account_infos.len() as u64,
) signers_seeds as *const _ as *const u8,
}; signers_seeds.len() as u64,
match result { )
SUCCESS => Ok(()), };
_ => Err(result.into()), match result {
crate::entrypoint::SUCCESS => Ok(()),
_ => Err(result.into()),
}
} }
#[cfg(not(target_arch = "bpf"))]
crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds)
} }
#[cfg(target_arch = "bpf")]
extern "C" { extern "C" {
fn sol_invoke_signed_rust( fn sol_invoke_signed_rust(
instruction_addr: *const u8, instruction_addr: *const u8,

View File

@ -1,36 +1,54 @@
//! @brief Syscall stubs when building for non-BPF targets //! @brief Syscall stubs when building for programs for non-BPF targets
#[cfg(not(target_arch = "bpf"))] use crate::{
#[no_mangle] account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
/// # Safety program_error::ProgramError,
pub unsafe fn sol_log_(message: *const u8, length: u64) { };
let slice = std::slice::from_raw_parts(message, length as usize); use std::sync::{Arc, RwLock};
let string = std::str::from_utf8(&slice).unwrap();
println!("{}", string); lazy_static::lazy_static! {
static ref SYSCALL_STUBS: Arc<RwLock<Box<dyn SyscallStubs>>> = Arc::new(RwLock::new(Box::new(DefaultSyscallStubs {})));
} }
#[cfg(not(target_arch = "bpf"))] // The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in
#[no_mangle] // alternatives
pub fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
println!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5); std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
} }
#[cfg(not(target_arch = "bpf"))] pub trait SyscallStubs: Sync + Send {
#[no_mangle] fn sol_log(&self, message: &str) {
pub fn sol_invoke_signed_rust() { println!("{}", message);
println!("sol_invoke_signed_rust()"); }
fn sol_invoke_signed(
&self,
_instruction: &Instruction,
_account_infos: &[AccountInfo],
_signers_seeds: &[&[&[u8]]],
) -> ProgramResult {
sol_log("SyscallStubs: sol_invoke_signed() not available");
Err(ProgramError::InvalidArgument)
}
} }
#[macro_export] struct DefaultSyscallStubs {}
macro_rules! program_stubs { impl SyscallStubs for DefaultSyscallStubs {}
() => {
#[cfg(not(target_arch = "bpf"))] pub(crate) fn sol_log(message: &str) {
#[test] SYSCALL_STUBS.read().unwrap().sol_log(message);
fn pull_in_externs() { }
use solana_sdk::program_stubs::{sol_invoke_signed_rust, sol_log_, sol_log_64_};
unsafe { sol_log_("sol_log_".as_ptr(), 8) }; pub(crate) fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
sol_log_64_(1, 2, 3, 4, 5); sol_log(&format!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5));
sol_invoke_signed_rust(); }
}
}; pub(crate) fn sol_invoke_signed(
instruction: &Instruction,
account_infos: &[AccountInfo],
signers_seeds: &[&[&[u8]]],
) -> ProgramResult {
SYSCALL_STUBS
.read()
.unwrap()
.sol_invoke_signed(instruction, account_infos, signers_seeds)
} }

View File

@ -204,12 +204,17 @@ impl Pubkey {
} }
/// Log a `Pubkey` from a program /// Log a `Pubkey` from a program
#[cfg(feature = "program")]
pub fn log(&self) { pub fn log(&self) {
extern "C" { #[cfg(all(feature = "program", target_arch = "bpf"))]
fn sol_log_pubkey(pubkey_addr: *const u8); {
}; extern "C" {
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) }; fn sol_log_pubkey(pubkey_addr: *const u8);
};
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) };
}
#[cfg(all(feature = "program", not(target_arch = "bpf")))]
crate::program_stubs::sol_log(&self.to_string());
} }
} }