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",
"hmac",
"itertools 0.9.0",
"lazy_static",
"libsecp256k1",
"log 0.4.8",
"memmap",

View File

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

View File

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

View File

@ -1,6 +1,6 @@
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> {
fn decode_custom_error_to_enum(custom: u32) -> Option<E>
where

View File

@ -84,7 +84,23 @@ pub mod entrypoint_deprecated;
pub mod log;
pub mod program;
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;
// 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;
// Modules not usable by on-chain programs

View File

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

View File

@ -1,9 +1,6 @@
#![cfg(feature = "program")]
use crate::{
account_info::AccountInfo, entrypoint::ProgramResult, entrypoint::SUCCESS,
instruction::Instruction,
};
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
/// Invoke a cross-program instruction
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {
@ -32,20 +29,28 @@ pub fn invoke_signed(
}
}
let result = unsafe {
sol_invoke_signed_rust(
instruction as *const _ as *const u8,
account_infos as *const _ as *const u8,
account_infos.len() as u64,
signers_seeds as *const _ as *const u8,
signers_seeds.len() as u64,
)
};
match result {
SUCCESS => Ok(()),
_ => Err(result.into()),
#[cfg(target_arch = "bpf")]
{
let result = unsafe {
sol_invoke_signed_rust(
instruction as *const _ as *const u8,
account_infos as *const _ as *const u8,
account_infos.len() as u64,
signers_seeds as *const _ as *const u8,
signers_seeds.len() as u64,
)
};
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" {
fn sol_invoke_signed_rust(
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"))]
#[no_mangle]
/// # Safety
pub unsafe fn sol_log_(message: *const u8, length: u64) {
let slice = std::slice::from_raw_parts(message, length as usize);
let string = std::str::from_utf8(&slice).unwrap();
println!("{}", string);
use crate::{
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
program_error::ProgramError,
};
use std::sync::{Arc, RwLock};
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"))]
#[no_mangle]
pub fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
println!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5);
// The default syscall stubs don't do much, but `set_syscalls()` can be used to swap in
// alternatives
pub fn set_syscall_stubs(syscall_stubs: Box<dyn SyscallStubs>) -> Box<dyn SyscallStubs> {
std::mem::replace(&mut SYSCALL_STUBS.write().unwrap(), syscall_stubs)
}
#[cfg(not(target_arch = "bpf"))]
#[no_mangle]
pub fn sol_invoke_signed_rust() {
println!("sol_invoke_signed_rust()");
pub trait SyscallStubs: Sync + Send {
fn sol_log(&self, message: &str) {
println!("{}", message);
}
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]
macro_rules! program_stubs {
() => {
#[cfg(not(target_arch = "bpf"))]
#[test]
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) };
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,12 +204,17 @@ impl Pubkey {
}
/// Log a `Pubkey` from a program
#[cfg(feature = "program")]
pub fn log(&self) {
extern "C" {
fn sol_log_pubkey(pubkey_addr: *const u8);
};
unsafe { sol_log_pubkey(self.as_ref() as *const _ as *const u8) };
#[cfg(all(feature = "program", target_arch = "bpf"))]
{
extern "C" {
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());
}
}