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,6 +29,8 @@ pub fn invoke_signed(
} }
} }
#[cfg(target_arch = "bpf")]
{
let result = unsafe { let result = unsafe {
sol_invoke_signed_rust( sol_invoke_signed_rust(
instruction as *const _ as *const u8, instruction as *const _ as *const u8,
@ -42,10 +41,16 @@ pub fn invoke_signed(
) )
}; };
match result { match result {
SUCCESS => Ok(()), crate::entrypoint::SUCCESS => Ok(()),
_ => Err(result.into()), _ => 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,
#[macro_export] _instruction: &Instruction,
macro_rules! program_stubs { _account_infos: &[AccountInfo],
() => { _signers_seeds: &[&[&[u8]]],
#[cfg(not(target_arch = "bpf"))] ) -> ProgramResult {
#[test] sol_log("SyscallStubs: sol_invoke_signed() not available");
fn pull_in_externs() { Err(ProgramError::InvalidArgument)
use solana_sdk::program_stubs::{sol_invoke_signed_rust, sol_log_, sol_log_64_};
unsafe { sol_log_("sol_log_".as_ptr(), 8) };
sol_log_64_(1, 2, 3, 4, 5);
sol_invoke_signed_rust();
} }
}; }
struct DefaultSyscallStubs {}
impl SyscallStubs for DefaultSyscallStubs {}
pub(crate) fn sol_log(message: &str) {
SYSCALL_STUBS.read().unwrap().sol_log(message);
}
pub(crate) fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
sol_log(&format!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5));
}
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,13 +204,18 @@ 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) {
#[cfg(all(feature = "program", target_arch = "bpf"))]
{
extern "C" { extern "C" {
fn sol_log_pubkey(pubkey_addr: *const u8); fn sol_log_pubkey(pubkey_addr: *const u8);
}; };
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *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());
}
} }
impl AsRef<[u8]> for Pubkey { impl AsRef<[u8]> for Pubkey {