diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 23591afc61539b..ed184302739f2c 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5717,9 +5717,9 @@ dependencies = [ "solana-measure", "solana-program-runtime", "solana-runtime", - "solana-sbf-rust-invoke", - "solana-sbf-rust-realloc", - "solana-sbf-rust-realloc-invoke", + "solana-sbf-rust-invoke-dep", + "solana-sbf-rust-realloc-dep", + "solana-sbf-rust-realloc-invoke-dep", "solana-sdk", "solana-svm", "solana-transaction-status", @@ -5878,8 +5878,9 @@ version = "2.0.0" dependencies = [ "rustversion", "solana-program", - "solana-sbf-rust-invoked", - "solana-sbf-rust-realloc", + "solana-sbf-rust-invoke-dep", + "solana-sbf-rust-invoked-dep", + "solana-sbf-rust-realloc-dep", ] [[package]] @@ -5903,9 +5904,21 @@ dependencies = [ "solana-program", ] +[[package]] +name = "solana-sbf-rust-invoke-dep" +version = "2.0.0" + [[package]] name = "solana-sbf-rust-invoked" version = "2.0.0" +dependencies = [ + "solana-program", + "solana-sbf-rust-invoked-dep", +] + +[[package]] +name = "solana-sbf-rust-invoked-dep" +version = "2.0.0" dependencies = [ "solana-program", ] @@ -6007,6 +6020,14 @@ dependencies = [ [[package]] name = "solana-sbf-rust-realloc" version = "2.0.0" +dependencies = [ + "solana-program", + "solana-sbf-rust-realloc-dep", +] + +[[package]] +name = "solana-sbf-rust-realloc-dep" +version = "2.0.0" dependencies = [ "solana-program", ] @@ -6016,9 +6037,14 @@ name = "solana-sbf-rust-realloc-invoke" version = "2.0.0" dependencies = [ "solana-program", - "solana-sbf-rust-realloc", + "solana-sbf-rust-realloc-dep", + "solana-sbf-rust-realloc-invoke-dep", ] +[[package]] +name = "solana-sbf-rust-realloc-invoke-dep" +version = "2.0.0" + [[package]] name = "solana-sbf-rust-remaining-compute-units" version = "2.0.0" diff --git a/programs/sbf/Cargo.toml b/programs/sbf/Cargo.toml index 78a73c2b11f19c..9f84029a185d75 100644 --- a/programs/sbf/Cargo.toml +++ b/programs/sbf/Cargo.toml @@ -40,13 +40,13 @@ solana-program-runtime = { path = "../../program-runtime", version = "=2.0.0" } solana-program-test = { path = "../../program-test", version = "=2.0.0" } solana-runtime = { path = "../../runtime", version = "=2.0.0" } solana-sbf-rust-128bit-dep = { path = "rust/128bit_dep", version = "=2.0.0" } -solana-sbf-rust-invoke = { path = "rust/invoke", version = "=2.0.0" } -solana-sbf-rust-invoked = { path = "rust/invoked", version = "=2.0.0", default-features = false } +solana-sbf-rust-invoke-dep = { path = "rust/invoke_dep", version = "=2.0.0" } +solana-sbf-rust-invoked-dep = { path = "rust/invoked_dep", version = "=2.0.0" } solana-sbf-rust-many-args-dep = { path = "rust/many_args_dep", version = "=2.0.0" } solana-sbf-rust-mem = { path = "rust/mem", version = "=2.0.0" } solana-sbf-rust-param-passing-dep = { path = "rust/param_passing_dep", version = "=2.0.0" } -solana-sbf-rust-realloc = { path = "rust/realloc", version = "=2.0.0", default-features = false } -solana-sbf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=2.0.0" } +solana-sbf-rust-realloc-dep = { path = "rust/realloc_dep", version = "=2.0.0" } +solana-sbf-rust-realloc-invoke-dep = { path = "rust/realloc_invoke_dep", version = "=2.0.0" } solana-sdk = { path = "../../sdk", version = "=2.0.0" } solana-svm = { path = "../../svm", version = "=2.0.0" } solana-transaction-status = { path = "../../transaction-status", version = "=2.0.0" } @@ -92,9 +92,9 @@ solana-logger = { workspace = true } solana-measure = { workspace = true } solana-program-runtime = { workspace = true } solana-runtime = { workspace = true, features = ["dev-context-only-utils"] } -solana-sbf-rust-invoke = { workspace = true } -solana-sbf-rust-realloc = { workspace = true, features = ["default"] } -solana-sbf-rust-realloc-invoke = { workspace = true } +solana-sbf-rust-invoke-dep = { workspace = true } +solana-sbf-rust-realloc-dep = { workspace = true } +solana-sbf-rust-realloc-invoke-dep = { workspace = true } solana-sdk = { workspace = true } solana-svm = { workspace = true } solana-transaction-status = { workspace = true } diff --git a/programs/sbf/rust/invoke/Cargo.toml b/programs/sbf/rust/invoke/Cargo.toml index 506e678faefa54..cc0490dd866da7 100644 --- a/programs/sbf/rust/invoke/Cargo.toml +++ b/programs/sbf/rust/invoke/Cargo.toml @@ -8,15 +8,12 @@ homepage = { workspace = true } license = { workspace = true } edition = { workspace = true } -[features] -default = ["program"] -program = [] - [dependencies] rustversion = { workspace = true } solana-program = { workspace = true } -solana-sbf-rust-invoked = { workspace = true } -solana-sbf-rust-realloc = { workspace = true } +solana-sbf-rust-invoke-dep = { workspace = true } +solana-sbf-rust-invoked-dep = { workspace = true } +solana-sbf-rust-realloc-dep = { workspace = true } [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/invoke/src/lib.rs b/programs/sbf/rust/invoke/src/lib.rs index f7b0b7bb400e93..72d23245ce7150 100644 --- a/programs/sbf/rust/invoke/src/lib.rs +++ b/programs/sbf/rust/invoke/src/lib.rs @@ -1,3 +1,1372 @@ //! Example Rust-based SBF program that issues a cross-program-invocation -pub mod instructions; -pub mod processor; + +#![allow(unreachable_code)] +#![allow(clippy::arithmetic_side_effects)] + +use { + solana_program::{ + account_info::AccountInfo, + bpf_loader_deprecated, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, + instruction::Instruction, + msg, + program::{get_return_data, invoke, invoke_signed, set_return_data}, + program_error::ProgramError, + pubkey::{Pubkey, PubkeyError}, + syscalls::{ + MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN, + }, + system_instruction, system_program, + }, + solana_sbf_rust_invoke_dep::*, + solana_sbf_rust_invoked_dep::*, + solana_sbf_rust_realloc_dep::*, + std::{cell::RefCell, mem, rc::Rc, slice}, +}; + +fn do_nested_invokes(num_nested_invokes: u64, accounts: &[AccountInfo]) -> ProgramResult { + assert!(accounts[ARGUMENT_INDEX].is_signer); + + let pre_argument_lamports = accounts[ARGUMENT_INDEX].lamports(); + let pre_invoke_argument_lamports = accounts[INVOKED_ARGUMENT_INDEX].lamports(); + { + let mut lamports = (*accounts[ARGUMENT_INDEX].lamports).borrow_mut(); + **lamports = (*lamports).saturating_sub(5); + let mut lamports = (*accounts[INVOKED_ARGUMENT_INDEX].lamports).borrow_mut(); + **lamports = (*lamports).saturating_add(5); + } + + msg!("First invoke"); + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + ], + vec![NESTED_INVOKE, num_nested_invokes as u8], + ); + invoke(&instruction, accounts)?; + msg!("2nd invoke from first program"); + invoke(&instruction, accounts)?; + + assert_eq!( + accounts[ARGUMENT_INDEX].lamports(), + pre_argument_lamports + .saturating_sub(5) + .saturating_add(2_u64.saturating_mul(num_nested_invokes)) + ); + assert_eq!( + accounts[INVOKED_ARGUMENT_INDEX].lamports(), + pre_invoke_argument_lamports + .saturating_add(5) + .saturating_sub(2_u64.saturating_mul(num_nested_invokes)) + ); + Ok(()) +} + +solana_program::entrypoint!(process_instruction); +fn process_instruction<'a>( + program_id: &Pubkey, + accounts: &[AccountInfo<'a>], + instruction_data: &[u8], +) -> ProgramResult { + msg!("invoke Rust program"); + + let bump_seed1 = instruction_data[1]; + let bump_seed2 = instruction_data[2]; + let bump_seed3 = instruction_data[3]; + + match instruction_data[0] { + TEST_SUCCESS => { + msg!("Call system program create account"); + { + let from_lamports = accounts[FROM_INDEX].lamports(); + let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports(); + assert_eq!(accounts[DERIVED_KEY1_INDEX].data_len(), 0); + assert!(solana_program::system_program::check_id( + accounts[DERIVED_KEY1_INDEX].owner + )); + + let instruction = system_instruction::create_account( + accounts[FROM_INDEX].key, + accounts[DERIVED_KEY1_INDEX].key, + 42, + MAX_PERMITTED_DATA_INCREASE as u64, + program_id, + ); + invoke_signed( + &instruction, + accounts, + &[&[b"You pass butter", &[bump_seed1]]], + )?; + + assert_eq!( + accounts[FROM_INDEX].lamports(), + from_lamports.saturating_sub(42) + ); + assert_eq!( + accounts[DERIVED_KEY1_INDEX].lamports(), + to_lamports.saturating_add(42) + ); + assert_eq!(program_id, accounts[DERIVED_KEY1_INDEX].owner); + assert_eq!( + accounts[DERIVED_KEY1_INDEX].data_len(), + MAX_PERMITTED_DATA_INCREASE + ); + let mut data = accounts[DERIVED_KEY1_INDEX].try_borrow_mut_data()?; + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0); + data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)] = 0x0f; + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0x0f); + for i in 0..20 { + data[i] = i as u8; + } + } + + msg!("Call system program transfer"); + { + let from_lamports = accounts[FROM_INDEX].lamports(); + let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports(); + let instruction = system_instruction::transfer( + accounts[FROM_INDEX].key, + accounts[DERIVED_KEY1_INDEX].key, + 1, + ); + invoke(&instruction, accounts)?; + assert_eq!( + accounts[FROM_INDEX].lamports(), + from_lamports.saturating_sub(1) + ); + assert_eq!( + accounts[DERIVED_KEY1_INDEX].lamports(), + to_lamports.saturating_add(1) + ); + } + + msg!("Test data translation"); + { + { + let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; + for i in 0..100 { + data[i as usize] = i; + } + } + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), + ], + vec![VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5], + ); + invoke(&instruction, accounts)?; + } + + msg!("Test no instruction data"); + { + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, true, true)], + vec![], + ); + invoke(&instruction, accounts)?; + } + + msg!("Test refcell usage"); + { + let writable = INVOKED_ARGUMENT_INDEX; + let readable = INVOKED_PROGRAM_INDEX; + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[writable].key, true, true), + (accounts[readable].key, false, false), + ], + vec![RETURN_OK, 1, 2, 3, 4, 5], + ); + + // success with this account configuration as a check + invoke(&instruction, accounts)?; + + { + // writable but lamports borrow_mut'd + let _ref_mut = accounts[writable].try_borrow_mut_lamports()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // writable but data borrow_mut'd + let _ref_mut = accounts[writable].try_borrow_mut_data()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // writable but lamports borrow'd + let _ref_mut = accounts[writable].try_borrow_lamports()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // writable but data borrow'd + let _ref_mut = accounts[writable].try_borrow_data()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // readable but lamports borrow_mut'd + let _ref_mut = accounts[readable].try_borrow_mut_lamports()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // readable but data borrow_mut'd + let _ref_mut = accounts[readable].try_borrow_mut_data()?; + assert_eq!( + invoke(&instruction, accounts), + Err(ProgramError::AccountBorrowFailed) + ); + } + { + // readable but lamports borrow'd + let _ref_mut = accounts[readable].try_borrow_lamports()?; + invoke(&instruction, accounts)?; + } + { + // readable but data borrow'd + let _ref_mut = accounts[readable].try_borrow_data()?; + invoke(&instruction, accounts)?; + } + } + + msg!("Test create_program_address"); + { + assert_eq!( + &Pubkey::create_program_address( + &[b"You pass butter", &[bump_seed1]], + program_id + )?, + accounts[DERIVED_KEY1_INDEX].key + ); + let new_program_id = Pubkey::new_from_array([6u8; 32]); + assert_eq!( + Pubkey::create_program_address(&[b"You pass butter"], &new_program_id) + .unwrap_err(), + PubkeyError::InvalidSeeds + ); + } + + msg!("Test try_find_program_address"); + { + let (address, bump_seed) = + Pubkey::try_find_program_address(&[b"You pass butter"], program_id).unwrap(); + assert_eq!(&address, accounts[DERIVED_KEY1_INDEX].key); + assert_eq!(bump_seed, bump_seed1); + let new_program_id = Pubkey::new_from_array([6u8; 32]); + assert_eq!( + Pubkey::create_program_address(&[b"You pass butter"], &new_program_id) + .unwrap_err(), + PubkeyError::InvalidSeeds + ); + } + + msg!("Test derived signers"); + { + assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); + + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[DERIVED_KEY1_INDEX].key, true, true), + (accounts[DERIVED_KEY2_INDEX].key, true, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + ], + vec![DERIVED_SIGNERS, bump_seed2, bump_seed3], + ); + invoke_signed( + &invoked_instruction, + accounts, + &[&[b"You pass butter", &[bump_seed1]]], + )?; + } + + msg!("Test readonly with writable account"); + { + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, false, true)], + vec![VERIFY_WRITER], + ); + invoke(&invoked_instruction, accounts)?; + } + + msg!("Test nested invoke"); + { + do_nested_invokes(4, accounts)?; + } + + msg!("Test privilege deescalation"); + { + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], + vec![VERIFY_PRIVILEGE_DEESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + } + + msg!("Verify data values are retained and updated"); + { + let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..100 { + assert_eq!(data[i as usize], i); + } + let data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..10 { + assert_eq!(data[i as usize], i); + } + } + + msg!("Verify data write before cpi call with deescalated writable"); + { + { + let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; + for i in 0..100 { + data[i as usize] = 42; + } + } + + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, false, false)], + vec![VERIFY_PRIVILEGE_DEESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..100 { + assert_eq!(data[i as usize], 42); + } + } + + msg!("Create account and init data"); + { + let from_lamports = accounts[FROM_INDEX].lamports(); + let to_lamports = accounts[DERIVED_KEY2_INDEX].lamports(); + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[FROM_INDEX].key, true, true), + (accounts[DERIVED_KEY2_INDEX].key, true, false), + (accounts[SYSTEM_PROGRAM_INDEX].key, false, false), + ], + vec![CREATE_AND_INIT, bump_seed2], + ); + invoke(&instruction, accounts)?; + + assert_eq!( + accounts[FROM_INDEX].lamports(), + from_lamports.saturating_sub(1) + ); + assert_eq!( + accounts[DERIVED_KEY2_INDEX].lamports(), + to_lamports.saturating_add(1) + ); + let data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; + assert_eq!(data[0], 0x0e); + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0x0f); + for i in 1..20 { + assert_eq!(data[i], i as u8); + } + } + + msg!("Test return data via invoked"); + { + // this should be cleared on entry, the invoked tests for this + set_return_data(b"x"); + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, false, true)], + vec![SET_RETURN_DATA], + ); + let _ = invoke(&instruction, accounts); + + assert_eq!( + get_return_data(), + Some(( + *accounts[INVOKED_PROGRAM_INDEX].key, + b"Set by invoked".to_vec() + )) + ); + } + + msg!("Test accounts re-ordering"); + { + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[FROM_INDEX].key, true, true)], + vec![RETURN_OK], + ); + // put the relevant account at the end of a larger account list + let mut reordered_accounts = accounts.to_vec(); + let account_info = reordered_accounts.remove(FROM_INDEX); + reordered_accounts.push(accounts[0].clone()); + reordered_accounts.push(account_info); + invoke(&instruction, &reordered_accounts)?; + } + } + TEST_PRIVILEGE_ESCALATION_SIGNER => { + msg!("Test privilege escalation signer"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + // Signer privilege escalation will always fail the whole transaction + invoked_instruction.accounts[0].is_signer = true; + invoke(&invoked_instruction, accounts)?; + } + TEST_PRIVILEGE_ESCALATION_WRITABLE => { + msg!("Test privilege escalation writable"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + // Writable privilege escalation will always fail the whole transaction + invoked_instruction.accounts[0].is_writable = true; + invoke(&invoked_instruction, accounts)?; + } + TEST_PPROGRAM_NOT_EXECUTABLE => { + msg!("Test program not executable"); + let instruction = create_instruction( + *accounts[ARGUMENT_INDEX].key, + &[(accounts[ARGUMENT_INDEX].key, true, true)], + vec![RETURN_OK], + ); + invoke(&instruction, accounts)?; + } + TEST_EMPTY_ACCOUNTS_SLICE => { + msg!("Empty accounts slice"); + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], + vec![], + ); + invoke(&instruction, &[])?; + } + TEST_CAP_SEEDS => { + msg!("Test program max seeds"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + invoke_signed( + &instruction, + accounts, + &[&[ + b"1", b"2", b"3", b"4", b"5", b"6", b"7", b"8", b"9", b"0", b"1", b"2", b"3", + b"4", b"5", b"6", b"7", + ]], + )?; + } + TEST_CAP_SIGNERS => { + msg!("Test program max signers"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + invoke_signed( + &instruction, + accounts, + &[ + &[b"1"], + &[b"2"], + &[b"3"], + &[b"4"], + &[b"5"], + &[b"6"], + &[b"7"], + &[b"8"], + &[b"9"], + &[b"0"], + &[b"1"], + &[b"2"], + &[b"3"], + &[b"4"], + &[b"5"], + &[b"6"], + &[b"7"], + ], + )?; + } + TEST_ALLOC_ACCESS_VIOLATION => { + msg!("Test resize violation"); + let pubkey = *accounts[FROM_INDEX].key; + let owner = *accounts[FROM_INDEX].owner; + let ptr = accounts[FROM_INDEX].data.borrow().as_ptr() as u64 as *mut _; + let len = accounts[FROM_INDEX].data_len(); + let data = unsafe { std::slice::from_raw_parts_mut(ptr, len) }; + let mut lamports = accounts[FROM_INDEX].lamports(); + let from_info = + AccountInfo::new(&pubkey, false, true, &mut lamports, data, &owner, false, 0); + + let pubkey = *accounts[DERIVED_KEY1_INDEX].key; + let owner = *accounts[DERIVED_KEY1_INDEX].owner; + // Point to top edge of heap, attempt to allocate into unprivileged memory + let data = unsafe { std::slice::from_raw_parts_mut(0x300007ff8 as *mut _, 0) }; + let mut lamports = accounts[DERIVED_KEY1_INDEX].lamports(); + let derived_info = + AccountInfo::new(&pubkey, false, true, &mut lamports, data, &owner, false, 0); + + let pubkey = *accounts[SYSTEM_PROGRAM_INDEX].key; + let owner = *accounts[SYSTEM_PROGRAM_INDEX].owner; + let ptr = accounts[SYSTEM_PROGRAM_INDEX].data.borrow().as_ptr() as u64 as *mut _; + let len = accounts[SYSTEM_PROGRAM_INDEX].data_len(); + let data = unsafe { std::slice::from_raw_parts_mut(ptr, len) }; + let mut lamports = accounts[SYSTEM_PROGRAM_INDEX].lamports(); + let system_info = + AccountInfo::new(&pubkey, false, false, &mut lamports, data, &owner, true, 0); + + let instruction = system_instruction::create_account( + accounts[FROM_INDEX].key, + accounts[DERIVED_KEY1_INDEX].key, + 42, + MAX_PERMITTED_DATA_INCREASE as u64, + program_id, + ); + + invoke_signed( + &instruction, + &[system_info.clone(), from_info.clone(), derived_info.clone()], + &[&[b"You pass butter", &[bump_seed1]]], + )?; + } + TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED => { + msg!("Test max instruction data len exceeded"); + let data_len = MAX_CPI_INSTRUCTION_DATA_LEN.saturating_add(1) as usize; + let instruction = + create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; data_len]); + invoke_signed(&instruction, &[], &[])?; + } + TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED => { + msg!("Test max instruction accounts exceeded"); + let default_key = Pubkey::default(); + let account_metas_len = (MAX_CPI_INSTRUCTION_ACCOUNTS as usize).saturating_add(1); + let account_metas = vec![(&default_key, false, false); account_metas_len]; + let instruction = + create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &account_metas, vec![]); + invoke_signed(&instruction, &[], &[])?; + } + TEST_MAX_ACCOUNT_INFOS_EXCEEDED => { + msg!("Test max account infos exceeded"); + let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); + let account_infos_len = MAX_CPI_ACCOUNT_INFOS.saturating_add(1); + let account_infos = vec![accounts[0].clone(); account_infos_len]; + invoke_signed(&instruction, &account_infos, &[])?; + } + TEST_RETURN_ERROR => { + msg!("Test return error"); + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)], + vec![RETURN_ERROR], + ); + let _ = invoke(&instruction, accounts); + } + TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { + msg!("Test privilege deescalation escalation signer"); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER], + ); + invoke(&invoked_instruction, accounts)?; + } + TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { + msg!("Test privilege deescalation escalation writable"); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE], + ); + invoke(&invoked_instruction, accounts)?; + } + TEST_WRITABLE_DEESCALATION_WRITABLE => { + msg!("Test writable deescalation writable"); + const NUM_BYTES: usize = 10; + let mut buffer = [0; NUM_BYTES]; + buffer.copy_from_slice(&accounts[INVOKED_ARGUMENT_INDEX].data.borrow()[..NUM_BYTES]); + + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], + vec![WRITE_ACCOUNT, NUM_BYTES as u8], + ); + let _ = invoke(&instruction, accounts); + + assert_eq!( + buffer, + accounts[INVOKED_ARGUMENT_INDEX].data.borrow()[..NUM_BYTES] + ); + } + TEST_NESTED_INVOKE_TOO_DEEP => { + let _ = do_nested_invokes(5, accounts); + } + TEST_CALL_PRECOMPILE => { + msg!("Test calling precompiled program from cpi"); + let instruction = + Instruction::new_with_bytes(*accounts[ED25519_PROGRAM_INDEX].key, &[], vec![]); + invoke(&instruction, accounts)?; + } + ADD_LAMPORTS => { + // make sure the total balance is fine + { + let mut lamports = (*accounts[0].lamports).borrow_mut(); + **lamports = (*lamports).saturating_add(1); + } + } + TEST_RETURN_DATA_TOO_LARGE => { + set_return_data(&[1u8; 1028]); + } + TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER => { + msg!("Test duplicate privilege escalation signer"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[DERIVED_KEY3_INDEX].key, false, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + // Signer privilege escalation will always fail the whole transaction + invoked_instruction.accounts[1].is_signer = true; + invoke(&invoked_instruction, accounts)?; + } + TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE => { + msg!("Test duplicate privilege escalation writable"); + let mut invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[DERIVED_KEY3_INDEX].key, false, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + (accounts[DERIVED_KEY3_INDEX].key, false, false), + ], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + + // Writable privilege escalation will always fail the whole transaction + invoked_instruction.accounts[1].is_writable = true; + invoke(&invoked_instruction, accounts)?; + } + TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE => { + msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE"); + invoke( + &create_instruction( + *program_id, + &[ + (program_id, false, false), + (accounts[ARGUMENT_INDEX].key, true, false), + ], + vec![ + TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED, + 42, + 42, + 42, + ], + ), + accounts, + ) + .unwrap(); + let account = &accounts[ARGUMENT_INDEX]; + // this should cause the tx to fail since the callee changed ownership + unsafe { + *account + .data + .borrow_mut() + .get_unchecked_mut(instruction_data[1] as usize) = 42 + }; + } + TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED => { + msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED"); + let account = &accounts[ARGUMENT_INDEX]; + account.data.borrow_mut().fill(0); + account.assign(&system_program::id()); + } + TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER => { + msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER"); + let account = &accounts[ARGUMENT_INDEX]; + let invoked_program_id = accounts[INVOKED_PROGRAM_INDEX].key; + account.data.borrow_mut().fill(0); + account.assign(invoked_program_id); + invoke( + &create_instruction( + *invoked_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (invoked_program_id, false, false), + ], + vec![RETURN_OK], + ), + accounts, + ) + .unwrap(); + // this should cause the tx to failsince invoked_program_id now owns + // the account + unsafe { + *account + .data + .borrow_mut() + .get_unchecked_mut(instruction_data[1] as usize) = 42 + }; + } + TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER => { + msg!("TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER"); + const INVOKE_PROGRAM_INDEX: usize = 3; + const REALLOC_PROGRAM_INDEX: usize = 4; + let account = &accounts[ARGUMENT_INDEX]; + let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + account.realloc(0, false).unwrap(); + account.assign(realloc_program_id); + + // Place a RcBox> in the account data. This + // allows us to test having CallerAccount::ref_to_len_in_vm in an + // account region. + let rc_box_addr = + account.data.borrow_mut().as_mut_ptr() as *mut RcBox>; + let rc_box_size = mem::size_of::>>(); + unsafe { + std::ptr::write( + rc_box_addr, + RcBox { + strong: 1, + weak: 0, + // We're testing what happens if we make CPI update the + // slice length after we put the slice in the account + // address range. To do so, we need to move the data + // pointer past the RcBox or CPI will clobber the length + // change when it copies the callee's account data back + // into the caller's account data + // https://github.com/solana-labs/solana/blob/fa28958bd69054d1c2348e0a731011e93d44d7af/programs/bpf_loader/src/syscalls/cpi.rs#L1487 + value: RefCell::new(slice::from_raw_parts_mut( + account.data.borrow_mut().as_mut_ptr().add(rc_box_size), + 0, + )), + }, + ); + } + + // CPI now will update the serialized length in the wrong place, + // since we moved the account data slice. To hit the corner case we + // want to hit, we'll need to update the serialized length manually + // or during deserialize_parameters() we'll get + // AccountDataSizeChanged + let serialized_len_ptr = + unsafe { account.data.borrow_mut().as_mut_ptr().offset(-8) as *mut u64 }; + + unsafe { + overwrite_account_data( + account, + Rc::from_raw(((rc_box_addr as usize) + mem::size_of::() * 2) as *mut _), + ); + } + + let mut instruction_data = vec![REALLOC, 0]; + instruction_data.extend_from_slice(&rc_box_size.to_le_bytes()); + + // check that the account is empty before we CPI + assert_eq!(account.data_len(), 0); + + invoke( + &create_instruction( + *realloc_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (realloc_program_id, false, false), + (invoke_program_id, false, false), + ], + instruction_data.to_vec(), + ), + accounts, + ) + .unwrap(); + + // verify that CPI did update `ref_to_len_in_vm` + assert_eq!(account.data_len(), rc_box_size); + + // update the serialized length so we don't error out early with AccountDataSizeChanged + unsafe { *serialized_len_ptr = rc_box_size as u64 }; + + // hack to avoid dropping the RcBox, which is supposed to be on the + // heap but we put it into account data. If we don't do this, + // dropping the Rc will cause + // global_deallocator.dealloc(rc_box_addr) which is invalid and + // happens to write a poison value into the account. + unsafe { + overwrite_account_data(account, Rc::new(RefCell::new(&mut []))); + } + } + TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE => { + msg!("TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE"); + const INVOKE_PROGRAM_INDEX: usize = 3; + const REALLOC_PROGRAM_INDEX: usize = 4; + let account = &accounts[ARGUMENT_INDEX]; + let target_account_index = instruction_data[1] as usize; + let target_account = &accounts[target_account_index]; + let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + account.realloc(0, false).unwrap(); + account.assign(realloc_program_id); + target_account.realloc(0, false).unwrap(); + target_account.assign(realloc_program_id); + + let rc_box_addr = + target_account.data.borrow_mut().as_mut_ptr() as *mut RcBox>; + let rc_box_size = mem::size_of::>>(); + unsafe { + std::ptr::write( + rc_box_addr, + RcBox { + strong: 1, + weak: 0, + // The difference with + // TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER + // is that we don't move the data pointer past the + // RcBox. This is needed to avoid the "Invalid account + // info pointer" check when direct mapping is enabled. + // This also means we don't need to update the + // serialized len like we do in the other test. + value: RefCell::new(slice::from_raw_parts_mut( + account.data.borrow_mut().as_mut_ptr(), + 0, + )), + }, + ); + } + + let serialized_len_ptr = + unsafe { account.data.borrow_mut().as_mut_ptr().offset(-8) as *mut u64 }; + // Place a RcBox> in the account data. This + // allows us to test having CallerAccount::ref_to_len_in_vm in an + // account region. + unsafe { + overwrite_account_data( + account, + Rc::from_raw(((rc_box_addr as usize) + mem::size_of::() * 2) as *mut _), + ); + } + + let mut instruction_data = vec![REALLOC, 0]; + instruction_data.extend_from_slice(&rc_box_size.to_le_bytes()); + + // check that the account is empty before we CPI + assert_eq!(account.data_len(), 0); + + invoke( + &create_instruction( + *realloc_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (target_account.key, true, false), + (realloc_program_id, false, false), + (invoke_program_id, false, false), + ], + instruction_data.to_vec(), + ), + accounts, + ) + .unwrap(); + + unsafe { *serialized_len_ptr = rc_box_size as u64 }; + // hack to avoid dropping the RcBox, which is supposed to be on the + // heap but we put it into account data. If we don't do this, + // dropping the Rc will cause + // global_deallocator.dealloc(rc_box_addr) which is invalid and + // happens to write a poison value into the account. + unsafe { + overwrite_account_data(account, Rc::new(RefCell::new(&mut []))); + } + } + TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER => { + msg!("TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER"); + const INVOKE_PROGRAM_INDEX: usize = 3; + let account = &accounts[ARGUMENT_INDEX]; + let invoked_program_id = accounts[INVOKED_PROGRAM_INDEX].key; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + invoke( + &create_instruction( + *invoked_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (invoked_program_id, false, false), + (invoke_program_id, false, false), + ], + vec![ASSIGN_ACCOUNT_TO_CALLER], + ), + accounts, + ) + .unwrap(); + // this should succeed since the callee gave us ownership of the + // account + unsafe { + *account + .data + .borrow_mut() + .get_unchecked_mut(instruction_data[1] as usize) = 42 + }; + } + TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS"); + const CALLEE_PROGRAM_INDEX: usize = 3; + let account = &accounts[ARGUMENT_INDEX]; + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + + let expected = { + let data = &instruction_data[1..]; + let prev_len = account.data_len(); + account.realloc(prev_len + data.len(), false)?; + account.data.borrow_mut()[prev_len..].copy_from_slice(data); + account.data.borrow().to_vec() + }; + + let mut instruction_data = vec![TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED]; + instruction_data.extend_from_slice(&expected); + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + instruction_data, + ), + accounts, + ) + .unwrap(); + } + TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED"); + const ARGUMENT_INDEX: usize = 0; + let account = &accounts[ARGUMENT_INDEX]; + assert_eq!(*account.data.borrow(), &instruction_data[1..]); + } + TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS"); + const REALLOC_PROGRAM_INDEX: usize = 2; + const INVOKE_PROGRAM_INDEX: usize = 3; + let account = &accounts[ARGUMENT_INDEX]; + let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; + let realloc_program_owner = accounts[REALLOC_PROGRAM_INDEX].owner; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + let mut instruction_data = instruction_data.to_vec(); + let mut expected = account.data.borrow().to_vec(); + expected.extend_from_slice(&instruction_data[1..]); + instruction_data[0] = REALLOC_EXTEND_FROM_SLICE; + invoke( + &create_instruction( + *realloc_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (realloc_program_id, false, false), + (invoke_program_id, false, false), + ], + instruction_data.to_vec(), + ), + accounts, + ) + .unwrap(); + + if !bpf_loader_deprecated::check_id(realloc_program_owner) { + assert_eq!(&*account.data.borrow(), &expected); + } + } + TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN"); + const REALLOC_PROGRAM_INDEX: usize = 2; + const INVOKE_PROGRAM_INDEX: usize = 3; + let account = &accounts[ARGUMENT_INDEX]; + let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; + let realloc_program_owner = accounts[REALLOC_PROGRAM_INDEX].owner; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); + let prev_len = account.data_len(); + let expected = account.data.borrow()[..new_len].to_vec(); + let mut instruction_data = vec![REALLOC, 0]; + instruction_data.extend_from_slice(&new_len.to_le_bytes()); + invoke( + &create_instruction( + *realloc_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (realloc_program_id, false, false), + (invoke_program_id, false, false), + ], + instruction_data, + ), + accounts, + ) + .unwrap(); + + // deserialize_parameters_unaligned predates realloc support, and + // hardcodes the account data length to the original length. + if !bpf_loader_deprecated::check_id(realloc_program_owner) { + assert_eq!(&*account.data.borrow(), &expected); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + prev_len - new_len, + ) + }, + &vec![0; prev_len - new_len] + ); + } + } + TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS"); + const INVOKE_PROGRAM_INDEX: usize = 3; + const SENTINEL: u8 = 42; + let account = &accounts[ARGUMENT_INDEX]; + let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; + + let prev_data = { + let data = &instruction_data[9..]; + let prev_len = account.data_len(); + account.realloc(prev_len + data.len(), false)?; + account.data.borrow_mut()[prev_len..].copy_from_slice(data); + unsafe { + // write a sentinel value just outside the account data to + // check that when CPI zeroes the realloc region it doesn't + // zero too much + *account + .data + .borrow_mut() + .as_mut_ptr() + .add(prev_len + data.len()) = SENTINEL; + }; + account.data.borrow().to_vec() + }; + + let mut expected = account.data.borrow().to_vec(); + let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); + expected.extend_from_slice(&instruction_data[9..]); + let mut instruction_data = + vec![TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED]; + instruction_data.extend_from_slice(&new_len.to_le_bytes()); + invoke( + &create_instruction( + *invoke_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (invoke_program_id, false, false), + ], + instruction_data, + ), + accounts, + ) + .unwrap(); + + assert_eq!(*account.data.borrow(), &prev_data[..new_len]); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + prev_data.len() - new_len, + ) + }, + &vec![0; prev_data.len() - new_len] + ); + assert_eq!( + unsafe { *account.data.borrow().as_ptr().add(prev_data.len()) }, + SENTINEL + ); + } + TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED => { + msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED"); + const ARGUMENT_INDEX: usize = 0; + let account = &accounts[ARGUMENT_INDEX]; + let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); + account.realloc(new_len, false).unwrap(); + } + TEST_CPI_INVALID_KEY_POINTER => { + msg!("TEST_CPI_INVALID_KEY_POINTER"); + const CALLEE_PROGRAM_INDEX: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let key = *account.key; + let key = &key as *const _ as usize; + #[rustversion::attr(since(1.72), allow(invalid_reference_casting))] + fn overwrite_account_key(account: &AccountInfo, key: *const Pubkey) { + unsafe { + let ptr = mem::transmute::<_, *mut *const Pubkey>(&account.key); + std::ptr::write_volatile(ptr, key); + } + } + overwrite_account_key(&accounts[ARGUMENT_INDEX], key as *const Pubkey); + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![], + ), + accounts, + ) + .unwrap(); + } + TEST_CPI_INVALID_LAMPORTS_POINTER => { + msg!("TEST_CPI_INVALID_LAMPORTS_POINTER"); + const CALLEE_PROGRAM_INDEX: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let mut lamports = account.lamports(); + account + .lamports + .replace(unsafe { mem::transmute::<&'_ mut u64, &'a mut u64>(&mut lamports) }); + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![], + ), + accounts, + ) + .unwrap(); + } + TEST_CPI_INVALID_OWNER_POINTER => { + msg!("TEST_CPI_INVALID_OWNER_POINTER"); + const CALLEE_PROGRAM_INDEX: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let owner = account.owner as *const _ as usize + 1; + #[rustversion::attr(since(1.72), allow(invalid_reference_casting))] + fn overwrite_account_owner(account: &AccountInfo, owner: *const Pubkey) { + unsafe { + let ptr = mem::transmute::<_, *mut *const Pubkey>(&account.owner); + std::ptr::write_volatile(ptr, owner); + } + } + overwrite_account_owner(account, owner as *const Pubkey); + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![], + ), + accounts, + ) + .unwrap(); + } + TEST_CPI_INVALID_DATA_POINTER => { + msg!("TEST_CPI_INVALID_DATA_POINTER"); + const CALLEE_PROGRAM_INDEX: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let data = unsafe { + slice::from_raw_parts_mut(account.data.borrow_mut()[1..].as_mut_ptr(), 0) + }; + account.data.replace(data); + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![], + ), + accounts, + ) + .unwrap(); + } + TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION => { + msg!("TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION"); + const CALLEE_PROGRAM_INDEX: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; + let original_data_len = account.data_len(); + + // Initial data is all [0xFF; 20] + assert_eq!(&*account.data.borrow(), &[0xFF; 20]); + + // Realloc to [0xFE; 10] + invoke( + &create_instruction( + *callee_program_id, + &[ + (account.key, true, false), + (callee_program_id, false, false), + ], + vec![0xFE; 10], + ), + accounts, + ) + .unwrap(); + + // Check that [10..20] is zeroed + let new_len = account.data_len(); + assert_eq!(&*account.data.borrow(), &[0xFE; 10]); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + original_data_len - new_len, + ) + }, + &vec![0; original_data_len - new_len] + ); + + // Realloc to [0xFD; 5] + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![0xFD; 5], + ), + accounts, + ) + .unwrap(); + + // Check that [5..20] is zeroed + let new_len = account.data_len(); + assert_eq!(&*account.data.borrow(), &[0xFD; 5]); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + original_data_len - new_len, + ) + }, + &vec![0; original_data_len - new_len] + ); + + // Realloc to [0xFC; 2] + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![0xFC; 2], + ), + accounts, + ) + .unwrap(); + + // Check that [2..20] is zeroed + let new_len = account.data_len(); + assert_eq!(&*account.data.borrow(), &[0xFC; 2]); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + original_data_len - new_len, + ) + }, + &vec![0; original_data_len - new_len] + ); + + // Realloc to [0xFC; 2]. Here we keep the same length, but realloc the underlying + // vector. CPI must zero even if the length is unchanged. + invoke( + &create_instruction( + *callee_program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (callee_program_id, false, false), + ], + vec![0xFC; 2], + ), + accounts, + ) + .unwrap(); + + // Check that [2..20] is zeroed + let new_len = account.data_len(); + assert_eq!(&*account.data.borrow(), &[0xFC; 2]); + assert_eq!( + unsafe { + slice::from_raw_parts( + account.data.borrow().as_ptr().add(new_len), + original_data_len - new_len, + ) + }, + &vec![0; original_data_len - new_len] + ); + } + TEST_WRITE_ACCOUNT => { + msg!("TEST_WRITE_ACCOUNT"); + let target_account_index = instruction_data[1] as usize; + let target_account = &accounts[target_account_index]; + let byte_index = usize::from_le_bytes(instruction_data[2..10].try_into().unwrap()); + target_account.data.borrow_mut()[byte_index] = instruction_data[10]; + } + _ => panic!("unexpected program data"), + } + + Ok(()) +} + +#[repr(C)] +struct RcBox { + strong: usize, + weak: usize, + value: T, +} + +#[rustversion::attr(since(1.72), allow(invalid_reference_casting))] +unsafe fn overwrite_account_data(account: &AccountInfo, data: Rc>) { + std::ptr::write_volatile( + &account.data as *const _ as usize as *mut Rc>, + data, + ); +} diff --git a/programs/sbf/rust/invoke/src/processor.rs b/programs/sbf/rust/invoke/src/processor.rs deleted file mode 100644 index e441ba872b3b81..00000000000000 --- a/programs/sbf/rust/invoke/src/processor.rs +++ /dev/null @@ -1,1373 +0,0 @@ -//! Example Rust-based SBF program that issues a cross-program-invocation - -#![cfg(feature = "program")] -#![allow(unreachable_code)] -#![allow(clippy::arithmetic_side_effects)] - -use { - crate::instructions::*, - solana_program::{ - account_info::AccountInfo, - bpf_loader_deprecated, - entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, - instruction::Instruction, - msg, - program::{get_return_data, invoke, invoke_signed, set_return_data}, - program_error::ProgramError, - pubkey::{Pubkey, PubkeyError}, - syscalls::{ - MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN, - }, - system_instruction, system_program, - }, - solana_sbf_rust_invoked::instructions::*, - solana_sbf_rust_realloc::instructions::*, - std::{cell::RefCell, mem, rc::Rc, slice}, -}; - -fn do_nested_invokes(num_nested_invokes: u64, accounts: &[AccountInfo]) -> ProgramResult { - assert!(accounts[ARGUMENT_INDEX].is_signer); - - let pre_argument_lamports = accounts[ARGUMENT_INDEX].lamports(); - let pre_invoke_argument_lamports = accounts[INVOKED_ARGUMENT_INDEX].lamports(); - { - let mut lamports = (*accounts[ARGUMENT_INDEX].lamports).borrow_mut(); - **lamports = (*lamports).saturating_sub(5); - let mut lamports = (*accounts[INVOKED_ARGUMENT_INDEX].lamports).borrow_mut(); - **lamports = (*lamports).saturating_add(5); - } - - msg!("First invoke"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - ], - vec![NESTED_INVOKE, num_nested_invokes as u8], - ); - invoke(&instruction, accounts)?; - msg!("2nd invoke from first program"); - invoke(&instruction, accounts)?; - - assert_eq!( - accounts[ARGUMENT_INDEX].lamports(), - pre_argument_lamports - .saturating_sub(5) - .saturating_add(2_u64.saturating_mul(num_nested_invokes)) - ); - assert_eq!( - accounts[INVOKED_ARGUMENT_INDEX].lamports(), - pre_invoke_argument_lamports - .saturating_add(5) - .saturating_sub(2_u64.saturating_mul(num_nested_invokes)) - ); - Ok(()) -} - -solana_program::entrypoint!(process_instruction); -fn process_instruction<'a>( - program_id: &Pubkey, - accounts: &[AccountInfo<'a>], - instruction_data: &[u8], -) -> ProgramResult { - msg!("invoke Rust program"); - - let bump_seed1 = instruction_data[1]; - let bump_seed2 = instruction_data[2]; - let bump_seed3 = instruction_data[3]; - - match instruction_data[0] { - TEST_SUCCESS => { - msg!("Call system program create account"); - { - let from_lamports = accounts[FROM_INDEX].lamports(); - let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports(); - assert_eq!(accounts[DERIVED_KEY1_INDEX].data_len(), 0); - assert!(solana_program::system_program::check_id( - accounts[DERIVED_KEY1_INDEX].owner - )); - - let instruction = system_instruction::create_account( - accounts[FROM_INDEX].key, - accounts[DERIVED_KEY1_INDEX].key, - 42, - MAX_PERMITTED_DATA_INCREASE as u64, - program_id, - ); - invoke_signed( - &instruction, - accounts, - &[&[b"You pass butter", &[bump_seed1]]], - )?; - - assert_eq!( - accounts[FROM_INDEX].lamports(), - from_lamports.saturating_sub(42) - ); - assert_eq!( - accounts[DERIVED_KEY1_INDEX].lamports(), - to_lamports.saturating_add(42) - ); - assert_eq!(program_id, accounts[DERIVED_KEY1_INDEX].owner); - assert_eq!( - accounts[DERIVED_KEY1_INDEX].data_len(), - MAX_PERMITTED_DATA_INCREASE - ); - let mut data = accounts[DERIVED_KEY1_INDEX].try_borrow_mut_data()?; - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0); - data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)] = 0x0f; - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0x0f); - for i in 0..20 { - data[i] = i as u8; - } - } - - msg!("Call system program transfer"); - { - let from_lamports = accounts[FROM_INDEX].lamports(); - let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports(); - let instruction = system_instruction::transfer( - accounts[FROM_INDEX].key, - accounts[DERIVED_KEY1_INDEX].key, - 1, - ); - invoke(&instruction, accounts)?; - assert_eq!( - accounts[FROM_INDEX].lamports(), - from_lamports.saturating_sub(1) - ); - assert_eq!( - accounts[DERIVED_KEY1_INDEX].lamports(), - to_lamports.saturating_add(1) - ); - } - - msg!("Test data translation"); - { - { - let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; - for i in 0..100 { - data[i as usize] = i; - } - } - - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[INVOKED_PROGRAM_DUP_INDEX].key, false, false), - ], - vec![VERIFY_TRANSLATIONS, 1, 2, 3, 4, 5], - ); - invoke(&instruction, accounts)?; - } - - msg!("Test no instruction data"); - { - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, true, true)], - vec![], - ); - invoke(&instruction, accounts)?; - } - - msg!("Test refcell usage"); - { - let writable = INVOKED_ARGUMENT_INDEX; - let readable = INVOKED_PROGRAM_INDEX; - - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[writable].key, true, true), - (accounts[readable].key, false, false), - ], - vec![RETURN_OK, 1, 2, 3, 4, 5], - ); - - // success with this account configuration as a check - invoke(&instruction, accounts)?; - - { - // writable but lamports borrow_mut'd - let _ref_mut = accounts[writable].try_borrow_mut_lamports()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // writable but data borrow_mut'd - let _ref_mut = accounts[writable].try_borrow_mut_data()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // writable but lamports borrow'd - let _ref_mut = accounts[writable].try_borrow_lamports()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // writable but data borrow'd - let _ref_mut = accounts[writable].try_borrow_data()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // readable but lamports borrow_mut'd - let _ref_mut = accounts[readable].try_borrow_mut_lamports()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // readable but data borrow_mut'd - let _ref_mut = accounts[readable].try_borrow_mut_data()?; - assert_eq!( - invoke(&instruction, accounts), - Err(ProgramError::AccountBorrowFailed) - ); - } - { - // readable but lamports borrow'd - let _ref_mut = accounts[readable].try_borrow_lamports()?; - invoke(&instruction, accounts)?; - } - { - // readable but data borrow'd - let _ref_mut = accounts[readable].try_borrow_data()?; - invoke(&instruction, accounts)?; - } - } - - msg!("Test create_program_address"); - { - assert_eq!( - &Pubkey::create_program_address( - &[b"You pass butter", &[bump_seed1]], - program_id - )?, - accounts[DERIVED_KEY1_INDEX].key - ); - let new_program_id = Pubkey::new_from_array([6u8; 32]); - assert_eq!( - Pubkey::create_program_address(&[b"You pass butter"], &new_program_id) - .unwrap_err(), - PubkeyError::InvalidSeeds - ); - } - - msg!("Test try_find_program_address"); - { - let (address, bump_seed) = - Pubkey::try_find_program_address(&[b"You pass butter"], program_id).unwrap(); - assert_eq!(&address, accounts[DERIVED_KEY1_INDEX].key); - assert_eq!(bump_seed, bump_seed1); - let new_program_id = Pubkey::new_from_array([6u8; 32]); - assert_eq!( - Pubkey::create_program_address(&[b"You pass butter"], &new_program_id) - .unwrap_err(), - PubkeyError::InvalidSeeds - ); - } - - msg!("Test derived signers"); - { - assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); - - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[DERIVED_KEY1_INDEX].key, true, true), - (accounts[DERIVED_KEY2_INDEX].key, true, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - ], - vec![DERIVED_SIGNERS, bump_seed2, bump_seed3], - ); - invoke_signed( - &invoked_instruction, - accounts, - &[&[b"You pass butter", &[bump_seed1]]], - )?; - } - - msg!("Test readonly with writable account"); - { - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, false, true)], - vec![VERIFY_WRITER], - ); - invoke(&invoked_instruction, accounts)?; - } - - msg!("Test nested invoke"); - { - do_nested_invokes(4, accounts)?; - } - - msg!("Test privilege deescalation"); - { - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], - vec![VERIFY_PRIVILEGE_DEESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - } - - msg!("Verify data values are retained and updated"); - { - let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..100 { - assert_eq!(data[i as usize], i); - } - let data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..10 { - assert_eq!(data[i as usize], i); - } - } - - msg!("Verify data write before cpi call with deescalated writable"); - { - { - let mut data = accounts[ARGUMENT_INDEX].try_borrow_mut_data()?; - for i in 0..100 { - data[i as usize] = 42; - } - } - - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, false, false)], - vec![VERIFY_PRIVILEGE_DEESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - - let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..100 { - assert_eq!(data[i as usize], 42); - } - } - - msg!("Create account and init data"); - { - let from_lamports = accounts[FROM_INDEX].lamports(); - let to_lamports = accounts[DERIVED_KEY2_INDEX].lamports(); - - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[FROM_INDEX].key, true, true), - (accounts[DERIVED_KEY2_INDEX].key, true, false), - (accounts[SYSTEM_PROGRAM_INDEX].key, false, false), - ], - vec![CREATE_AND_INIT, bump_seed2], - ); - invoke(&instruction, accounts)?; - - assert_eq!( - accounts[FROM_INDEX].lamports(), - from_lamports.saturating_sub(1) - ); - assert_eq!( - accounts[DERIVED_KEY2_INDEX].lamports(), - to_lamports.saturating_add(1) - ); - let data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; - assert_eq!(data[0], 0x0e); - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE.saturating_sub(1)], 0x0f); - for i in 1..20 { - assert_eq!(data[i], i as u8); - } - } - - msg!("Test return data via invoked"); - { - // this should be cleared on entry, the invoked tests for this - set_return_data(b"x"); - - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, false, true)], - vec![SET_RETURN_DATA], - ); - let _ = invoke(&instruction, accounts); - - assert_eq!( - get_return_data(), - Some(( - *accounts[INVOKED_PROGRAM_INDEX].key, - b"Set by invoked".to_vec() - )) - ); - } - - msg!("Test accounts re-ordering"); - { - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[FROM_INDEX].key, true, true)], - vec![RETURN_OK], - ); - // put the relevant account at the end of a larger account list - let mut reordered_accounts = accounts.to_vec(); - let account_info = reordered_accounts.remove(FROM_INDEX); - reordered_accounts.push(accounts[0].clone()); - reordered_accounts.push(account_info); - invoke(&instruction, &reordered_accounts)?; - } - } - TEST_PRIVILEGE_ESCALATION_SIGNER => { - msg!("Test privilege escalation signer"); - let mut invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - - // Signer privilege escalation will always fail the whole transaction - invoked_instruction.accounts[0].is_signer = true; - invoke(&invoked_instruction, accounts)?; - } - TEST_PRIVILEGE_ESCALATION_WRITABLE => { - msg!("Test privilege escalation writable"); - let mut invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[DERIVED_KEY3_INDEX].key, false, false)], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - - // Writable privilege escalation will always fail the whole transaction - invoked_instruction.accounts[0].is_writable = true; - invoke(&invoked_instruction, accounts)?; - } - TEST_PPROGRAM_NOT_EXECUTABLE => { - msg!("Test program not executable"); - let instruction = create_instruction( - *accounts[ARGUMENT_INDEX].key, - &[(accounts[ARGUMENT_INDEX].key, true, true)], - vec![RETURN_OK], - ); - invoke(&instruction, accounts)?; - } - TEST_EMPTY_ACCOUNTS_SLICE => { - msg!("Empty accounts slice"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], - vec![], - ); - invoke(&instruction, &[])?; - } - TEST_CAP_SEEDS => { - msg!("Test program max seeds"); - let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); - invoke_signed( - &instruction, - accounts, - &[&[ - b"1", b"2", b"3", b"4", b"5", b"6", b"7", b"8", b"9", b"0", b"1", b"2", b"3", - b"4", b"5", b"6", b"7", - ]], - )?; - } - TEST_CAP_SIGNERS => { - msg!("Test program max signers"); - let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); - invoke_signed( - &instruction, - accounts, - &[ - &[b"1"], - &[b"2"], - &[b"3"], - &[b"4"], - &[b"5"], - &[b"6"], - &[b"7"], - &[b"8"], - &[b"9"], - &[b"0"], - &[b"1"], - &[b"2"], - &[b"3"], - &[b"4"], - &[b"5"], - &[b"6"], - &[b"7"], - ], - )?; - } - TEST_ALLOC_ACCESS_VIOLATION => { - msg!("Test resize violation"); - let pubkey = *accounts[FROM_INDEX].key; - let owner = *accounts[FROM_INDEX].owner; - let ptr = accounts[FROM_INDEX].data.borrow().as_ptr() as u64 as *mut _; - let len = accounts[FROM_INDEX].data_len(); - let data = unsafe { std::slice::from_raw_parts_mut(ptr, len) }; - let mut lamports = accounts[FROM_INDEX].lamports(); - let from_info = - AccountInfo::new(&pubkey, false, true, &mut lamports, data, &owner, false, 0); - - let pubkey = *accounts[DERIVED_KEY1_INDEX].key; - let owner = *accounts[DERIVED_KEY1_INDEX].owner; - // Point to top edge of heap, attempt to allocate into unprivileged memory - let data = unsafe { std::slice::from_raw_parts_mut(0x300007ff8 as *mut _, 0) }; - let mut lamports = accounts[DERIVED_KEY1_INDEX].lamports(); - let derived_info = - AccountInfo::new(&pubkey, false, true, &mut lamports, data, &owner, false, 0); - - let pubkey = *accounts[SYSTEM_PROGRAM_INDEX].key; - let owner = *accounts[SYSTEM_PROGRAM_INDEX].owner; - let ptr = accounts[SYSTEM_PROGRAM_INDEX].data.borrow().as_ptr() as u64 as *mut _; - let len = accounts[SYSTEM_PROGRAM_INDEX].data_len(); - let data = unsafe { std::slice::from_raw_parts_mut(ptr, len) }; - let mut lamports = accounts[SYSTEM_PROGRAM_INDEX].lamports(); - let system_info = - AccountInfo::new(&pubkey, false, false, &mut lamports, data, &owner, true, 0); - - let instruction = system_instruction::create_account( - accounts[FROM_INDEX].key, - accounts[DERIVED_KEY1_INDEX].key, - 42, - MAX_PERMITTED_DATA_INCREASE as u64, - program_id, - ); - - invoke_signed( - &instruction, - &[system_info.clone(), from_info.clone(), derived_info.clone()], - &[&[b"You pass butter", &[bump_seed1]]], - )?; - } - TEST_MAX_INSTRUCTION_DATA_LEN_EXCEEDED => { - msg!("Test max instruction data len exceeded"); - let data_len = MAX_CPI_INSTRUCTION_DATA_LEN.saturating_add(1) as usize; - let instruction = - create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; data_len]); - invoke_signed(&instruction, &[], &[])?; - } - TEST_MAX_INSTRUCTION_ACCOUNTS_EXCEEDED => { - msg!("Test max instruction accounts exceeded"); - let default_key = Pubkey::default(); - let account_metas_len = (MAX_CPI_INSTRUCTION_ACCOUNTS as usize).saturating_add(1); - let account_metas = vec![(&default_key, false, false); account_metas_len]; - let instruction = - create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &account_metas, vec![]); - invoke_signed(&instruction, &[], &[])?; - } - TEST_MAX_ACCOUNT_INFOS_EXCEEDED => { - msg!("Test max account infos exceeded"); - let instruction = create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![]); - let account_infos_len = MAX_CPI_ACCOUNT_INFOS.saturating_add(1); - let account_infos = vec![accounts[0].clone(); account_infos_len]; - invoke_signed(&instruction, &account_infos, &[])?; - } - TEST_RETURN_ERROR => { - msg!("Test return error"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)], - vec![RETURN_ERROR], - ); - let _ = invoke(&instruction, accounts); - } - TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { - msg!("Test privilege deescalation escalation signer"); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), - ], - vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER], - ); - invoke(&invoked_instruction, accounts)?; - } - TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { - msg!("Test privilege deescalation escalation writable"); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - (accounts[INVOKED_ARGUMENT_INDEX].key, false, false), - ], - vec![VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE], - ); - invoke(&invoked_instruction, accounts)?; - } - TEST_WRITABLE_DEESCALATION_WRITABLE => { - msg!("Test writable deescalation writable"); - const NUM_BYTES: usize = 10; - let mut buffer = [0; NUM_BYTES]; - buffer.copy_from_slice(&accounts[INVOKED_ARGUMENT_INDEX].data.borrow()[..NUM_BYTES]); - - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, false)], - vec![WRITE_ACCOUNT, NUM_BYTES as u8], - ); - let _ = invoke(&instruction, accounts); - - assert_eq!( - buffer, - accounts[INVOKED_ARGUMENT_INDEX].data.borrow()[..NUM_BYTES] - ); - } - TEST_NESTED_INVOKE_TOO_DEEP => { - let _ = do_nested_invokes(5, accounts); - } - TEST_CALL_PRECOMPILE => { - msg!("Test calling precompiled program from cpi"); - let instruction = - Instruction::new_with_bytes(*accounts[ED25519_PROGRAM_INDEX].key, &[], vec![]); - invoke(&instruction, accounts)?; - } - ADD_LAMPORTS => { - // make sure the total balance is fine - { - let mut lamports = (*accounts[0].lamports).borrow_mut(); - **lamports = (*lamports).saturating_add(1); - } - } - TEST_RETURN_DATA_TOO_LARGE => { - set_return_data(&[1u8; 1028]); - } - TEST_DUPLICATE_PRIVILEGE_ESCALATION_SIGNER => { - msg!("Test duplicate privilege escalation signer"); - let mut invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[DERIVED_KEY3_INDEX].key, false, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - ], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - - // Signer privilege escalation will always fail the whole transaction - invoked_instruction.accounts[1].is_signer = true; - invoke(&invoked_instruction, accounts)?; - } - TEST_DUPLICATE_PRIVILEGE_ESCALATION_WRITABLE => { - msg!("Test duplicate privilege escalation writable"); - let mut invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[DERIVED_KEY3_INDEX].key, false, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - (accounts[DERIVED_KEY3_INDEX].key, false, false), - ], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - - // Writable privilege escalation will always fail the whole transaction - invoked_instruction.accounts[1].is_writable = true; - invoke(&invoked_instruction, accounts)?; - } - TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE => { - msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE"); - invoke( - &create_instruction( - *program_id, - &[ - (program_id, false, false), - (accounts[ARGUMENT_INDEX].key, true, false), - ], - vec![ - TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED, - 42, - 42, - 42, - ], - ), - accounts, - ) - .unwrap(); - let account = &accounts[ARGUMENT_INDEX]; - // this should cause the tx to fail since the callee changed ownership - unsafe { - *account - .data - .borrow_mut() - .get_unchecked_mut(instruction_data[1] as usize) = 42 - }; - } - TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED => { - msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLEE_NESTED"); - let account = &accounts[ARGUMENT_INDEX]; - account.data.borrow_mut().fill(0); - account.assign(&system_program::id()); - } - TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER => { - msg!("TEST_FORBID_WRITE_AFTER_OWNERSHIP_CHANGE_IN_CALLER"); - let account = &accounts[ARGUMENT_INDEX]; - let invoked_program_id = accounts[INVOKED_PROGRAM_INDEX].key; - account.data.borrow_mut().fill(0); - account.assign(invoked_program_id); - invoke( - &create_instruction( - *invoked_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (invoked_program_id, false, false), - ], - vec![RETURN_OK], - ), - accounts, - ) - .unwrap(); - // this should cause the tx to failsince invoked_program_id now owns - // the account - unsafe { - *account - .data - .borrow_mut() - .get_unchecked_mut(instruction_data[1] as usize) = 42 - }; - } - TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER => { - msg!("TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER"); - const INVOKE_PROGRAM_INDEX: usize = 3; - const REALLOC_PROGRAM_INDEX: usize = 4; - let account = &accounts[ARGUMENT_INDEX]; - let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - account.realloc(0, false).unwrap(); - account.assign(realloc_program_id); - - // Place a RcBox> in the account data. This - // allows us to test having CallerAccount::ref_to_len_in_vm in an - // account region. - let rc_box_addr = - account.data.borrow_mut().as_mut_ptr() as *mut RcBox>; - let rc_box_size = mem::size_of::>>(); - unsafe { - std::ptr::write( - rc_box_addr, - RcBox { - strong: 1, - weak: 0, - // We're testing what happens if we make CPI update the - // slice length after we put the slice in the account - // address range. To do so, we need to move the data - // pointer past the RcBox or CPI will clobber the length - // change when it copies the callee's account data back - // into the caller's account data - // https://github.com/solana-labs/solana/blob/fa28958bd69054d1c2348e0a731011e93d44d7af/programs/bpf_loader/src/syscalls/cpi.rs#L1487 - value: RefCell::new(slice::from_raw_parts_mut( - account.data.borrow_mut().as_mut_ptr().add(rc_box_size), - 0, - )), - }, - ); - } - - // CPI now will update the serialized length in the wrong place, - // since we moved the account data slice. To hit the corner case we - // want to hit, we'll need to update the serialized length manually - // or during deserialize_parameters() we'll get - // AccountDataSizeChanged - let serialized_len_ptr = - unsafe { account.data.borrow_mut().as_mut_ptr().offset(-8) as *mut u64 }; - - unsafe { - overwrite_account_data( - account, - Rc::from_raw(((rc_box_addr as usize) + mem::size_of::() * 2) as *mut _), - ); - } - - let mut instruction_data = vec![REALLOC, 0]; - instruction_data.extend_from_slice(&rc_box_size.to_le_bytes()); - - // check that the account is empty before we CPI - assert_eq!(account.data_len(), 0); - - invoke( - &create_instruction( - *realloc_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (realloc_program_id, false, false), - (invoke_program_id, false, false), - ], - instruction_data.to_vec(), - ), - accounts, - ) - .unwrap(); - - // verify that CPI did update `ref_to_len_in_vm` - assert_eq!(account.data_len(), rc_box_size); - - // update the serialized length so we don't error out early with AccountDataSizeChanged - unsafe { *serialized_len_ptr = rc_box_size as u64 }; - - // hack to avoid dropping the RcBox, which is supposed to be on the - // heap but we put it into account data. If we don't do this, - // dropping the Rc will cause - // global_deallocator.dealloc(rc_box_addr) which is invalid and - // happens to write a poison value into the account. - unsafe { - overwrite_account_data(account, Rc::new(RefCell::new(&mut []))); - } - } - TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE => { - msg!("TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE"); - const INVOKE_PROGRAM_INDEX: usize = 3; - const REALLOC_PROGRAM_INDEX: usize = 4; - let account = &accounts[ARGUMENT_INDEX]; - let target_account_index = instruction_data[1] as usize; - let target_account = &accounts[target_account_index]; - let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - account.realloc(0, false).unwrap(); - account.assign(realloc_program_id); - target_account.realloc(0, false).unwrap(); - target_account.assign(realloc_program_id); - - let rc_box_addr = - target_account.data.borrow_mut().as_mut_ptr() as *mut RcBox>; - let rc_box_size = mem::size_of::>>(); - unsafe { - std::ptr::write( - rc_box_addr, - RcBox { - strong: 1, - weak: 0, - // The difference with - // TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE_MOVING_DATA_POINTER - // is that we don't move the data pointer past the - // RcBox. This is needed to avoid the "Invalid account - // info pointer" check when direct mapping is enabled. - // This also means we don't need to update the - // serialized len like we do in the other test. - value: RefCell::new(slice::from_raw_parts_mut( - account.data.borrow_mut().as_mut_ptr(), - 0, - )), - }, - ); - } - - let serialized_len_ptr = - unsafe { account.data.borrow_mut().as_mut_ptr().offset(-8) as *mut u64 }; - // Place a RcBox> in the account data. This - // allows us to test having CallerAccount::ref_to_len_in_vm in an - // account region. - unsafe { - overwrite_account_data( - account, - Rc::from_raw(((rc_box_addr as usize) + mem::size_of::() * 2) as *mut _), - ); - } - - let mut instruction_data = vec![REALLOC, 0]; - instruction_data.extend_from_slice(&rc_box_size.to_le_bytes()); - - // check that the account is empty before we CPI - assert_eq!(account.data_len(), 0); - - invoke( - &create_instruction( - *realloc_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (target_account.key, true, false), - (realloc_program_id, false, false), - (invoke_program_id, false, false), - ], - instruction_data.to_vec(), - ), - accounts, - ) - .unwrap(); - - unsafe { *serialized_len_ptr = rc_box_size as u64 }; - // hack to avoid dropping the RcBox, which is supposed to be on the - // heap but we put it into account data. If we don't do this, - // dropping the Rc will cause - // global_deallocator.dealloc(rc_box_addr) which is invalid and - // happens to write a poison value into the account. - unsafe { - overwrite_account_data(account, Rc::new(RefCell::new(&mut []))); - } - } - TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER => { - msg!("TEST_ALLOW_WRITE_AFTER_OWNERSHIP_CHANGE_TO_CALLER"); - const INVOKE_PROGRAM_INDEX: usize = 3; - let account = &accounts[ARGUMENT_INDEX]; - let invoked_program_id = accounts[INVOKED_PROGRAM_INDEX].key; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - invoke( - &create_instruction( - *invoked_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (invoked_program_id, false, false), - (invoke_program_id, false, false), - ], - vec![ASSIGN_ACCOUNT_TO_CALLER], - ), - accounts, - ) - .unwrap(); - // this should succeed since the callee gave us ownership of the - // account - unsafe { - *account - .data - .borrow_mut() - .get_unchecked_mut(instruction_data[1] as usize) = 42 - }; - } - TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS"); - const CALLEE_PROGRAM_INDEX: usize = 3; - let account = &accounts[ARGUMENT_INDEX]; - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - - let expected = { - let data = &instruction_data[1..]; - let prev_len = account.data_len(); - account.realloc(prev_len + data.len(), false)?; - account.data.borrow_mut()[prev_len..].copy_from_slice(data); - account.data.borrow().to_vec() - }; - - let mut instruction_data = vec![TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED]; - instruction_data.extend_from_slice(&expected); - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - instruction_data, - ), - accounts, - ) - .unwrap(); - } - TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_NESTED"); - const ARGUMENT_INDEX: usize = 0; - let account = &accounts[ARGUMENT_INDEX]; - assert_eq!(*account.data.borrow(), &instruction_data[1..]); - } - TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLEE_GROWS"); - const REALLOC_PROGRAM_INDEX: usize = 2; - const INVOKE_PROGRAM_INDEX: usize = 3; - let account = &accounts[ARGUMENT_INDEX]; - let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; - let realloc_program_owner = accounts[REALLOC_PROGRAM_INDEX].owner; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - let mut instruction_data = instruction_data.to_vec(); - let mut expected = account.data.borrow().to_vec(); - expected.extend_from_slice(&instruction_data[1..]); - instruction_data[0] = REALLOC_EXTEND_FROM_SLICE; - invoke( - &create_instruction( - *realloc_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (realloc_program_id, false, false), - (invoke_program_id, false, false), - ], - instruction_data.to_vec(), - ), - accounts, - ) - .unwrap(); - - if !bpf_loader_deprecated::check_id(realloc_program_owner) { - assert_eq!(&*account.data.borrow(), &expected); - } - } - TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLEE_SHRINKS_SMALLER_THAN_ORIGINAL_LEN"); - const REALLOC_PROGRAM_INDEX: usize = 2; - const INVOKE_PROGRAM_INDEX: usize = 3; - let account = &accounts[ARGUMENT_INDEX]; - let realloc_program_id = accounts[REALLOC_PROGRAM_INDEX].key; - let realloc_program_owner = accounts[REALLOC_PROGRAM_INDEX].owner; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); - let prev_len = account.data_len(); - let expected = account.data.borrow()[..new_len].to_vec(); - let mut instruction_data = vec![REALLOC, 0]; - instruction_data.extend_from_slice(&new_len.to_le_bytes()); - invoke( - &create_instruction( - *realloc_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (realloc_program_id, false, false), - (invoke_program_id, false, false), - ], - instruction_data, - ), - accounts, - ) - .unwrap(); - - // deserialize_parameters_unaligned predates realloc support, and - // hardcodes the account data length to the original length. - if !bpf_loader_deprecated::check_id(realloc_program_owner) { - assert_eq!(&*account.data.borrow(), &expected); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - prev_len - new_len, - ) - }, - &vec![0; prev_len - new_len] - ); - } - } - TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS"); - const INVOKE_PROGRAM_INDEX: usize = 3; - const SENTINEL: u8 = 42; - let account = &accounts[ARGUMENT_INDEX]; - let invoke_program_id = accounts[INVOKE_PROGRAM_INDEX].key; - - let prev_data = { - let data = &instruction_data[9..]; - let prev_len = account.data_len(); - account.realloc(prev_len + data.len(), false)?; - account.data.borrow_mut()[prev_len..].copy_from_slice(data); - unsafe { - // write a sentinel value just outside the account data to - // check that when CPI zeroes the realloc region it doesn't - // zero too much - *account - .data - .borrow_mut() - .as_mut_ptr() - .add(prev_len + data.len()) = SENTINEL; - }; - account.data.borrow().to_vec() - }; - - let mut expected = account.data.borrow().to_vec(); - let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); - expected.extend_from_slice(&instruction_data[9..]); - let mut instruction_data = - vec![TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED]; - instruction_data.extend_from_slice(&new_len.to_le_bytes()); - invoke( - &create_instruction( - *invoke_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (invoke_program_id, false, false), - ], - instruction_data, - ), - accounts, - ) - .unwrap(); - - assert_eq!(*account.data.borrow(), &prev_data[..new_len]); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - prev_data.len() - new_len, - ) - }, - &vec![0; prev_data.len() - new_len] - ); - assert_eq!( - unsafe { *account.data.borrow().as_ptr().add(prev_data.len()) }, - SENTINEL - ); - } - TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED => { - msg!("TEST_CPI_ACCOUNT_UPDATE_CALLER_GROWS_CALLEE_SHRINKS_NESTED"); - const ARGUMENT_INDEX: usize = 0; - let account = &accounts[ARGUMENT_INDEX]; - let new_len = usize::from_le_bytes(instruction_data[1..9].try_into().unwrap()); - account.realloc(new_len, false).unwrap(); - } - TEST_CPI_INVALID_KEY_POINTER => { - msg!("TEST_CPI_INVALID_KEY_POINTER"); - const CALLEE_PROGRAM_INDEX: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let key = *account.key; - let key = &key as *const _ as usize; - #[rustversion::attr(since(1.72), allow(invalid_reference_casting))] - fn overwrite_account_key(account: &AccountInfo, key: *const Pubkey) { - unsafe { - let ptr = mem::transmute::<_, *mut *const Pubkey>(&account.key); - std::ptr::write_volatile(ptr, key); - } - } - overwrite_account_key(&accounts[ARGUMENT_INDEX], key as *const Pubkey); - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![], - ), - accounts, - ) - .unwrap(); - } - TEST_CPI_INVALID_LAMPORTS_POINTER => { - msg!("TEST_CPI_INVALID_LAMPORTS_POINTER"); - const CALLEE_PROGRAM_INDEX: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let mut lamports = account.lamports(); - account - .lamports - .replace(unsafe { mem::transmute::<&'_ mut u64, &'a mut u64>(&mut lamports) }); - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![], - ), - accounts, - ) - .unwrap(); - } - TEST_CPI_INVALID_OWNER_POINTER => { - msg!("TEST_CPI_INVALID_OWNER_POINTER"); - const CALLEE_PROGRAM_INDEX: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let owner = account.owner as *const _ as usize + 1; - #[rustversion::attr(since(1.72), allow(invalid_reference_casting))] - fn overwrite_account_owner(account: &AccountInfo, owner: *const Pubkey) { - unsafe { - let ptr = mem::transmute::<_, *mut *const Pubkey>(&account.owner); - std::ptr::write_volatile(ptr, owner); - } - } - overwrite_account_owner(account, owner as *const Pubkey); - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![], - ), - accounts, - ) - .unwrap(); - } - TEST_CPI_INVALID_DATA_POINTER => { - msg!("TEST_CPI_INVALID_DATA_POINTER"); - const CALLEE_PROGRAM_INDEX: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let data = unsafe { - slice::from_raw_parts_mut(account.data.borrow_mut()[1..].as_mut_ptr(), 0) - }; - account.data.replace(data); - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![], - ), - accounts, - ) - .unwrap(); - } - TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION => { - msg!("TEST_CPI_CHANGE_ACCOUNT_DATA_MEMORY_ALLOCATION"); - const CALLEE_PROGRAM_INDEX: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let callee_program_id = accounts[CALLEE_PROGRAM_INDEX].key; - let original_data_len = account.data_len(); - - // Initial data is all [0xFF; 20] - assert_eq!(&*account.data.borrow(), &[0xFF; 20]); - - // Realloc to [0xFE; 10] - invoke( - &create_instruction( - *callee_program_id, - &[ - (account.key, true, false), - (callee_program_id, false, false), - ], - vec![0xFE; 10], - ), - accounts, - ) - .unwrap(); - - // Check that [10..20] is zeroed - let new_len = account.data_len(); - assert_eq!(&*account.data.borrow(), &[0xFE; 10]); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - original_data_len - new_len, - ) - }, - &vec![0; original_data_len - new_len] - ); - - // Realloc to [0xFD; 5] - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![0xFD; 5], - ), - accounts, - ) - .unwrap(); - - // Check that [5..20] is zeroed - let new_len = account.data_len(); - assert_eq!(&*account.data.borrow(), &[0xFD; 5]); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - original_data_len - new_len, - ) - }, - &vec![0; original_data_len - new_len] - ); - - // Realloc to [0xFC; 2] - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![0xFC; 2], - ), - accounts, - ) - .unwrap(); - - // Check that [2..20] is zeroed - let new_len = account.data_len(); - assert_eq!(&*account.data.borrow(), &[0xFC; 2]); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - original_data_len - new_len, - ) - }, - &vec![0; original_data_len - new_len] - ); - - // Realloc to [0xFC; 2]. Here we keep the same length, but realloc the underlying - // vector. CPI must zero even if the length is unchanged. - invoke( - &create_instruction( - *callee_program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (callee_program_id, false, false), - ], - vec![0xFC; 2], - ), - accounts, - ) - .unwrap(); - - // Check that [2..20] is zeroed - let new_len = account.data_len(); - assert_eq!(&*account.data.borrow(), &[0xFC; 2]); - assert_eq!( - unsafe { - slice::from_raw_parts( - account.data.borrow().as_ptr().add(new_len), - original_data_len - new_len, - ) - }, - &vec![0; original_data_len - new_len] - ); - } - TEST_WRITE_ACCOUNT => { - msg!("TEST_WRITE_ACCOUNT"); - let target_account_index = instruction_data[1] as usize; - let target_account = &accounts[target_account_index]; - let byte_index = usize::from_le_bytes(instruction_data[2..10].try_into().unwrap()); - target_account.data.borrow_mut()[byte_index] = instruction_data[10]; - } - _ => panic!("unexpected program data"), - } - - Ok(()) -} - -#[repr(C)] -struct RcBox { - strong: usize, - weak: usize, - value: T, -} - -#[rustversion::attr(since(1.72), allow(invalid_reference_casting))] -unsafe fn overwrite_account_data(account: &AccountInfo, data: Rc>) { - std::ptr::write_volatile( - &account.data as *const _ as usize as *mut Rc>, - data, - ); -} diff --git a/programs/sbf/rust/invoke_dep/Cargo.toml b/programs/sbf/rust/invoke_dep/Cargo.toml new file mode 100644 index 00000000000000..4b6a403c3fed9d --- /dev/null +++ b/programs/sbf/rust/invoke_dep/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "solana-sbf-rust-invoke-dep" +version = { workspace = true } +description = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[lib] +crate-type = ["lib"] diff --git a/programs/sbf/rust/invoke/src/instructions.rs b/programs/sbf/rust/invoke_dep/src/lib.rs similarity index 100% rename from programs/sbf/rust/invoke/src/instructions.rs rename to programs/sbf/rust/invoke_dep/src/lib.rs diff --git a/programs/sbf/rust/invoked/Cargo.toml b/programs/sbf/rust/invoked/Cargo.toml index 9abbe5275fa856..3d7ae3eb55c56e 100644 --- a/programs/sbf/rust/invoked/Cargo.toml +++ b/programs/sbf/rust/invoked/Cargo.toml @@ -8,12 +8,9 @@ homepage = { workspace = true } license = { workspace = true } edition = { workspace = true } -[features] -default = ["program"] -program = [] - [dependencies] solana-program = { workspace = true } +solana-sbf-rust-invoked-dep = { workspace = true } [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/invoked/src/lib.rs b/programs/sbf/rust/invoked/src/lib.rs index eb0dde96b85804..2260aa29021ac2 100644 --- a/programs/sbf/rust/invoked/src/lib.rs +++ b/programs/sbf/rust/invoked/src/lib.rs @@ -1,4 +1,314 @@ //! Example Rust-based SBF program that issues a cross-program-invocation -pub mod instructions; -pub mod processor; +#![allow(clippy::arithmetic_side_effects)] + +use { + solana_program::{ + account_info::AccountInfo, + bpf_loader_upgradeable, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, + log::sol_log_64, + msg, + program::{get_return_data, invoke, invoke_signed, set_return_data}, + program_error::ProgramError, + pubkey::Pubkey, + system_instruction, + }, + solana_sbf_rust_invoked_dep::*, +}; + +solana_program::entrypoint!(process_instruction); +#[allow(clippy::cognitive_complexity)] +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + msg!("Invoked program"); + + if instruction_data.is_empty() { + return Ok(()); + } + + assert_eq!(get_return_data(), None); + + match instruction_data[0] { + VERIFY_TRANSLATIONS => { + msg!("verify data translations"); + + const ARGUMENT_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + const INVOKED_PROGRAM_INDEX: usize = 2; + const INVOKED_PROGRAM_DUP_INDEX: usize = 3; + + assert_eq!(&instruction_data[1..], &[1, 2, 3, 4, 5]); + assert_eq!(accounts.len(), 4); + + assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); + assert_eq!(accounts[ARGUMENT_INDEX].data_len(), 100); + assert!(accounts[ARGUMENT_INDEX].is_signer); + assert!(accounts[ARGUMENT_INDEX].is_writable); + assert_eq!(accounts[ARGUMENT_INDEX].rent_epoch, u64::MAX); + assert!(!accounts[ARGUMENT_INDEX].executable); + { + let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; + for i in 0..100 { + assert_eq!(data[i as usize], i); + } + } + + assert_eq!( + accounts[INVOKED_ARGUMENT_INDEX].owner, + accounts[INVOKED_PROGRAM_INDEX].key + ); + assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].lamports(), 10); + assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].data_len(), 10); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); + assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].rent_epoch, u64::MAX); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].executable); + + assert_eq!(accounts[INVOKED_PROGRAM_INDEX].key, program_id); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].owner, + &bpf_loader_upgradeable::id() + ); + assert!(!accounts[INVOKED_PROGRAM_INDEX].is_signer); + assert!(!accounts[INVOKED_PROGRAM_INDEX].is_writable); + assert_eq!(accounts[INVOKED_PROGRAM_INDEX].rent_epoch, u64::MAX); + assert!(accounts[INVOKED_PROGRAM_INDEX].executable); + + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].key, + accounts[INVOKED_PROGRAM_DUP_INDEX].key + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].owner, + accounts[INVOKED_PROGRAM_DUP_INDEX].owner + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].lamports, + accounts[INVOKED_PROGRAM_DUP_INDEX].lamports + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].is_signer, + accounts[INVOKED_PROGRAM_DUP_INDEX].is_signer + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].is_writable, + accounts[INVOKED_PROGRAM_DUP_INDEX].is_writable + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].rent_epoch, + accounts[INVOKED_PROGRAM_DUP_INDEX].rent_epoch + ); + assert_eq!( + accounts[INVOKED_PROGRAM_INDEX].executable, + accounts[INVOKED_PROGRAM_DUP_INDEX].executable + ); + { + let data = accounts[INVOKED_PROGRAM_INDEX].try_borrow_data()?; + assert!(accounts[INVOKED_PROGRAM_DUP_INDEX] + .try_borrow_mut_data() + .is_err()); + sol_log_64(data[0] as u64, 0, 0, 0, 0); + } + } + RETURN_OK => { + msg!("Ok"); + return Ok(()); + } + RETURN_ERROR => { + msg!("return error"); + return Err(ProgramError::Custom(42)); + } + DERIVED_SIGNERS => { + msg!("verify derived signers"); + const INVOKED_PROGRAM_INDEX: usize = 0; + const DERIVED_KEY1_INDEX: usize = 1; + const DERIVED_KEY2_INDEX: usize = 2; + const DERIVED_KEY3_INDEX: usize = 3; + + assert!(accounts[DERIVED_KEY1_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); + assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); + + let bump_seed2 = instruction_data[1]; + let bump_seed3 = instruction_data[2]; + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[DERIVED_KEY1_INDEX].key, true, false), + (accounts[DERIVED_KEY2_INDEX].key, true, true), + (accounts[DERIVED_KEY3_INDEX].key, false, true), + ], + vec![VERIFY_NESTED_SIGNERS], + ); + invoke_signed( + &invoked_instruction, + accounts, + &[ + &[b"Lil'", b"Bits", &[bump_seed2]], + &[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[bump_seed3]], + ], + )?; + } + VERIFY_NESTED_SIGNERS => { + msg!("verify nested derived signers"); + const DERIVED_KEY1_INDEX: usize = 0; + const DERIVED_KEY2_INDEX: usize = 1; + const DERIVED_KEY3_INDEX: usize = 2; + + assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); + assert!(accounts[DERIVED_KEY2_INDEX].is_signer); + assert!(accounts[DERIVED_KEY3_INDEX].is_signer); + } + VERIFY_WRITER => { + msg!("verify writable"); + const ARGUMENT_INDEX: usize = 0; + + assert!(!accounts[ARGUMENT_INDEX].is_writable); + } + VERIFY_PRIVILEGE_ESCALATION => { + msg!("Verify privilege escalation"); + } + VERIFY_PRIVILEGE_DEESCALATION => { + msg!("verify privilege deescalation"); + const INVOKED_ARGUMENT_INDEX: usize = 0; + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + } + VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { + msg!("verify privilege deescalation escalation signer"); + const INVOKED_PROGRAM_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, true, false)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + } + VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { + msg!("verify privilege deescalation escalation writable"); + const INVOKED_PROGRAM_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)], + vec![VERIFY_PRIVILEGE_ESCALATION], + ); + invoke(&invoked_instruction, accounts)?; + } + NESTED_INVOKE => { + msg!("nested invoke"); + const ARGUMENT_INDEX: usize = 0; + const INVOKED_ARGUMENT_INDEX: usize = 1; + const INVOKED_PROGRAM_INDEX: usize = 2; + + assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); + assert!(instruction_data.len() > 1); + + **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() -= 1; + **accounts[ARGUMENT_INDEX].lamports.borrow_mut() += 1; + let remaining_invokes = instruction_data[1]; + if remaining_invokes > 1 { + msg!("Invoke again"); + let invoked_instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[ + (accounts[ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), + (accounts[INVOKED_PROGRAM_INDEX].key, false, false), + ], + vec![NESTED_INVOKE, remaining_invokes - 1], + ); + invoke(&invoked_instruction, accounts)?; + } else { + msg!("Last invoked"); + { + let mut data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_mut_data()?; + for i in 0..10 { + data[i as usize] = i; + } + } + } + } + WRITE_ACCOUNT => { + msg!("write account"); + const ARGUMENT_INDEX: usize = 0; + + for i in 0..instruction_data[1] { + accounts[ARGUMENT_INDEX].data.borrow_mut()[i as usize] = instruction_data[1]; + } + } + CREATE_AND_INIT => { + msg!("Create and init data"); + { + const FROM_INDEX: usize = 0; + const DERIVED_KEY2_INDEX: usize = 1; + + let from_lamports = accounts[FROM_INDEX].lamports(); + let to_lamports = accounts[DERIVED_KEY2_INDEX].lamports(); + assert_eq!(accounts[DERIVED_KEY2_INDEX].data_len(), 0); + assert!(solana_program::system_program::check_id( + accounts[DERIVED_KEY2_INDEX].owner + )); + + let bump_seed2 = instruction_data[1]; + let instruction = system_instruction::create_account( + accounts[FROM_INDEX].key, + accounts[DERIVED_KEY2_INDEX].key, + 1, + MAX_PERMITTED_DATA_INCREASE as u64, + program_id, + ); + invoke_signed( + &instruction, + accounts, + &[&[b"Lil'", b"Bits", &[bump_seed2]]], + )?; + + assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1); + assert_eq!(accounts[DERIVED_KEY2_INDEX].lamports(), to_lamports + 1); + assert_eq!(program_id, accounts[DERIVED_KEY2_INDEX].owner); + assert_eq!( + accounts[DERIVED_KEY2_INDEX].data_len(), + MAX_PERMITTED_DATA_INCREASE + ); + let mut data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; + assert_eq!(data[0], 0); + data[0] = 0x0e; + assert_eq!(data[0], 0x0e); + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0); + data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f; + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0x0f); + for i in 1..20 { + data[i] = i as u8; + } + } + } + SET_RETURN_DATA => { + msg!("Set return data"); + + set_return_data(b"Set by invoked"); + } + ASSIGN_ACCOUNT_TO_CALLER => { + msg!("Assigning account to caller"); + const ARGUMENT_INDEX: usize = 0; + const CALLER_PROGRAM_ID: usize = 2; + let account = &accounts[ARGUMENT_INDEX]; + let caller_program_id = accounts[CALLER_PROGRAM_ID].key; + account.assign(caller_program_id); + } + _ => panic!(), + } + + Ok(()) +} diff --git a/programs/sbf/rust/invoked/src/processor.rs b/programs/sbf/rust/invoked/src/processor.rs deleted file mode 100644 index 8f60a90c15cf4e..00000000000000 --- a/programs/sbf/rust/invoked/src/processor.rs +++ /dev/null @@ -1,315 +0,0 @@ -//! Example Rust-based SBF program that issues a cross-program-invocation - -#![cfg(feature = "program")] -#![allow(clippy::arithmetic_side_effects)] - -use { - crate::instructions::*, - solana_program::{ - account_info::AccountInfo, - bpf_loader_upgradeable, - entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, - log::sol_log_64, - msg, - program::{get_return_data, invoke, invoke_signed, set_return_data}, - program_error::ProgramError, - pubkey::Pubkey, - system_instruction, - }, -}; - -solana_program::entrypoint!(process_instruction); -#[allow(clippy::cognitive_complexity)] -fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - msg!("Invoked program"); - - if instruction_data.is_empty() { - return Ok(()); - } - - assert_eq!(get_return_data(), None); - - match instruction_data[0] { - VERIFY_TRANSLATIONS => { - msg!("verify data translations"); - - const ARGUMENT_INDEX: usize = 0; - const INVOKED_ARGUMENT_INDEX: usize = 1; - const INVOKED_PROGRAM_INDEX: usize = 2; - const INVOKED_PROGRAM_DUP_INDEX: usize = 3; - - assert_eq!(&instruction_data[1..], &[1, 2, 3, 4, 5]); - assert_eq!(accounts.len(), 4); - - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); - assert_eq!(accounts[ARGUMENT_INDEX].data_len(), 100); - assert!(accounts[ARGUMENT_INDEX].is_signer); - assert!(accounts[ARGUMENT_INDEX].is_writable); - assert_eq!(accounts[ARGUMENT_INDEX].rent_epoch, u64::MAX); - assert!(!accounts[ARGUMENT_INDEX].executable); - { - let data = accounts[ARGUMENT_INDEX].try_borrow_data()?; - for i in 0..100 { - assert_eq!(data[i as usize], i); - } - } - - assert_eq!( - accounts[INVOKED_ARGUMENT_INDEX].owner, - accounts[INVOKED_PROGRAM_INDEX].key - ); - assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].lamports(), 10); - assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].data_len(), 10); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_writable); - assert_eq!(accounts[INVOKED_ARGUMENT_INDEX].rent_epoch, u64::MAX); - assert!(!accounts[INVOKED_ARGUMENT_INDEX].executable); - - assert_eq!(accounts[INVOKED_PROGRAM_INDEX].key, program_id); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].owner, - &bpf_loader_upgradeable::id() - ); - assert!(!accounts[INVOKED_PROGRAM_INDEX].is_signer); - assert!(!accounts[INVOKED_PROGRAM_INDEX].is_writable); - assert_eq!(accounts[INVOKED_PROGRAM_INDEX].rent_epoch, u64::MAX); - assert!(accounts[INVOKED_PROGRAM_INDEX].executable); - - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].key, - accounts[INVOKED_PROGRAM_DUP_INDEX].key - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].owner, - accounts[INVOKED_PROGRAM_DUP_INDEX].owner - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].lamports, - accounts[INVOKED_PROGRAM_DUP_INDEX].lamports - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].is_signer, - accounts[INVOKED_PROGRAM_DUP_INDEX].is_signer - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].is_writable, - accounts[INVOKED_PROGRAM_DUP_INDEX].is_writable - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].rent_epoch, - accounts[INVOKED_PROGRAM_DUP_INDEX].rent_epoch - ); - assert_eq!( - accounts[INVOKED_PROGRAM_INDEX].executable, - accounts[INVOKED_PROGRAM_DUP_INDEX].executable - ); - { - let data = accounts[INVOKED_PROGRAM_INDEX].try_borrow_data()?; - assert!(accounts[INVOKED_PROGRAM_DUP_INDEX] - .try_borrow_mut_data() - .is_err()); - sol_log_64(data[0] as u64, 0, 0, 0, 0); - } - } - RETURN_OK => { - msg!("Ok"); - return Ok(()); - } - RETURN_ERROR => { - msg!("return error"); - return Err(ProgramError::Custom(42)); - } - DERIVED_SIGNERS => { - msg!("verify derived signers"); - const INVOKED_PROGRAM_INDEX: usize = 0; - const DERIVED_KEY1_INDEX: usize = 1; - const DERIVED_KEY2_INDEX: usize = 2; - const DERIVED_KEY3_INDEX: usize = 3; - - assert!(accounts[DERIVED_KEY1_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY2_INDEX].is_signer); - assert!(!accounts[DERIVED_KEY3_INDEX].is_signer); - - let bump_seed2 = instruction_data[1]; - let bump_seed3 = instruction_data[2]; - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[DERIVED_KEY1_INDEX].key, true, false), - (accounts[DERIVED_KEY2_INDEX].key, true, true), - (accounts[DERIVED_KEY3_INDEX].key, false, true), - ], - vec![VERIFY_NESTED_SIGNERS], - ); - invoke_signed( - &invoked_instruction, - accounts, - &[ - &[b"Lil'", b"Bits", &[bump_seed2]], - &[accounts[DERIVED_KEY2_INDEX].key.as_ref(), &[bump_seed3]], - ], - )?; - } - VERIFY_NESTED_SIGNERS => { - msg!("verify nested derived signers"); - const DERIVED_KEY1_INDEX: usize = 0; - const DERIVED_KEY2_INDEX: usize = 1; - const DERIVED_KEY3_INDEX: usize = 2; - - assert!(!accounts[DERIVED_KEY1_INDEX].is_signer); - assert!(accounts[DERIVED_KEY2_INDEX].is_signer); - assert!(accounts[DERIVED_KEY3_INDEX].is_signer); - } - VERIFY_WRITER => { - msg!("verify writable"); - const ARGUMENT_INDEX: usize = 0; - - assert!(!accounts[ARGUMENT_INDEX].is_writable); - } - VERIFY_PRIVILEGE_ESCALATION => { - msg!("Verify privilege escalation"); - } - VERIFY_PRIVILEGE_DEESCALATION => { - msg!("verify privilege deescalation"); - const INVOKED_ARGUMENT_INDEX: usize = 0; - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); - } - VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER => { - msg!("verify privilege deescalation escalation signer"); - const INVOKED_PROGRAM_INDEX: usize = 0; - const INVOKED_ARGUMENT_INDEX: usize = 1; - - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, true, false)], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - } - VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE => { - msg!("verify privilege deescalation escalation writable"); - const INVOKED_PROGRAM_INDEX: usize = 0; - const INVOKED_ARGUMENT_INDEX: usize = 1; - - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(!accounts[INVOKED_ARGUMENT_INDEX].is_writable); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(accounts[INVOKED_ARGUMENT_INDEX].key, false, true)], - vec![VERIFY_PRIVILEGE_ESCALATION], - ); - invoke(&invoked_instruction, accounts)?; - } - NESTED_INVOKE => { - msg!("nested invoke"); - const ARGUMENT_INDEX: usize = 0; - const INVOKED_ARGUMENT_INDEX: usize = 1; - const INVOKED_PROGRAM_INDEX: usize = 2; - - assert!(accounts[INVOKED_ARGUMENT_INDEX].is_signer); - assert!(instruction_data.len() > 1); - - **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() -= 1; - **accounts[ARGUMENT_INDEX].lamports.borrow_mut() += 1; - let remaining_invokes = instruction_data[1]; - if remaining_invokes > 1 { - msg!("Invoke again"); - let invoked_instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[ - (accounts[ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_ARGUMENT_INDEX].key, true, true), - (accounts[INVOKED_PROGRAM_INDEX].key, false, false), - ], - vec![NESTED_INVOKE, remaining_invokes - 1], - ); - invoke(&invoked_instruction, accounts)?; - } else { - msg!("Last invoked"); - { - let mut data = accounts[INVOKED_ARGUMENT_INDEX].try_borrow_mut_data()?; - for i in 0..10 { - data[i as usize] = i; - } - } - } - } - WRITE_ACCOUNT => { - msg!("write account"); - const ARGUMENT_INDEX: usize = 0; - - for i in 0..instruction_data[1] { - accounts[ARGUMENT_INDEX].data.borrow_mut()[i as usize] = instruction_data[1]; - } - } - CREATE_AND_INIT => { - msg!("Create and init data"); - { - const FROM_INDEX: usize = 0; - const DERIVED_KEY2_INDEX: usize = 1; - - let from_lamports = accounts[FROM_INDEX].lamports(); - let to_lamports = accounts[DERIVED_KEY2_INDEX].lamports(); - assert_eq!(accounts[DERIVED_KEY2_INDEX].data_len(), 0); - assert!(solana_program::system_program::check_id( - accounts[DERIVED_KEY2_INDEX].owner - )); - - let bump_seed2 = instruction_data[1]; - let instruction = system_instruction::create_account( - accounts[FROM_INDEX].key, - accounts[DERIVED_KEY2_INDEX].key, - 1, - MAX_PERMITTED_DATA_INCREASE as u64, - program_id, - ); - invoke_signed( - &instruction, - accounts, - &[&[b"Lil'", b"Bits", &[bump_seed2]]], - )?; - - assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1); - assert_eq!(accounts[DERIVED_KEY2_INDEX].lamports(), to_lamports + 1); - assert_eq!(program_id, accounts[DERIVED_KEY2_INDEX].owner); - assert_eq!( - accounts[DERIVED_KEY2_INDEX].data_len(), - MAX_PERMITTED_DATA_INCREASE - ); - let mut data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; - assert_eq!(data[0], 0); - data[0] = 0x0e; - assert_eq!(data[0], 0x0e); - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0); - data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f; - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0x0f); - for i in 1..20 { - data[i] = i as u8; - } - } - } - SET_RETURN_DATA => { - msg!("Set return data"); - - set_return_data(b"Set by invoked"); - } - ASSIGN_ACCOUNT_TO_CALLER => { - msg!("Assigning account to caller"); - const ARGUMENT_INDEX: usize = 0; - const CALLER_PROGRAM_ID: usize = 2; - let account = &accounts[ARGUMENT_INDEX]; - let caller_program_id = accounts[CALLER_PROGRAM_ID].key; - account.assign(caller_program_id); - } - _ => panic!(), - } - - Ok(()) -} diff --git a/programs/sbf/rust/invoked_dep/Cargo.toml b/programs/sbf/rust/invoked_dep/Cargo.toml new file mode 100644 index 00000000000000..52cebbba70f172 --- /dev/null +++ b/programs/sbf/rust/invoked_dep/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "solana-sbf-rust-invoked-dep" +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 } + +[lib] +crate-type = ["lib"] diff --git a/programs/sbf/rust/invoked/src/instructions.rs b/programs/sbf/rust/invoked_dep/src/lib.rs similarity index 100% rename from programs/sbf/rust/invoked/src/instructions.rs rename to programs/sbf/rust/invoked_dep/src/lib.rs diff --git a/programs/sbf/rust/log_data/Cargo.toml b/programs/sbf/rust/log_data/Cargo.toml index c8e719694c399f..4f857fd1338584 100644 --- a/programs/sbf/rust/log_data/Cargo.toml +++ b/programs/sbf/rust/log_data/Cargo.toml @@ -11,9 +11,5 @@ edition = { workspace = true } [dependencies] solana-program = { workspace = true } -[features] -default = ["program"] -program = [] - [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/log_data/src/lib.rs b/programs/sbf/rust/log_data/src/lib.rs index 66b7849f5d5bc5..514fc345f50492 100644 --- a/programs/sbf/rust/log_data/src/lib.rs +++ b/programs/sbf/rust/log_data/src/lib.rs @@ -1,7 +1,5 @@ //! Example Rust-based SBF program that uses sol_log_data syscall -#![cfg(feature = "program")] - use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, log::sol_log_data, program::set_return_data, pubkey::Pubkey, diff --git a/programs/sbf/rust/realloc/Cargo.toml b/programs/sbf/rust/realloc/Cargo.toml index e977b08c1aa577..ddad08872e4eac 100644 --- a/programs/sbf/rust/realloc/Cargo.toml +++ b/programs/sbf/rust/realloc/Cargo.toml @@ -8,12 +8,9 @@ homepage = { workspace = true } license = { workspace = true } edition = { workspace = true } -[features] -default = ["program"] -program = [] - [dependencies] solana-program = { workspace = true } +solana-sbf-rust-realloc-dep = { workspace = true } [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/realloc/src/lib.rs b/programs/sbf/rust/realloc/src/lib.rs index 30c76b478de943..dd7bbafbcec4ff 100644 --- a/programs/sbf/rust/realloc/src/lib.rs +++ b/programs/sbf/rust/realloc/src/lib.rs @@ -1,4 +1,197 @@ //! Example Rust-based SBF realloc test program -pub mod instructions; -pub mod processor; +#![allow(clippy::arithmetic_side_effects)] + +extern crate solana_program; +use { + solana_program::{ + account_info::AccountInfo, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, + msg, + program::invoke, + pubkey::Pubkey, + system_instruction, system_program, + }, + solana_sbf_rust_realloc_dep::*, + std::{convert::TryInto, mem}, +}; + +solana_program::entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + let account = &accounts[0]; + + match instruction_data[0] { + REALLOC => { + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); + msg!("realloc to {}", new_len); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + } + REALLOC_EXTEND => { + let pre_len = account.data_len(); + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); + msg!("realloc extend by {}", new_len); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + } + REALLOC_EXTEND_AND_UNDO => { + let pre_len = account.data_len(); + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); + msg!("realloc extend by {}", new_len); + account.realloc(new_len, false)?; + msg!("undo realloc"); + account.realloc(pre_len, false)?; + assert_eq!(pre_len, account.data_len()); + } + REALLOC_EXTEND_AND_FILL => { + let pre_len = account.data_len(); + let fill = instruction_data[2]; + let (bytes, _) = instruction_data[4..].split_at(std::mem::size_of::()); + let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); + msg!("realloc extend by {}", new_len); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + account.try_borrow_mut_data()?[pre_len..].fill(fill); + } + // extend the account and do a 8-bytes write across the original account + // length and the realloc region + EXTEND_AND_WRITE_U64 => { + let pre_len = account.data_len(); + let new_len = mem::size_of::(); + assert!(pre_len < new_len); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + + let (bytes, _) = instruction_data[1..].split_at(new_len); + let value = u64::from_le_bytes(bytes.try_into().unwrap()); + msg!( + "write {} to account {:p}", + value, + account.try_borrow_data().unwrap().as_ptr() + ); + // exercise memory write + unsafe { + *account + .try_borrow_mut_data() + .unwrap() + .as_mut_ptr() + .cast::() = value + }; + // exercise memory read + assert_eq!( + unsafe { + *account + .try_borrow_mut_data() + .unwrap() + .as_ptr() + .cast::() + }, + value + ); + } + REALLOC_AND_ASSIGN => { + msg!("realloc and assign"); + account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?; + assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len()); + account.assign(&system_program::id()); + assert_eq!(*account.owner, system_program::id()); + } + REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => { + msg!("realloc and assign to self via system program"); + let pre_len = account.data_len(); + account.realloc(pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; + assert_eq!( + pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), + account.data_len() + ); + invoke( + &system_instruction::assign(account.key, program_id), + accounts, + )?; + assert_eq!(account.owner, program_id); + } + ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => { + msg!("assign to self via system program and realloc"); + let pre_len = account.data_len(); + invoke( + &system_instruction::assign(account.key, program_id), + accounts, + )?; + assert_eq!(account.owner, program_id); + account.realloc(pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; + assert_eq!( + account.data_len(), + pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE) + ); + } + DEALLOC_AND_ASSIGN_TO_CALLER => { + msg!("dealloc and assign to caller"); + account.realloc(0, false)?; + assert_eq!(account.data_len(), 0); + account.assign(accounts[1].key); + assert_eq!(account.owner, accounts[1].key); + } + CHECK => { + msg!("check"); + assert_eq!(100, account.data_len()); + let data = account.try_borrow_mut_data()?; + for x in data[0..5].iter() { + assert_eq!(0, *x); + } + for x in data[5..].iter() { + assert_eq!(2, *x); + } + } + ZERO_INIT => { + account.realloc(10, false)?; + { + let mut data = account.try_borrow_mut_data()?; + for i in 0..10 { + assert_eq!(0, data[i]); + } + data.fill(1); + for i in 0..10 { + assert_eq!(1, data[i]); + } + } + + account.realloc(5, false)?; + account.realloc(10, false)?; + { + let data = account.try_borrow_data()?; + for i in 0..10 { + assert_eq!(1, data[i]); + } + } + + account.realloc(5, false)?; + account.realloc(10, true)?; + { + let data = account.try_borrow_data()?; + for i in 0..5 { + assert_eq!(1, data[i]); + } + for i in 5..10 { + assert_eq!(0, data[i]); + } + } + } + REALLOC_EXTEND_FROM_SLICE => { + msg!("realloc extend from slice"); + let data = &instruction_data[1..]; + let prev_len = account.data_len(); + account.realloc(prev_len.saturating_add(data.len()), false)?; + account.data.borrow_mut()[prev_len..].copy_from_slice(data); + } + _ => panic!(), + } + + Ok(()) +} diff --git a/programs/sbf/rust/realloc/src/processor.rs b/programs/sbf/rust/realloc/src/processor.rs deleted file mode 100644 index 0997995edbe536..00000000000000 --- a/programs/sbf/rust/realloc/src/processor.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! Example Rust-based SBF realloc test program - -#![cfg(feature = "program")] -#![allow(clippy::arithmetic_side_effects)] - -extern crate solana_program; -use { - crate::instructions::*, - solana_program::{ - account_info::AccountInfo, - entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, - msg, - program::invoke, - pubkey::Pubkey, - system_instruction, system_program, - }, - std::{convert::TryInto, mem}, -}; - -solana_program::entrypoint!(process_instruction); -fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - let account = &accounts[0]; - - match instruction_data[0] { - REALLOC => { - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); - msg!("realloc to {}", new_len); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - } - REALLOC_EXTEND => { - let pre_len = account.data_len(); - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); - msg!("realloc extend by {}", new_len); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - } - REALLOC_EXTEND_AND_UNDO => { - let pre_len = account.data_len(); - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); - msg!("realloc extend by {}", new_len); - account.realloc(new_len, false)?; - msg!("undo realloc"); - account.realloc(pre_len, false)?; - assert_eq!(pre_len, account.data_len()); - } - REALLOC_EXTEND_AND_FILL => { - let pre_len = account.data_len(); - let fill = instruction_data[2]; - let (bytes, _) = instruction_data[4..].split_at(std::mem::size_of::()); - let new_len = pre_len.saturating_add(usize::from_le_bytes(bytes.try_into().unwrap())); - msg!("realloc extend by {}", new_len); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - account.try_borrow_mut_data()?[pre_len..].fill(fill); - } - // extend the account and do a 8-bytes write across the original account - // length and the realloc region - EXTEND_AND_WRITE_U64 => { - let pre_len = account.data_len(); - let new_len = mem::size_of::(); - assert!(pre_len < new_len); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - - let (bytes, _) = instruction_data[1..].split_at(new_len); - let value = u64::from_le_bytes(bytes.try_into().unwrap()); - msg!( - "write {} to account {:p}", - value, - account.try_borrow_data().unwrap().as_ptr() - ); - // exercise memory write - unsafe { - *account - .try_borrow_mut_data() - .unwrap() - .as_mut_ptr() - .cast::() = value - }; - // exercise memory read - assert_eq!( - unsafe { - *account - .try_borrow_mut_data() - .unwrap() - .as_ptr() - .cast::() - }, - value - ); - } - REALLOC_AND_ASSIGN => { - msg!("realloc and assign"); - account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?; - assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len()); - account.assign(&system_program::id()); - assert_eq!(*account.owner, system_program::id()); - } - REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => { - msg!("realloc and assign to self via system program"); - let pre_len = account.data_len(); - account.realloc(pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; - assert_eq!( - pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), - account.data_len() - ); - invoke( - &system_instruction::assign(account.key, program_id), - accounts, - )?; - assert_eq!(account.owner, program_id); - } - ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => { - msg!("assign to self via system program and realloc"); - let pre_len = account.data_len(); - invoke( - &system_instruction::assign(account.key, program_id), - accounts, - )?; - assert_eq!(account.owner, program_id); - account.realloc(pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; - assert_eq!( - account.data_len(), - pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE) - ); - } - DEALLOC_AND_ASSIGN_TO_CALLER => { - msg!("dealloc and assign to caller"); - account.realloc(0, false)?; - assert_eq!(account.data_len(), 0); - account.assign(accounts[1].key); - assert_eq!(account.owner, accounts[1].key); - } - CHECK => { - msg!("check"); - assert_eq!(100, account.data_len()); - let data = account.try_borrow_mut_data()?; - for x in data[0..5].iter() { - assert_eq!(0, *x); - } - for x in data[5..].iter() { - assert_eq!(2, *x); - } - } - ZERO_INIT => { - account.realloc(10, false)?; - { - let mut data = account.try_borrow_mut_data()?; - for i in 0..10 { - assert_eq!(0, data[i]); - } - data.fill(1); - for i in 0..10 { - assert_eq!(1, data[i]); - } - } - - account.realloc(5, false)?; - account.realloc(10, false)?; - { - let data = account.try_borrow_data()?; - for i in 0..10 { - assert_eq!(1, data[i]); - } - } - - account.realloc(5, false)?; - account.realloc(10, true)?; - { - let data = account.try_borrow_data()?; - for i in 0..5 { - assert_eq!(1, data[i]); - } - for i in 5..10 { - assert_eq!(0, data[i]); - } - } - } - REALLOC_EXTEND_FROM_SLICE => { - msg!("realloc extend from slice"); - let data = &instruction_data[1..]; - let prev_len = account.data_len(); - account.realloc(prev_len.saturating_add(data.len()), false)?; - account.data.borrow_mut()[prev_len..].copy_from_slice(data); - } - _ => panic!(), - } - - Ok(()) -} diff --git a/programs/sbf/rust/realloc_dep/Cargo.toml b/programs/sbf/rust/realloc_dep/Cargo.toml new file mode 100644 index 00000000000000..40b01115e09c64 --- /dev/null +++ b/programs/sbf/rust/realloc_dep/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "solana-sbf-rust-realloc-dep" +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 } + +[lib] +crate-type = ["lib"] diff --git a/programs/sbf/rust/realloc/src/instructions.rs b/programs/sbf/rust/realloc_dep/src/lib.rs similarity index 100% rename from programs/sbf/rust/realloc/src/instructions.rs rename to programs/sbf/rust/realloc_dep/src/lib.rs diff --git a/programs/sbf/rust/realloc_invoke/Cargo.toml b/programs/sbf/rust/realloc_invoke/Cargo.toml index f27a382ae37c2a..5d037fdfcccd7c 100644 --- a/programs/sbf/rust/realloc_invoke/Cargo.toml +++ b/programs/sbf/rust/realloc_invoke/Cargo.toml @@ -8,13 +8,10 @@ homepage = { workspace = true } license = { workspace = true } edition = { workspace = true } -[features] -default = ["program"] -program = [] - [dependencies] solana-program = { workspace = true } -solana-sbf-rust-realloc = { workspace = true } +solana-sbf-rust-realloc-dep = { workspace = true } +solana-sbf-rust-realloc-invoke-dep = { workspace = true } [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/realloc_invoke/src/lib.rs b/programs/sbf/rust/realloc_invoke/src/lib.rs index 30c76b478de943..530c62a826125a 100644 --- a/programs/sbf/rust/realloc_invoke/src/lib.rs +++ b/programs/sbf/rust/realloc_invoke/src/lib.rs @@ -1,4 +1,333 @@ //! Example Rust-based SBF realloc test program -pub mod instructions; -pub mod processor; +extern crate solana_program; +use { + solana_program::{ + account_info::AccountInfo, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, + instruction::{AccountMeta, Instruction}, + msg, + program::invoke, + pubkey::Pubkey, + system_instruction, system_program, + }, + solana_sbf_rust_realloc_dep::*, + solana_sbf_rust_realloc_invoke_dep::*, + std::convert::TryInto, +}; + +solana_program::entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + let account = &accounts[0]; + let invoke_program_id = accounts[1].key; + let pre_len = account.data_len(); + let mut bump = 0; + + match instruction_data[0] { + INVOKE_REALLOC_ZERO_RO => { + msg!("invoke realloc to zero of ro account"); + // Realloc RO account + let mut instruction = realloc(invoke_program_id, account.key, 0, &mut bump); + instruction.accounts[0].is_writable = false; + invoke(&instruction, accounts)?; + } + INVOKE_REALLOC_ZERO => { + msg!("invoke realloc to zero"); + invoke( + &realloc(invoke_program_id, account.key, 0, &mut bump), + accounts, + )?; + assert_eq!(0, account.data_len()); + } + INVOKE_REALLOC_MAX_PLUS_ONE => { + msg!("invoke realloc max + 1"); + invoke( + &realloc( + invoke_program_id, + account.key, + MAX_PERMITTED_DATA_INCREASE.saturating_add(1), + &mut bump, + ), + accounts, + )?; + } + INVOKE_REALLOC_EXTEND_MAX => { + msg!("invoke realloc max"); + invoke( + &realloc_extend( + invoke_program_id, + account.key, + MAX_PERMITTED_DATA_INCREASE, + &mut bump, + ), + accounts, + )?; + assert_eq!( + pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), + account.data_len() + ); + } + INVOKE_REALLOC_TO_THEN_LOCAL_REALLOC_EXTEND => { + let (bytes, remaining_data) = + instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); + msg!("invoke realloc to {} byte(s)", new_len); + let realloc_to_ix = { + let mut instruction_data = vec![INVOKE_REALLOC_TO, 1]; + instruction_data.extend_from_slice(&new_len.to_le_bytes()); + + Instruction::new_with_bytes( + *invoke_program_id, + &instruction_data, + vec![ + AccountMeta::new(*account.key, false), + AccountMeta::new_readonly(*invoke_program_id, false), + ], + ) + }; + invoke(&realloc_to_ix, accounts)?; + assert_eq!(new_len, account.data_len()); + let (bytes, _) = remaining_data.split_at(std::mem::size_of::()); + let extend_len = usize::from_le_bytes(bytes.try_into().unwrap()); + msg!("realloc extend {} byte(s)", extend_len); + account.realloc(new_len.saturating_add(extend_len), false)?; + assert_eq!(new_len.saturating_add(extend_len), account.data_len()); + } + INVOKE_REALLOC_MAX_TWICE => { + msg!("invoke realloc max twice"); + invoke( + &realloc( + invoke_program_id, + account.key, + MAX_PERMITTED_DATA_INCREASE, + &mut bump, + ), + accounts, + )?; + let new_len = pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE); + assert_eq!(new_len, account.data_len()); + account.realloc(new_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; + assert_eq!( + new_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), + account.data_len() + ); + } + INVOKE_REALLOC_AND_ASSIGN => { + msg!("invoke realloc and assign"); + invoke( + &Instruction::new_with_bytes( + *invoke_program_id, + &[REALLOC_AND_ASSIGN], + vec![AccountMeta::new(*account.key, false)], + ), + accounts, + )?; + assert_eq!( + pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), + account.data_len() + ); + assert_eq!(*account.owner, system_program::id()); + } + INVOKE_REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => { + msg!("invoke realloc and assign to self via system program"); + invoke( + &Instruction::new_with_bytes( + *accounts[1].key, + &[REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM], + vec![ + AccountMeta::new(*account.key, true), + AccountMeta::new_readonly(*accounts[2].key, false), + ], + ), + accounts, + )?; + } + INVOKE_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => { + msg!("invoke assign to self and realloc via system program"); + invoke( + &Instruction::new_with_bytes( + *accounts[1].key, + &[ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC], + vec![ + AccountMeta::new(*account.key, true), + AccountMeta::new_readonly(*accounts[2].key, false), + ], + ), + accounts, + )?; + } + INVOKE_REALLOC_INVOKE_CHECK => { + msg!("realloc invoke check size"); + account.realloc(100, false)?; + assert_eq!(100, account.data_len()); + account.try_borrow_mut_data()?[pre_len..].fill(2); + invoke( + &Instruction::new_with_bytes( + *accounts[1].key, + &[CHECK], + vec![AccountMeta::new(*account.key, false)], + ), + accounts, + )?; + } + INVOKE_REALLOC_TO => { + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); + msg!("realloc to {}", new_len); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + if pre_len < new_len { + account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); + } + } + INVOKE_REALLOC_RECURSIVE => { + msg!("realloc invoke recursive"); + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); + account.realloc(new_len, false)?; + assert_eq!(new_len, account.data_len()); + account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); + let final_len: usize = 200; + let mut new_instruction_data = vec![]; + new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); + new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); + invoke( + &Instruction::new_with_bytes( + *program_id, + &new_instruction_data, + vec![ + AccountMeta::new(*account.key, false), + AccountMeta::new_readonly(*accounts[1].key, false), + ], + ), + accounts, + )?; + assert_eq!(final_len, account.data_len()); + let data = account.try_borrow_mut_data()?; + for i in 0..new_len { + assert_eq!(data[i], instruction_data[1]); + } + for i in new_len..final_len { + assert_eq!(data[i], new_instruction_data[1]); + } + } + INVOKE_CREATE_ACCOUNT_REALLOC_CHECK => { + msg!("Create new account, realloc, and check"); + let pre_len: usize = 100; + invoke( + &system_instruction::create_account( + accounts[0].key, + accounts[1].key, + 3000000, // large enough for rent exemption + pre_len as u64, + program_id, + ), + accounts, + )?; + assert_eq!(pre_len, accounts[1].data_len()); + accounts[1].realloc(pre_len.saturating_add(1), false)?; + assert_eq!(pre_len.saturating_add(1), accounts[1].data_len()); + assert_eq!(accounts[1].owner, program_id); + let final_len: usize = 200; + let mut new_instruction_data = vec![]; + new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); + new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); + invoke( + &Instruction::new_with_bytes( + *program_id, + &new_instruction_data, + vec![ + AccountMeta::new(*accounts[1].key, false), + AccountMeta::new_readonly(*accounts[3].key, false), + ], + ), + accounts, + )?; + assert_eq!(final_len, accounts[1].data_len()); + } + INVOKE_DEALLOC_AND_ASSIGN => { + msg!("realloc zerod"); + let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); + let pre_len = usize::from_le_bytes(bytes.try_into().unwrap()); + let new_len = pre_len.saturating_mul(2); + assert_eq!(pre_len, 100); + { + let data = account.try_borrow_mut_data()?; + for i in 0..pre_len { + assert_eq!(data[i], instruction_data[1]); + } + } + + invoke( + &Instruction::new_with_bytes( + *accounts[2].key, + &[DEALLOC_AND_ASSIGN_TO_CALLER], + vec![ + AccountMeta::new(*account.key, false), + AccountMeta::new_readonly(*accounts[1].key, false), + ], + ), + accounts, + )?; + assert_eq!(account.owner, program_id); + assert_eq!(account.data_len(), 0); + account.realloc(new_len, false)?; + assert_eq!(account.data_len(), new_len); + { + let data = account.try_borrow_mut_data()?; + for i in 0..new_len { + assert_eq!(data[i], 0); + } + } + } + INVOKE_REALLOC_MAX_INVOKE_MAX => { + msg!("invoke realloc max invoke max"); + assert_eq!(0, account.data_len()); + account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?; + assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len()); + account.assign(invoke_program_id); + assert_eq!(account.owner, invoke_program_id); + invoke( + &realloc_extend( + invoke_program_id, + account.key, + MAX_PERMITTED_DATA_INCREASE, + &mut bump, + ), + accounts, + )?; + } + INVOKE_INVOKE_MAX_TWICE => { + msg!("invoke invoke max twice"); + assert_eq!(0, account.data_len()); + account.assign(accounts[2].key); + assert_eq!(account.owner, accounts[2].key); + invoke( + &realloc( + accounts[2].key, + account.key, + MAX_PERMITTED_DATA_INCREASE, + &mut bump, + ), + accounts, + )?; + invoke( + &realloc_extend( + accounts[2].key, + account.key, + MAX_PERMITTED_DATA_INCREASE, + &mut bump, + ), + accounts, + )?; + panic!("last invoke should fail"); + } + _ => panic!(), + } + + Ok(()) +} diff --git a/programs/sbf/rust/realloc_invoke/src/processor.rs b/programs/sbf/rust/realloc_invoke/src/processor.rs deleted file mode 100644 index 7d0052e52e1c73..00000000000000 --- a/programs/sbf/rust/realloc_invoke/src/processor.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! Example Rust-based SBF realloc test program - -#![cfg(feature = "program")] - -extern crate solana_program; -use { - crate::instructions::*, - solana_program::{ - account_info::AccountInfo, - entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, - instruction::{AccountMeta, Instruction}, - msg, - program::invoke, - pubkey::Pubkey, - system_instruction, system_program, - }, - solana_sbf_rust_realloc::instructions::*, - std::convert::TryInto, -}; - -solana_program::entrypoint!(process_instruction); -fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - let account = &accounts[0]; - let invoke_program_id = accounts[1].key; - let pre_len = account.data_len(); - let mut bump = 0; - - match instruction_data[0] { - INVOKE_REALLOC_ZERO_RO => { - msg!("invoke realloc to zero of ro account"); - // Realloc RO account - let mut instruction = realloc(invoke_program_id, account.key, 0, &mut bump); - instruction.accounts[0].is_writable = false; - invoke(&instruction, accounts)?; - } - INVOKE_REALLOC_ZERO => { - msg!("invoke realloc to zero"); - invoke( - &realloc(invoke_program_id, account.key, 0, &mut bump), - accounts, - )?; - assert_eq!(0, account.data_len()); - } - INVOKE_REALLOC_MAX_PLUS_ONE => { - msg!("invoke realloc max + 1"); - invoke( - &realloc( - invoke_program_id, - account.key, - MAX_PERMITTED_DATA_INCREASE.saturating_add(1), - &mut bump, - ), - accounts, - )?; - } - INVOKE_REALLOC_EXTEND_MAX => { - msg!("invoke realloc max"); - invoke( - &realloc_extend( - invoke_program_id, - account.key, - MAX_PERMITTED_DATA_INCREASE, - &mut bump, - ), - accounts, - )?; - assert_eq!( - pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), - account.data_len() - ); - } - INVOKE_REALLOC_TO_THEN_LOCAL_REALLOC_EXTEND => { - let (bytes, remaining_data) = - instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); - msg!("invoke realloc to {} byte(s)", new_len); - let realloc_to_ix = { - let mut instruction_data = vec![INVOKE_REALLOC_TO, 1]; - instruction_data.extend_from_slice(&new_len.to_le_bytes()); - - Instruction::new_with_bytes( - *invoke_program_id, - &instruction_data, - vec![ - AccountMeta::new(*account.key, false), - AccountMeta::new_readonly(*invoke_program_id, false), - ], - ) - }; - invoke(&realloc_to_ix, accounts)?; - assert_eq!(new_len, account.data_len()); - let (bytes, _) = remaining_data.split_at(std::mem::size_of::()); - let extend_len = usize::from_le_bytes(bytes.try_into().unwrap()); - msg!("realloc extend {} byte(s)", extend_len); - account.realloc(new_len.saturating_add(extend_len), false)?; - assert_eq!(new_len.saturating_add(extend_len), account.data_len()); - } - INVOKE_REALLOC_MAX_TWICE => { - msg!("invoke realloc max twice"); - invoke( - &realloc( - invoke_program_id, - account.key, - MAX_PERMITTED_DATA_INCREASE, - &mut bump, - ), - accounts, - )?; - let new_len = pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE); - assert_eq!(new_len, account.data_len()); - account.realloc(new_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), false)?; - assert_eq!( - new_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), - account.data_len() - ); - } - INVOKE_REALLOC_AND_ASSIGN => { - msg!("invoke realloc and assign"); - invoke( - &Instruction::new_with_bytes( - *invoke_program_id, - &[REALLOC_AND_ASSIGN], - vec![AccountMeta::new(*account.key, false)], - ), - accounts, - )?; - assert_eq!( - pre_len.saturating_add(MAX_PERMITTED_DATA_INCREASE), - account.data_len() - ); - assert_eq!(*account.owner, system_program::id()); - } - INVOKE_REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => { - msg!("invoke realloc and assign to self via system program"); - invoke( - &Instruction::new_with_bytes( - *accounts[1].key, - &[REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM], - vec![ - AccountMeta::new(*account.key, true), - AccountMeta::new_readonly(*accounts[2].key, false), - ], - ), - accounts, - )?; - } - INVOKE_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => { - msg!("invoke assign to self and realloc via system program"); - invoke( - &Instruction::new_with_bytes( - *accounts[1].key, - &[ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC], - vec![ - AccountMeta::new(*account.key, true), - AccountMeta::new_readonly(*accounts[2].key, false), - ], - ), - accounts, - )?; - } - INVOKE_REALLOC_INVOKE_CHECK => { - msg!("realloc invoke check size"); - account.realloc(100, false)?; - assert_eq!(100, account.data_len()); - account.try_borrow_mut_data()?[pre_len..].fill(2); - invoke( - &Instruction::new_with_bytes( - *accounts[1].key, - &[CHECK], - vec![AccountMeta::new(*account.key, false)], - ), - accounts, - )?; - } - INVOKE_REALLOC_TO => { - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); - msg!("realloc to {}", new_len); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - if pre_len < new_len { - account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); - } - } - INVOKE_REALLOC_RECURSIVE => { - msg!("realloc invoke recursive"); - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); - account.realloc(new_len, false)?; - assert_eq!(new_len, account.data_len()); - account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); - let final_len: usize = 200; - let mut new_instruction_data = vec![]; - new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); - new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); - invoke( - &Instruction::new_with_bytes( - *program_id, - &new_instruction_data, - vec![ - AccountMeta::new(*account.key, false), - AccountMeta::new_readonly(*accounts[1].key, false), - ], - ), - accounts, - )?; - assert_eq!(final_len, account.data_len()); - let data = account.try_borrow_mut_data()?; - for i in 0..new_len { - assert_eq!(data[i], instruction_data[1]); - } - for i in new_len..final_len { - assert_eq!(data[i], new_instruction_data[1]); - } - } - INVOKE_CREATE_ACCOUNT_REALLOC_CHECK => { - msg!("Create new account, realloc, and check"); - let pre_len: usize = 100; - invoke( - &system_instruction::create_account( - accounts[0].key, - accounts[1].key, - 3000000, // large enough for rent exemption - pre_len as u64, - program_id, - ), - accounts, - )?; - assert_eq!(pre_len, accounts[1].data_len()); - accounts[1].realloc(pre_len.saturating_add(1), false)?; - assert_eq!(pre_len.saturating_add(1), accounts[1].data_len()); - assert_eq!(accounts[1].owner, program_id); - let final_len: usize = 200; - let mut new_instruction_data = vec![]; - new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); - new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); - invoke( - &Instruction::new_with_bytes( - *program_id, - &new_instruction_data, - vec![ - AccountMeta::new(*accounts[1].key, false), - AccountMeta::new_readonly(*accounts[3].key, false), - ], - ), - accounts, - )?; - assert_eq!(final_len, accounts[1].data_len()); - } - INVOKE_DEALLOC_AND_ASSIGN => { - msg!("realloc zerod"); - let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); - let pre_len = usize::from_le_bytes(bytes.try_into().unwrap()); - let new_len = pre_len.saturating_mul(2); - assert_eq!(pre_len, 100); - { - let data = account.try_borrow_mut_data()?; - for i in 0..pre_len { - assert_eq!(data[i], instruction_data[1]); - } - } - - invoke( - &Instruction::new_with_bytes( - *accounts[2].key, - &[DEALLOC_AND_ASSIGN_TO_CALLER], - vec![ - AccountMeta::new(*account.key, false), - AccountMeta::new_readonly(*accounts[1].key, false), - ], - ), - accounts, - )?; - assert_eq!(account.owner, program_id); - assert_eq!(account.data_len(), 0); - account.realloc(new_len, false)?; - assert_eq!(account.data_len(), new_len); - { - let data = account.try_borrow_mut_data()?; - for i in 0..new_len { - assert_eq!(data[i], 0); - } - } - } - INVOKE_REALLOC_MAX_INVOKE_MAX => { - msg!("invoke realloc max invoke max"); - assert_eq!(0, account.data_len()); - account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?; - assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len()); - account.assign(invoke_program_id); - assert_eq!(account.owner, invoke_program_id); - invoke( - &realloc_extend( - invoke_program_id, - account.key, - MAX_PERMITTED_DATA_INCREASE, - &mut bump, - ), - accounts, - )?; - } - INVOKE_INVOKE_MAX_TWICE => { - msg!("invoke invoke max twice"); - assert_eq!(0, account.data_len()); - account.assign(accounts[2].key); - assert_eq!(account.owner, accounts[2].key); - invoke( - &realloc( - accounts[2].key, - account.key, - MAX_PERMITTED_DATA_INCREASE, - &mut bump, - ), - accounts, - )?; - invoke( - &realloc_extend( - accounts[2].key, - account.key, - MAX_PERMITTED_DATA_INCREASE, - &mut bump, - ), - accounts, - )?; - panic!("last invoke should fail"); - } - _ => panic!(), - } - - Ok(()) -} diff --git a/programs/sbf/rust/realloc_invoke_dep/Cargo.toml b/programs/sbf/rust/realloc_invoke_dep/Cargo.toml new file mode 100644 index 00000000000000..85629d589d95d2 --- /dev/null +++ b/programs/sbf/rust/realloc_invoke_dep/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "solana-sbf-rust-realloc-invoke-dep" +version = { workspace = true } +description = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[lib] +crate-type = ["lib"] diff --git a/programs/sbf/rust/realloc_invoke/src/instructions.rs b/programs/sbf/rust/realloc_invoke_dep/src/lib.rs similarity index 100% rename from programs/sbf/rust/realloc_invoke/src/instructions.rs rename to programs/sbf/rust/realloc_invoke_dep/src/lib.rs diff --git a/programs/sbf/rust/sibling_inner_instructions/Cargo.toml b/programs/sbf/rust/sibling_inner_instructions/Cargo.toml index 8cae2ac59c5913..0641e2aa3895bd 100644 --- a/programs/sbf/rust/sibling_inner_instructions/Cargo.toml +++ b/programs/sbf/rust/sibling_inner_instructions/Cargo.toml @@ -11,9 +11,5 @@ edition = { workspace = true } [dependencies] solana-program = { workspace = true } -[features] -default = ["program"] -program = [] - [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/sibling_inner_instructions/src/lib.rs b/programs/sbf/rust/sibling_inner_instructions/src/lib.rs index bac79f67eee5e7..da70004d505e2c 100644 --- a/programs/sbf/rust/sibling_inner_instructions/src/lib.rs +++ b/programs/sbf/rust/sibling_inner_instructions/src/lib.rs @@ -1,6 +1,5 @@ //! Example Rust-based SBF program that queries sibling instructions -#![cfg(feature = "program")] #![allow(clippy::arithmetic_side_effects)] use solana_program::{ diff --git a/programs/sbf/rust/sibling_instructions/Cargo.toml b/programs/sbf/rust/sibling_instructions/Cargo.toml index c8e24672464eb3..6e10078c3aa46f 100644 --- a/programs/sbf/rust/sibling_instructions/Cargo.toml +++ b/programs/sbf/rust/sibling_instructions/Cargo.toml @@ -11,9 +11,5 @@ edition = { workspace = true } [dependencies] solana-program = { workspace = true } -[features] -default = ["program"] -program = [] - [lib] -crate-type = ["lib", "cdylib"] +crate-type = ["cdylib"] diff --git a/programs/sbf/rust/sibling_instructions/src/lib.rs b/programs/sbf/rust/sibling_instructions/src/lib.rs index b4d18b8f7a6027..7fd705007bb81e 100644 --- a/programs/sbf/rust/sibling_instructions/src/lib.rs +++ b/programs/sbf/rust/sibling_instructions/src/lib.rs @@ -1,7 +1,5 @@ //! Example Rust-based SBF program that queries sibling instructions -#![cfg(feature = "program")] - use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index f4a3a6699a6600..b0f2a95c195414 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -28,9 +28,9 @@ use { load_upgradeable_program_wrapper, set_upgrade_authority, upgrade_program, }, }, - solana_sbf_rust_invoke::instructions::*, - solana_sbf_rust_realloc::instructions::*, - solana_sbf_rust_realloc_invoke::instructions::*, + solana_sbf_rust_invoke_dep::*, + solana_sbf_rust_realloc_dep::*, + solana_sbf_rust_realloc_invoke_dep::*, solana_sdk::{ account::{ReadableAccount, WritableAccount}, account_utils::StateMut,