Skip to content

Commit c986303

Browse files
authored
Create edge case program for call arguments (#2422)
1 parent 57144b0 commit c986303

File tree

5 files changed

+351
-0
lines changed

5 files changed

+351
-0
lines changed

programs/sbf/Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/sbf/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ edition = "2021"
1111
array-bytes = "=1.4.1"
1212
bincode = { version = "1.1.4", default-features = false }
1313
blake3 = "1.0.0"
14+
borsh = "1.5.1"
1415
byteorder = "1.3.2"
1516
elf = "0.0.10"
1617
getrandom = "0.2.10"
@@ -90,6 +91,7 @@ frozen-abi = []
9091
[dev-dependencies]
9192
agave-validator = { workspace = true }
9293
bincode = { workspace = true }
94+
borsh = { workspace = true }
9395
byteorder = { workspace = true }
9496
elf = { workspace = true }
9597
itertools = { workspace = true }
@@ -131,6 +133,7 @@ members = [
131133
"rust/alt_bn128",
132134
"rust/alt_bn128_compression",
133135
"rust/big_mod_exp",
136+
"rust/call_args",
134137
"rust/call_depth",
135138
"rust/caller_access",
136139
"rust/curve25519",
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "solana-sbf-rust-call-args"
3+
version = { workspace = true }
4+
description = { workspace = true }
5+
authors = { workspace = true }
6+
repository = { workspace = true }
7+
homepage = { workspace = true }
8+
license = { workspace = true }
9+
edition = { workspace = true }
10+
11+
[dependencies]
12+
borsh = { workspace = true }
13+
solana-program = { workspace = true }
14+
15+
[lib]
16+
crate-type = ["cdylib"]
+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use {
2+
borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize},
3+
solana_program::{
4+
account_info::AccountInfo, entrypoint::ProgramResult, program::set_return_data,
5+
pubkey::Pubkey,
6+
},
7+
};
8+
9+
#[derive(BorshSerialize, BorshDeserialize, Clone, Copy)]
10+
struct Test128 {
11+
a: u128,
12+
b: u128,
13+
}
14+
15+
#[derive(BorshDeserialize)]
16+
struct InputData {
17+
test_128: Test128,
18+
arg1: i64,
19+
arg2: i64,
20+
arg3: i64,
21+
arg4: i64,
22+
arg5: i64,
23+
arg6: i64,
24+
arg7: i64,
25+
arg8: i64,
26+
}
27+
28+
#[derive(BorshSerialize)]
29+
struct OutputData {
30+
res_128: u128,
31+
res_256: Test128,
32+
many_args_1: i64,
33+
many_args_2: i64,
34+
}
35+
36+
solana_program::entrypoint!(entry);
37+
38+
pub fn entry(_program_id: &Pubkey, _accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
39+
// This code is supposed to occupy stack space. The purpose of this test is to make sure
40+
// we operate on the limits of the stack frame safely.
41+
let buffer: [u8; 3800] = [1; 3800];
42+
43+
let mut x: [u8; 16] = [0; 16];
44+
x.copy_from_slice(&buffer[3784..3800]);
45+
x[10] = 0x39;
46+
x[11] = 0x37;
47+
48+
// Assert the function call hasn't overwritten these values
49+
check_arr(x);
50+
assert_eq!(x[10], 0x39);
51+
assert_eq!(x[11], 0x37);
52+
53+
// The function call must not overwrite the values and the return must be correct.
54+
let y = check_arr_and_return(x);
55+
assert_eq!(x[10], 0x39);
56+
assert_eq!(x[11], 0x37);
57+
assert_eq!(y[10], 0x39);
58+
assert_eq!(y[11], 0x37);
59+
assert_eq!(y[15], 17);
60+
61+
let decoded: InputData = from_slice::<InputData>(data).unwrap();
62+
63+
let output = OutputData {
64+
res_128: test_128_arg(decoded.test_128.a, decoded.test_128.b),
65+
res_256: test_256_arg(decoded.test_128),
66+
many_args_1: many_args(
67+
decoded.arg1,
68+
decoded.arg2,
69+
decoded.arg3,
70+
decoded.arg4,
71+
decoded.arg5,
72+
decoded.arg6,
73+
decoded.arg7,
74+
decoded.arg8,
75+
),
76+
many_args_2: many_args_stack_space(
77+
decoded.arg1,
78+
decoded.arg2,
79+
decoded.arg3,
80+
decoded.arg4,
81+
decoded.arg5,
82+
decoded.arg6,
83+
decoded.arg7,
84+
decoded.arg8,
85+
),
86+
};
87+
88+
let encoded = to_vec(&output).unwrap();
89+
90+
set_return_data(encoded.as_slice());
91+
92+
Ok(())
93+
}
94+
95+
// In this function the argument is promoted to a pointer, so it does not overwrite the stack.
96+
#[allow(improper_ctypes_definitions)]
97+
#[inline(never)]
98+
extern "C" fn check_arr(x: [u8; 16]) {
99+
for (idx, item) in x.iter().enumerate() {
100+
if idx != 10 && idx != 11 {
101+
assert!(*item == 1u8);
102+
}
103+
}
104+
assert_eq!(x[11], 0x37);
105+
assert_eq!(x[10], 0x39);
106+
}
107+
108+
// Both the argument and return value are promoted to pointers.
109+
#[allow(improper_ctypes_definitions)]
110+
#[inline(never)]
111+
extern "C" fn check_arr_and_return(mut x: [u8; 16]) -> [u8; 16] {
112+
for (idx, item) in x.iter().enumerate() {
113+
if idx != 10 && idx != 11 {
114+
assert!(*item == 1u8);
115+
}
116+
}
117+
assert_eq!(x[11], 0x37);
118+
assert_eq!(x[10], 0x39);
119+
x[15] = 17;
120+
x
121+
}
122+
123+
// Test a 128 bit argument
124+
#[allow(clippy::arithmetic_side_effects)]
125+
#[inline(never)]
126+
fn test_128_arg(x: u128, y: u128) -> u128 {
127+
x % y
128+
}
129+
130+
// Test a 256-bit argument
131+
#[allow(clippy::arithmetic_side_effects)]
132+
#[inline(never)]
133+
fn test_256_arg(x: Test128) -> Test128 {
134+
Test128 {
135+
a: x.a + x.b,
136+
b: x.a - x.b,
137+
}
138+
}
139+
140+
// Test a function that needs to save arguments in the stack
141+
#[allow(clippy::arithmetic_side_effects)]
142+
#[inline(never)]
143+
extern "C" fn many_args(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64, g: i64, h: i64) -> i64 {
144+
let i = a + b;
145+
let j = i - c;
146+
let k = j + d;
147+
let l = k - e;
148+
let m = l % f;
149+
let n = m - g;
150+
n + h
151+
}
152+
153+
// Test a function that utilizes stack space and needs to retrieve arguments from the caller stack
154+
#[allow(clippy::arithmetic_side_effects)]
155+
#[inline(never)]
156+
extern "C" fn many_args_stack_space(
157+
a: i64,
158+
b: i64,
159+
c: i64,
160+
d: i64,
161+
e: i64,
162+
f: i64,
163+
g: i64,
164+
h: i64,
165+
) -> i64 {
166+
let s: [i64; 3] = [1, 2, 3];
167+
let i = a + b;
168+
let j = i - c;
169+
let k = j + d;
170+
let l = k - e;
171+
let m = l % f;
172+
let n = m - g;
173+
let o = n + h;
174+
let p = o + s[0];
175+
let q = p + s[1];
176+
q - s[2]
177+
}

0 commit comments

Comments
 (0)