Skip to content

Commit

Permalink
Create edge case for call args
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasSte committed Aug 5, 2024
1 parent 51208f8 commit 5db80ac
Show file tree
Hide file tree
Showing 5 changed files with 351 additions and 0 deletions.
9 changes: 9 additions & 0 deletions programs/sbf/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 programs/sbf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ edition = "2021"
array-bytes = "=1.4.1"
bincode = { version = "1.1.4", default-features = false }
blake3 = "1.0.0"
borsh = "1.5.1"
byteorder = "1.3.2"
elf = "0.0.10"
getrandom = "0.2.10"
Expand Down Expand Up @@ -90,6 +91,7 @@ frozen-abi = []
[dev-dependencies]
agave-validator = { workspace = true }
bincode = { workspace = true }
borsh = { workspace = true }
byteorder = { workspace = true }
elf = { workspace = true }
itertools = { workspace = true }
Expand Down Expand Up @@ -131,6 +133,7 @@ members = [
"rust/alt_bn128",
"rust/alt_bn128_compression",
"rust/big_mod_exp",
"rust/call_args",
"rust/call_depth",
"rust/caller_access",
"rust/curve25519",
Expand Down
16 changes: 16 additions & 0 deletions programs/sbf/rust/call_args/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "solana-sbf-rust-call-args"
version = { workspace = true }
description = { workspace = true }
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[dependencies]
solana-program = { workspace = true }
borsh = { workspace = true }

[lib]
crate-type = ["cdylib"]
177 changes: 177 additions & 0 deletions programs/sbf/rust/call_args/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use {
borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize},
solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program::set_return_data,
pubkey::Pubkey,
},
};

#[derive(BorshSerialize, BorshDeserialize, Clone, Copy)]
struct Test128 {
a: u128,
b: u128,
}

#[derive(BorshDeserialize)]
struct InputData {
test_128: Test128,
arg1: i64,
arg2: i64,
arg3: i64,
arg4: i64,
arg5: i64,
arg6: i64,
arg7: i64,
arg8: i64,
}

#[derive(BorshSerialize)]
struct OutputData {
res_128: u128,
res_256: Test128,
many_args_1: i64,
many_args_2: i64,
}

solana_program::entrypoint!(entry);

pub fn entry(_program_id: &Pubkey, _accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
// This code is supposed to occupy stack space. The purpose of this test is to make sure
// we operate on the limits of the stack frame safely.
let buffer: [u8; 3800] = [1; 3800];

let mut x: [u8; 16] = [0; 16];
x.copy_from_slice(&buffer[3784..3800]);
x[10] = 0x39;
x[11] = 0x37;

// Assert the function call hasn't overwritten these values
check_arr(x);
assert_eq!(x[10], 0x39);
assert_eq!(x[11], 0x37);

// The function call must not overwrite the values and the return must be correct.
let y = check_arr_and_return(x);
assert_eq!(x[10], 0x39);
assert_eq!(x[11], 0x37);
assert_eq!(y[10], 0x39);
assert_eq!(y[11], 0x37);
assert_eq!(y[15], 17);

let decoded: InputData = from_slice::<InputData>(data).unwrap();

let output = OutputData {
res_128: test_128_arg(decoded.test_128.a, decoded.test_128.b),
res_256: test_256_arg(decoded.test_128),
many_args_1: many_args(
decoded.arg1,
decoded.arg2,
decoded.arg3,
decoded.arg4,
decoded.arg5,
decoded.arg6,
decoded.arg7,
decoded.arg8,
),
many_args_2: many_args_stack_space(
decoded.arg1,
decoded.arg2,
decoded.arg3,
decoded.arg4,
decoded.arg5,
decoded.arg6,
decoded.arg7,
decoded.arg8,
),
};

let encoded = to_vec(&output).unwrap();

set_return_data(encoded.as_slice());

Ok(())
}

// In this function the argument is promoted to a pointer, so it does not overwrite the stack.
#[allow(improper_ctypes_definitions)]
#[inline(never)]
extern "C" fn check_arr(x: [u8; 16]) {
for (idx, item) in x.iter().enumerate() {
if idx != 10 && idx != 11 {
assert!(*item == 1u8);
}
}
assert_eq!(x[11], 0x37);
assert_eq!(x[10], 0x39);
}

// Both the argument and return value are promoted to pointers.
#[allow(improper_ctypes_definitions)]
#[inline(never)]
extern "C" fn check_arr_and_return(mut x: [u8; 16]) -> [u8; 16] {
for (idx, item) in x.iter().enumerate() {
if idx != 10 && idx != 11 {
assert!(*item == 1u8);
}
}
assert_eq!(x[11], 0x37);
assert_eq!(x[10], 0x39);
x[15] = 17;
x
}

// Test a 128 bit argument
#[allow(clippy::arithmetic_side_effects)]
#[inline(never)]
fn test_128_arg(x: u128, y: u128) -> u128 {
x % y
}

// Test a 256-bit argument
#[allow(clippy::arithmetic_side_effects)]
#[inline(never)]
fn test_256_arg(x: Test128) -> Test128 {
Test128 {
a: x.a + x.b,
b: x.a - x.b,
}
}

// Test a function that needs to save arguments in the stack
#[allow(clippy::arithmetic_side_effects)]
#[inline(never)]
extern "C" fn many_args(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64, h: i64) -> i64 {
let i = a + b;
let j = i - c;
let k = j + d;
let l = k - e;
let m = l % f;
let n = m - g;
n + h
}

// Test a function that utilizes stack space and needs to retrieve arguments from the caller stack
#[allow(clippy::arithmetic_side_effects)]
#[inline(never)]
extern "C" fn many_args_stack_space(
a: i64,
b: i64,
c: i64,
d: i64,
e: i64,
f: i64,
g: i64,
h: i64,
) -> i64 {
let s: [i64; 3] = [1, 2, 3];
let i = a + b;
let j = i - c;
let k = j + d;
let l = k - e;
let m = l % f;
let n = m - g;
let o = n + h;
let p = o + s[0];
let q = p + s[1];
q - s[2]
}
Loading

0 comments on commit 5db80ac

Please sign in to comment.