From d7f7742beb217ef4a6032bfbe7c5b9518b9ccc2f Mon Sep 17 00:00:00 2001 From: Lucas Ste <38472950+LucasSte@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:31:38 -0300 Subject: [PATCH] Add SBPFv1 tests to the CI pipeline (#5126) * Add SBPFv1 to the CI pipeline * Tune instruction count * Check upper bound instead of fixed number of instructions * Verify zeroed stack with dynamic stack frames * Use constants to estimate length of zeroed bytes * Remove comment (cherry picked from commit b704875fb5146ed46f1f0ff256be0d0efac5ee60) --- ci/test-stable.sh | 5 ++++- programs/sbf/Makefile | 18 ++++++++++++++++-- programs/sbf/rust/invoke/Cargo.toml | 3 +++ programs/sbf/rust/invoke/src/lib.rs | 25 ++++++++++++++++++++++--- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/ci/test-stable.sh b/ci/test-stable.sh index 4751d9d81ff61e..b34fe35afd2bd2 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -55,9 +55,12 @@ test-stable-sbf) # Install the platform tools _ platform-tools-sdk/sbf/scripts/install.sh - # SBF program tests + # SBPFv0 program tests _ make -C programs/sbf test-v0 + # SBPFv1 program tests + _ make -C programs/sbf clean-all test-v1 + exit 0 ;; test-docs) diff --git a/programs/sbf/Makefile b/programs/sbf/Makefile index 9e8887bee34f57..f1774e53813b68 100755 --- a/programs/sbf/Makefile +++ b/programs/sbf/Makefile @@ -2,13 +2,27 @@ SBF_SDK_PATH := ../../platform-tools-sdk/sbf SRC_DIR := c/src OUT_DIR := target/deploy -test-v0: all rust-v0 +clean-all: clean + cargo clean + +test: SBF_OUT_DIR=$(OUT_DIR) cargo test --features="sbf_rust,sbf_c" $(TEST_ARGS) +test-v1: + SBPF_CPU=v1 $(MAKE) all ; \ + $(MAKE) rust-v1 ; \ + $(MAKE) test + +test-v0: all rust-v0 test + rust-v0: cargo +solana build --release --target sbpf-solana-solana --workspace ; \ cp -r target/sbpf-solana-solana/release/* target/deploy -.PHONY: rust +rust-v1: + cargo +solana build --release --target sbpfv1-solana-solana --workspace --features dynamic-frames ; \ + cp -r target/sbpfv1-solana-solana/release/* target/deploy + +.PHONY: rust-v0 include $(SBF_SDK_PATH)/c/sbf.mk diff --git a/programs/sbf/rust/invoke/Cargo.toml b/programs/sbf/rust/invoke/Cargo.toml index 94aaf9a851dec2..e39e49b68dd400 100644 --- a/programs/sbf/rust/invoke/Cargo.toml +++ b/programs/sbf/rust/invoke/Cargo.toml @@ -19,3 +19,6 @@ crate-type = ["cdylib"] [lints] workspace = true + +[features] +dynamic-frames = [] diff --git a/programs/sbf/rust/invoke/src/lib.rs b/programs/sbf/rust/invoke/src/lib.rs index 7e97bcf5cb67ae..f4506943bcb328 100644 --- a/programs/sbf/rust/invoke/src/lib.rs +++ b/programs/sbf/rust/invoke/src/lib.rs @@ -3,6 +3,8 @@ #![allow(unreachable_code)] #![allow(clippy::arithmetic_side_effects)] +#[cfg(feature = "dynamic-frames")] +use solana_program::program_memory::sol_memcmp; use { solana_program::{ account_info::AccountInfo, @@ -1456,19 +1458,36 @@ fn process_instruction<'a>( heap[8..pos].fill(42); // Check that the stack is zeroed too. - // + let stack = unsafe { + slice::from_raw_parts_mut( + MM_STACK_START as *mut u8, + MAX_CALL_DEPTH * STACK_FRAME_SIZE, + ) + }; + + #[cfg(not(feature = "dynamic-frames"))] // We don't know in which frame we are now, so we skip a few (10) frames at the start // which might have been used by the current call stack. We check that the memory for // the 10..MAX_CALL_DEPTH frames is zeroed. Then we write a sentinel value, and in the // next nested invocation check that it's been zeroed. - let stack = - unsafe { slice::from_raw_parts_mut(MM_STACK_START as *mut u8, 0x100000000) }; + // + // When we don't have dynamic stack frames, the stack grows from lower addresses + // to higher addresses, so we compare accordingly. for i in 10..MAX_CALL_DEPTH { let stack = &mut stack[i * STACK_FRAME_SIZE..][..STACK_FRAME_SIZE]; assert!(stack == &ZEROS[..STACK_FRAME_SIZE], "stack not zeroed"); stack.fill(42); } + #[cfg(feature = "dynamic-frames")] + // When we have dynamic frames, the stack grows from the higher addresses, so we + // compare from zero until the beginning of a function frame. + { + const ZEROED_BYTES_LENGTH: usize = (MAX_CALL_DEPTH - 2) * STACK_FRAME_SIZE; + assert_eq!(sol_memcmp(stack, &ZEROS, ZEROED_BYTES_LENGTH), 0); + stack[..ZEROED_BYTES_LENGTH].fill(42); + } + // Recurse to check that the stack and heap are zeroed. // // We recurse until we go over max CPI depth and error out. Stack and heap allocations