Skip to content

Commit

Permalink
Add return data
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Sep 2, 2021
1 parent c550b32 commit d54e41d
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions program-runtime/src/instruction_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,9 @@ impl InstructionProcessor {
let keyed_accounts =
Self::create_keyed_accounts(message, instruction, executable_accounts, accounts);

// clear the return data
invoke_context.set_return_data(&[]);

// Invoke callee
invoke_context.push(program_id, &keyed_accounts)?;

Expand Down
1 change: 1 addition & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions programs/bpf/c/src/invoke/invoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <sol/log.h>
#include <sol/assert.h>
#include <sol/deserialize.h>
#include <sol/return_data.h>

static const uint8_t TEST_SUCCESS = 1;
static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2;
Expand Down Expand Up @@ -174,6 +175,26 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
}

sol_log("Test return data");
{
SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}};
uint8_t data[] = { SET_RETURN_DATA };
uint8_t buf[100];

const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};

sol_assert(SUCCESS ==
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));

uint64_t ret = sol_get_return_data(data, sizeof(data));

sol_assert(ret == sizeof(RETURN_DATA_VAL));

sol_assert(sol_memcmp(data, RETURN_DATA_VAL, sizeof(RETURN_DATA_VAL)));
}

sol_log("Test create_program_address");
{
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
Expand Down
3 changes: 3 additions & 0 deletions programs/bpf/c/src/invoked/instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
const uint8_t WRITE_ACCOUNT = 11;
const uint8_t SET_RETURN_DATA = 12;

#define RETURN_DATA_VAL "return data test"
8 changes: 8 additions & 0 deletions programs/bpf/c/src/invoked/invoked.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
return ERROR_INVALID_ARGUMENT;
}

// on entry, return data must always be empty
sol_assert(sol_get_return_data(NULL, 0) == 0);

if (params.data_len == 0) {
return SUCCESS;
}
Expand Down Expand Up @@ -91,6 +94,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_log("return Ok");
return SUCCESS;
}
case SET_RETURN_DATA: {
sol_set_return_data((const uint8_t*)RETURN_DATA_VAL, sizeof(RETURN_DATA_VAL));
sol_log("set return data");
return SUCCESS;
}
case RETURN_ERROR: {
sol_log("return error");
return 42;
Expand Down
35 changes: 35 additions & 0 deletions programs/bpf/c/src/return_data/return_data.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @brief return data Syscall test
*/
#include <solana_sdk.h>

#define DATA "the quick brown fox jumps over the lazy dog"

extern uint64_t entrypoint(const uint8_t *input) {
uint8_t buf[1024];

// There should be no return data on entry
uint64_t ret = sol_get_return_data(NULL, 0);

sol_assert(ret == 0);

// set some return data
sol_set_return_data((const uint8_t*)DATA, sizeof(DATA));

// try getting a subset
ret = sol_get_return_data(buf, 4);

sol_assert(ret == sizeof(DATA));

sol_assert(!sol_memcmp(buf, "the ", 4));

// try getting the whole thing
ret = sol_get_return_data(buf, sizeof(buf));

sol_assert(ret == sizeof(DATA));

sol_assert(!sol_memcmp(buf, (const uint8_t*)DATA, sizeof(DATA)));

// done
return SUCCESS;
}
5 changes: 5 additions & 0 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ fn run_program(
for i in 0..2 {
let mut parameter_bytes = parameter_bytes.clone();
{
invoke_context.set_return_data(&[]);

let mut vm = create_vm(
&loader_id,
executable.as_ref(),
Expand Down Expand Up @@ -432,6 +434,7 @@ fn test_program_bpf_sanity() {
("noop++", true),
("panic", false),
("relative_call", true),
("return_data", true),
("sanity", true),
("sanity++", true),
("secp256k1_recover", true),
Expand Down Expand Up @@ -878,6 +881,7 @@ fn test_program_bpf_invoke_sanity() {
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
Languages::Rust => vec![
system_program::id(),
Expand Down Expand Up @@ -1312,6 +1316,7 @@ fn assert_instruction_count() {
("noop", 5),
("noop++", 5),
("relative_call", 10),
("return_data", 469),
("sanity", 169),
("sanity++", 168),
("secp256k1_recover", 359),
Expand Down
5 changes: 5 additions & 0 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@ impl Executor for BpfExecutor {
let trace_string = String::from_utf8(trace_buffer).unwrap();
trace!("BPF Program Instruction Trace:\n{}", trace_string);
}
drop(vm);
let return_data = invoke_context.get_return_data();
if !return_data.is_empty() {
stable_log::program_return_data(&logger, return_data);
}
match result {
Ok(status) => {
if status != SUCCESS {
Expand Down
122 changes: 121 additions & 1 deletion programs/bpf_loader/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use solana_sdk::{
feature_set::{
blake3_syscall_enabled, close_upgradeable_program_accounts, disable_fees_sysvar,
enforce_aligned_host_addrs, libsecp256k1_0_5_upgrade_enabled, mem_overlap_fix,
secp256k1_recover_syscall_enabled,
return_data_syscall_enabled, secp256k1_recover_syscall_enabled,
},
hash::{Hasher, HASH_BYTES},
ic_msg,
Expand Down Expand Up @@ -52,6 +52,9 @@ use thiserror::Error as ThisError;
/// Maximum signers
pub const MAX_SIGNERS: usize = 16;

/// Maximum size return data
pub const MAX_RETURN_DATA: u64 = 1024;

/// Error definitions
#[derive(Debug, ThisError, PartialEq)]
pub enum SyscallError {
Expand Down Expand Up @@ -172,6 +175,14 @@ pub fn register_syscalls(
// Memory allocator
syscall_registry.register_syscall_by_name(b"sol_alloc_free_", SyscallAllocFree::call)?;

// Return data
if invoke_context.is_feature_active(&return_data_syscall_enabled::id()) {
syscall_registry
.register_syscall_by_name(b"sol_set_return_data", SyscallSetReturnData::call)?;
syscall_registry
.register_syscall_by_name(b"sol_get_return_data", SyscallGetReturnData::call)?;
}

Ok(syscall_registry)
}

Expand Down Expand Up @@ -347,6 +358,8 @@ pub fn bind_syscall_context_objects<'a>(

let is_fee_sysvar_via_syscall_active =
!invoke_context.is_feature_active(&disable_fees_sysvar::id());
let is_return_data_syscall_active =
invoke_context.is_feature_active(&return_data_syscall_enabled::id());

let invoke_context = Rc::new(RefCell::new(invoke_context));

Expand Down Expand Up @@ -380,6 +393,25 @@ pub fn bind_syscall_context_objects<'a>(
None,
)?;

// Return data
bind_feature_gated_syscall_context_object!(
vm,
is_return_data_syscall_active,
Box::new(SyscallSetReturnData {
invoke_context: invoke_context.clone(),
loader_id,
}),
);

bind_feature_gated_syscall_context_object!(
vm,
is_return_data_syscall_active,
Box::new(SyscallGetReturnData {
invoke_context: invoke_context.clone(),
loader_id,
}),
);

// Cross-program invocation syscalls
vm.bind_syscall_context_object(
Box::new(SyscallInvokeSignedC {
Expand Down Expand Up @@ -2463,6 +2495,94 @@ fn call<'a>(
Ok(SUCCESS)
}

// Return data hanlding
pub struct SyscallSetReturnData<'a> {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallSetReturnData<'a> {
fn call(
&mut self,
addr: u64,
len: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
if len > MAX_RETURN_DATA {
*result = Err(SyscallError::InstructionError(InstructionError::InvalidArgument).into());
return;
}

let mut invoke_context = question_mark!(
self.invoke_context
.try_borrow_mut()
.map_err(|_| SyscallError::InvokeContextBorrowFailed),
result
);

let cost = invoke_context.get_compute_budget().cpi_bytes_per_unit;
question_mark!(
invoke_context.get_compute_meter().consume(len / cost),
result
);

let return_data = question_mark!(
translate_slice::<u8>(memory_mapping, addr, len, self.loader_id, true,),
result
);

invoke_context.set_return_data(return_data);

*result = Ok(0);
}
}

pub struct SyscallGetReturnData<'a> {
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BpfError> for SyscallGetReturnData<'a> {
fn call(
&mut self,
addr: u64,
len: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &MemoryMapping,
result: &mut Result<u64, EbpfError<BpfError>>,
) {
let invoke_context = question_mark!(
self.invoke_context
.try_borrow()
.map_err(|_| SyscallError::InvokeContextBorrowFailed),
result
);

let return_data = invoke_context.get_return_data();

let length = std::cmp::min(return_data.len() as u64, len);

let cost = invoke_context.get_compute_budget().cpi_bytes_per_unit;
question_mark!(
invoke_context.get_compute_meter().consume(length / cost),
result
);

let return_data_result = question_mark!(
translate_slice_mut::<u8>(memory_mapping, addr, length, self.loader_id, true,),
result
);

return_data_result.copy_from_slice(&return_data[..length as usize]);

*result = Ok(return_data.len() as u64);
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 8 additions & 0 deletions runtime/src/message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub struct ThisInvokeContext<'a> {
sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
blockhash: &'a Hash,
fee_calculator: &'a FeeCalculator,
return_data: Vec<u8>,
}
impl<'a> ThisInvokeContext<'a> {
#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -121,6 +122,7 @@ impl<'a> ThisInvokeContext<'a> {
sysvars: RefCell::new(vec![]),
blockhash,
fee_calculator,
return_data: Vec::new(),
};
invoke_context
.invoke_stack
Expand Down Expand Up @@ -312,6 +314,12 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn get_fee_calculator(&self) -> &FeeCalculator {
self.fee_calculator
}
fn set_return_data(&mut self, data: &[u8]) {
self.return_data = data.to_vec();
}
fn get_return_data(&self) -> &[u8] {
&self.return_data
}
}
pub struct ThisLogger {
log_collector: Option<Rc<LogCollector>>,
Expand Down
1 change: 1 addition & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ full = [
assert_matches = { version = "1.5.0", optional = true }
bincode = "1.3.3"
borsh = "0.9.0"
base64 = "0.13"
borsh-derive = "0.9.0"
bs58 = "0.4.0"
bv = { version = "0.11.1", features = ["serde"] }
Expand Down
Loading

0 comments on commit d54e41d

Please sign in to comment.