diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 4952d3afad..2fe27b779c 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -2499,6 +2499,7 @@ dependencies = [ "net2", "solana-account-decoder", "solana-bpf-loader-program", + "solana-bpf-rust-invoke", "solana-bpf-rust-realloc", "solana-bpf-rust-realloc-invoke", "solana-cli-output", diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index 04c450bdba..acf0b0a0a3 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -26,9 +26,10 @@ itertools = "0.10.1" log = "0.4.11" miow = "0.3.6" net2 = "0.2.37" +solana-bpf-rust-invoke = { path = "rust/invoke", version = "=1.8.0"} solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.8.0"} -solana-bpf-rust-realloc = { path = "rust/realloc", version = "=1.8.0", features = ["custom-heap"]} -solana-bpf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.8.0", features = ["custom-heap"]} +solana-bpf-rust-realloc = { path = "rust/realloc", version = "=1.8.0"} +solana-bpf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.8.0"} solana-cli-output = { path = "../../cli-output", version = "=1.8.0" } solana-logger = { path = "../../logger", version = "=1.8.0" } solana-measure = { path = "../../measure", version = "=1.8.0" } diff --git a/programs/bpf/rust/invoke/Cargo.toml b/programs/bpf/rust/invoke/Cargo.toml index dd5c993906..13651cd194 100644 --- a/programs/bpf/rust/invoke/Cargo.toml +++ b/programs/bpf/rust/invoke/Cargo.toml @@ -9,12 +9,16 @@ homepage = "https://solana.com/" documentation = "https://docs.rs/solana-bpf-rust-invoke" edition = "2018" +[features] +default = ["program"] +program = [] + [dependencies] solana-bpf-rust-invoked = { path = "../invoked", default-features = false } solana-program = { path = "../../../../sdk/program", version = "=1.8.0" } [lib] -crate-type = ["cdylib"] +crate-type = ["lib", "cdylib"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/invoke/src/instructions.rs b/programs/bpf/rust/invoke/src/instructions.rs new file mode 100644 index 0000000000..cfcb11aceb --- /dev/null +++ b/programs/bpf/rust/invoke/src/instructions.rs @@ -0,0 +1,34 @@ +//! @brief Example Rust-based BPF program that issues a cross-program-invocation + +pub const TEST_SUCCESS: u8 = 1; +pub const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; +pub const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; +pub const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; +pub const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; +pub const TEST_CAP_SEEDS: u8 = 6; +pub const TEST_CAP_SIGNERS: u8 = 7; +pub const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8; +pub const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; +pub const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; +pub const TEST_RETURN_ERROR: u8 = 11; +pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; +pub const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; +pub const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14; +pub const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15; +pub const TEST_EXECUTABLE_LAMPORTS: u8 = 16; +pub const TEST_CALL_PRECOMPILE: u8 = 17; +pub const ADD_LAMPORTS: u8 = 18; + +pub const MINT_INDEX: usize = 0; +pub const ARGUMENT_INDEX: usize = 1; +pub const INVOKED_PROGRAM_INDEX: usize = 2; +pub const INVOKED_ARGUMENT_INDEX: usize = 3; +pub const INVOKED_PROGRAM_DUP_INDEX: usize = 4; +pub const ARGUMENT_DUP_INDEX: usize = 5; +pub const DERIVED_KEY1_INDEX: usize = 6; +pub const DERIVED_KEY2_INDEX: usize = 7; +pub const DERIVED_KEY3_INDEX: usize = 8; +pub const SYSTEM_PROGRAM_INDEX: usize = 9; +pub const FROM_INDEX: usize = 10; +pub const ED25519_PROGRAM_INDEX: usize = 11; +pub const INVOKE_PROGRAM_INDEX: usize = 12; diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 55286a8efc..dd82d33071 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -1,684 +1,4 @@ //! @brief Example Rust-based BPF program that issues a cross-program-invocation -#![allow(unreachable_code)] - -extern crate solana_program; - -use solana_bpf_rust_invoked::instruction::*; -use solana_program::{ - account_info::AccountInfo, - entrypoint, - 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}, - system_instruction, -}; - -const TEST_SUCCESS: u8 = 1; -const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; -const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; -const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; -const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; -const TEST_CAP_SEEDS: u8 = 6; -const TEST_CAP_SIGNERS: u8 = 7; -const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8; -const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; -const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; -const TEST_RETURN_ERROR: u8 = 11; -const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; -const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; -const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14; -const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15; -const TEST_EXECUTABLE_LAMPORTS: u8 = 16; -const TEST_CALL_PRECOMPILE: u8 = 17; -const ADD_LAMPORTS: u8 = 18; - -// const MINT_INDEX: usize = 0; // unused placeholder -const ARGUMENT_INDEX: usize = 1; -const INVOKED_PROGRAM_INDEX: usize = 2; -const INVOKED_ARGUMENT_INDEX: usize = 3; -const INVOKED_PROGRAM_DUP_INDEX: usize = 4; -// const ARGUMENT_DUP_INDEX: usize = 5; unused placeholder -const DERIVED_KEY1_INDEX: usize = 6; -const DERIVED_KEY2_INDEX: usize = 7; -const DERIVED_KEY3_INDEX: usize = 8; -const SYSTEM_PROGRAM_INDEX: usize = 9; -const FROM_INDEX: usize = 10; -const ED25519_PROGRAM_INDEX: usize = 11; -// const INVOKE_PROGRAM_INDEX: usize = 12; unused placeholder - -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(); - **accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5; - **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 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 - 5 + (2 * num_nested_invokes) - ); - assert_eq!( - accounts[INVOKED_ARGUMENT_INDEX].lamports(), - pre_invoke_argument_lamports + 5 - (2 * num_nested_invokes) - ); - Ok(()) -} - -entrypoint!(process_instruction); -fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - 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 - 42); - assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 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 - 1], 0); - data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f; - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 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 - 1); - assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 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 - 1); - assert_eq!(accounts[DERIVED_KEY2_INDEX].lamports(), to_lamports + 1); - let data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; - assert_eq!(data[0], 0x0e); - assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 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() - )) - ); - } - } - 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 mut 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, - &mut 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 mut 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, - &mut 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 mut 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, - &mut 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_INSTRUCTION_DATA_TOO_LARGE => { - msg!("Test instruction data too large"); - let instruction = - create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; 1500]); - invoke_signed(&instruction, &[], &[])?; - } - TEST_INSTRUCTION_META_TOO_LARGE => { - msg!("Test instruction metas too large"); - let instruction = create_instruction( - *accounts[INVOKED_PROGRAM_INDEX].key, - &[(&Pubkey::default(), false, false); 40], - vec![], - ); - invoke_signed(&instruction, &[], &[])?; - } - 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_mut()[..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_mut()[..NUM_BYTES] - ); - } - TEST_NESTED_INVOKE_TOO_DEEP => { - let _ = do_nested_invokes(5, accounts); - } - TEST_EXECUTABLE_LAMPORTS => { - msg!("Test executable lamports"); - let mut accounts = accounts.to_vec(); - - // set account to executable and subtract lamports - accounts[ARGUMENT_INDEX].executable = true; - **(*accounts[ARGUMENT_INDEX].lamports).borrow_mut() -= 1; - // add lamports to dest account - **(*accounts[DERIVED_KEY1_INDEX].lamports).borrow_mut() += 1; - - let instruction = create_instruction( - *program_id, - &[ - (accounts[ARGUMENT_INDEX].key, true, false), - (accounts[DERIVED_KEY1_INDEX].key, true, false), - ], - vec![ADD_LAMPORTS, 0, 0, 0], - ); - let _ = invoke(&instruction, &accounts); - - // reset executable account - **(*accounts[ARGUMENT_INDEX].lamports).borrow_mut() += 1; - } - 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 - **accounts[0].lamports.borrow_mut() += 1; - } - _ => panic!(), - } - - Ok(()) -} +pub mod instructions; +pub mod processor; diff --git a/programs/bpf/rust/invoke/src/processor.rs b/programs/bpf/rust/invoke/src/processor.rs new file mode 100644 index 0000000000..1209351326 --- /dev/null +++ b/programs/bpf/rust/invoke/src/processor.rs @@ -0,0 +1,651 @@ +//! @brief Example Rust-based BPF program that issues a cross-program-invocation + +#![cfg(feature = "program")] +#![allow(unreachable_code)] + +use crate::instructions::*; +use solana_bpf_rust_invoked::instructions::*; +use solana_program::{ + account_info::AccountInfo, + entrypoint, + 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}, + system_instruction, +}; + +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(); + **accounts[ARGUMENT_INDEX].lamports.borrow_mut() -= 5; + **accounts[INVOKED_ARGUMENT_INDEX].lamports.borrow_mut() += 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 - 5 + (2 * num_nested_invokes) + ); + assert_eq!( + accounts[INVOKED_ARGUMENT_INDEX].lamports(), + pre_invoke_argument_lamports + 5 - (2 * num_nested_invokes) + ); + Ok(()) +} + +entrypoint!(process_instruction); +fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + 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 - 42); + assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 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 - 1], 0); + data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f; + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 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 - 1); + assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 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 - 1); + assert_eq!(accounts[DERIVED_KEY2_INDEX].lamports(), to_lamports + 1); + let data = accounts[DERIVED_KEY2_INDEX].try_borrow_mut_data()?; + assert_eq!(data[0], 0x0e); + assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 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() + )) + ); + } + } + 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 mut 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, + &mut 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 mut 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, + &mut 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 mut 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, + &mut 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_INSTRUCTION_DATA_TOO_LARGE => { + msg!("Test instruction data too large"); + let instruction = + create_instruction(*accounts[INVOKED_PROGRAM_INDEX].key, &[], vec![0; 1500]); + invoke_signed(&instruction, &[], &[])?; + } + TEST_INSTRUCTION_META_TOO_LARGE => { + msg!("Test instruction metas too large"); + let instruction = create_instruction( + *accounts[INVOKED_PROGRAM_INDEX].key, + &[(&Pubkey::default(), false, false); 40], + vec![], + ); + invoke_signed(&instruction, &[], &[])?; + } + 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_mut()[..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_mut()[..NUM_BYTES] + ); + } + TEST_NESTED_INVOKE_TOO_DEEP => { + let _ = do_nested_invokes(5, accounts); + } + TEST_EXECUTABLE_LAMPORTS => { + msg!("Test executable lamports"); + let mut accounts = accounts.to_vec(); + + // set account to executable and subtract lamports + accounts[ARGUMENT_INDEX].executable = true; + **(*accounts[ARGUMENT_INDEX].lamports).borrow_mut() -= 1; + // add lamports to dest account + **(*accounts[DERIVED_KEY1_INDEX].lamports).borrow_mut() += 1; + + let instruction = create_instruction( + *program_id, + &[ + (accounts[ARGUMENT_INDEX].key, true, false), + (accounts[DERIVED_KEY1_INDEX].key, true, false), + ], + vec![ADD_LAMPORTS, 0, 0, 0], + ); + let _ = invoke(&instruction, &accounts); + + // reset executable account + **(*accounts[ARGUMENT_INDEX].lamports).borrow_mut() += 1; + } + 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 + **accounts[0].lamports.borrow_mut() += 1; + } + _ => panic!(), + } + + Ok(()) +} diff --git a/programs/bpf/rust/invoked/Cargo.toml b/programs/bpf/rust/invoked/Cargo.toml index b814d0b486..8bceb2b8b1 100644 --- a/programs/bpf/rust/invoked/Cargo.toml +++ b/programs/bpf/rust/invoked/Cargo.toml @@ -9,13 +9,13 @@ homepage = "https://solana.com/" documentation = "https://docs.rs/solana-bpf-rust-invoked" edition = "2018" -[dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.8.0" } - [features] default = ["program"] program = [] +[dependencies] +solana-program = { path = "../../../../sdk/program", version = "=1.8.0" } + [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/invoked/src/instruction.rs b/programs/bpf/rust/invoked/src/instructions.rs similarity index 100% rename from programs/bpf/rust/invoked/src/instruction.rs rename to programs/bpf/rust/invoked/src/instructions.rs diff --git a/programs/bpf/rust/invoked/src/lib.rs b/programs/bpf/rust/invoked/src/lib.rs index 80d92effa6..dd82d33071 100644 --- a/programs/bpf/rust/invoked/src/lib.rs +++ b/programs/bpf/rust/invoked/src/lib.rs @@ -1,4 +1,4 @@ //! @brief Example Rust-based BPF program that issues a cross-program-invocation -pub mod instruction; +pub mod instructions; pub mod processor; diff --git a/programs/bpf/rust/invoked/src/processor.rs b/programs/bpf/rust/invoked/src/processor.rs index 7d9aee889e..b25f2aee1d 100644 --- a/programs/bpf/rust/invoked/src/processor.rs +++ b/programs/bpf/rust/invoked/src/processor.rs @@ -2,7 +2,7 @@ #![cfg(feature = "program")] -use crate::instruction::*; +use crate::instructions::*; use solana_program::{ account_info::AccountInfo, bpf_loader, entrypoint, diff --git a/programs/bpf/rust/realloc/Cargo.toml b/programs/bpf/rust/realloc/Cargo.toml index 0e81372b3b..0e41b5a5b1 100644 --- a/programs/bpf/rust/realloc/Cargo.toml +++ b/programs/bpf/rust/realloc/Cargo.toml @@ -10,7 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-realloc" edition = "2018" [features] -custom-heap = [] +default = ["program"] +program = [] [dependencies] solana-program = { path = "../../../../sdk/program", version = "=1.8.0" } diff --git a/programs/bpf/rust/realloc/src/lib.rs b/programs/bpf/rust/realloc/src/lib.rs index 5a4c500425..19bb11f100 100644 --- a/programs/bpf/rust/realloc/src/lib.rs +++ b/programs/bpf/rust/realloc/src/lib.rs @@ -1,134 +1,4 @@ //! @brief Example Rust-based BPF realloc test program pub mod instructions; - -extern crate solana_program; -use crate::instructions::*; -use solana_program::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, - entrypoint::MAX_PERMITTED_DATA_INCREASE, msg, program::invoke, pubkey::Pubkey, - system_instruction, system_program, -}; -use std::convert::TryInto; - -entrypoint!(process_instruction); -#[allow(clippy::unnecessary_wraps)] -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 + 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_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 + 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); - } - 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 + MAX_PERMITTED_DATA_INCREASE, false)?; - assert_eq!(pre_len + 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 + MAX_PERMITTED_DATA_INCREASE, false)?; - assert_eq!(account.data_len(), pre_len + 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]); - } - } - } - _ => panic!(), - } - - Ok(()) -} +pub mod processor; diff --git a/programs/bpf/rust/realloc/src/processor.rs b/programs/bpf/rust/realloc/src/processor.rs new file mode 100644 index 0000000000..7755f5b3cc --- /dev/null +++ b/programs/bpf/rust/realloc/src/processor.rs @@ -0,0 +1,134 @@ +//! @brief Example Rust-based BPF realloc test program + +#![cfg(feature = "program")] + +extern crate solana_program; +use crate::instructions::*; +use solana_program::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, + entrypoint::MAX_PERMITTED_DATA_INCREASE, msg, program::invoke, pubkey::Pubkey, + system_instruction, system_program, +}; +use std::convert::TryInto; + +entrypoint!(process_instruction); +#[allow(clippy::unnecessary_wraps)] +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 + 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_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 + 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); + } + 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 + MAX_PERMITTED_DATA_INCREASE, false)?; + assert_eq!(pre_len + 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 + MAX_PERMITTED_DATA_INCREASE, false)?; + assert_eq!(account.data_len(), pre_len + 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]); + } + } + } + _ => panic!(), + } + + Ok(()) +} diff --git a/programs/bpf/rust/realloc_invoke/Cargo.toml b/programs/bpf/rust/realloc_invoke/Cargo.toml index 703762d072..b1ab97b7e3 100644 --- a/programs/bpf/rust/realloc_invoke/Cargo.toml +++ b/programs/bpf/rust/realloc_invoke/Cargo.toml @@ -10,11 +10,12 @@ documentation = "https://docs.rs/solana-bpf-rust-realloc-invoke" edition = "2018" [features] -custom-heap = [] +default = ["program"] +program = [] [dependencies] solana-program = { path = "../../../../sdk/program", version = "=1.8.0" } -solana-bpf-rust-realloc = { path = "../realloc", version = "=1.8.0", features = ["custom-heap"]} +solana-bpf-rust-realloc = { path = "../realloc", version = "=1.8.0", default-features = false } [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/realloc_invoke/src/lib.rs b/programs/bpf/rust/realloc_invoke/src/lib.rs index ca090bc385..19bb11f100 100644 --- a/programs/bpf/rust/realloc_invoke/src/lib.rs +++ b/programs/bpf/rust/realloc_invoke/src/lib.rs @@ -1,301 +1,4 @@ //! @brief Example Rust-based BPF realloc test program pub mod instructions; - -extern crate solana_program; -use crate::instructions::*; -use solana_bpf_rust_realloc::instructions::*; -use solana_program::{ - account_info::AccountInfo, - entrypoint, - entrypoint::ProgramResult, - entrypoint::MAX_PERMITTED_DATA_INCREASE, - instruction::{AccountMeta, Instruction}, - msg, - program::invoke, - pubkey::Pubkey, - system_instruction, system_program, -}; -use std::convert::TryInto; - -entrypoint!(process_instruction); -#[allow(clippy::unnecessary_wraps)] -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 + 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 + MAX_PERMITTED_DATA_INCREASE, 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 + MAX_PERMITTED_DATA_INCREASE; - assert_eq!(new_len, account.data_len()); - account.realloc(new_len + MAX_PERMITTED_DATA_INCREASE, false)?; - assert_eq!(new_len + 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 + 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, - 1, - pre_len as u64, - program_id, - ), - accounts, - )?; - assert_eq!(pre_len, accounts[1].data_len()); - accounts[1].realloc(pre_len + 1, false)?; - assert_eq!(pre_len + 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 * 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(()) -} +pub mod processor; diff --git a/programs/bpf/rust/realloc_invoke/src/processor.rs b/programs/bpf/rust/realloc_invoke/src/processor.rs new file mode 100644 index 0000000000..2353a7fadf --- /dev/null +++ b/programs/bpf/rust/realloc_invoke/src/processor.rs @@ -0,0 +1,301 @@ +//! @brief Example Rust-based BPF realloc test program + +#![cfg(feature = "program")] + +extern crate solana_program; +use crate::instructions::*; +use solana_bpf_rust_realloc::instructions::*; +use solana_program::{ + account_info::AccountInfo, + entrypoint, + entrypoint::ProgramResult, + entrypoint::MAX_PERMITTED_DATA_INCREASE, + instruction::{AccountMeta, Instruction}, + msg, + program::invoke, + pubkey::Pubkey, + system_instruction, system_program, +}; +use std::convert::TryInto; + +entrypoint!(process_instruction); +#[allow(clippy::unnecessary_wraps)] +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 + 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 + MAX_PERMITTED_DATA_INCREASE, 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 + MAX_PERMITTED_DATA_INCREASE; + assert_eq!(new_len, account.data_len()); + account.realloc(new_len + MAX_PERMITTED_DATA_INCREASE, false)?; + assert_eq!(new_len + 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 + 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, + 1, + pre_len as u64, + program_id, + ), + accounts, + )?; + assert_eq!(pre_len, accounts[1].data_len()); + accounts[1].realloc(pre_len + 1, false)?; + assert_eq!(pre_len + 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 * 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/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 177e506111..abaa747078 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -14,6 +14,7 @@ use solana_bpf_loader_program::{ syscalls::register_syscalls, BpfError, ThisInstructionMeter, }; +use solana_bpf_rust_invoke::instructions::*; use solana_bpf_rust_realloc::instructions::*; use solana_bpf_rust_realloc_invoke::instructions::*; use solana_cli_output::display::println_transaction; @@ -801,26 +802,6 @@ fn test_return_data_and_log_data_syscall() { fn test_program_bpf_invoke_sanity() { solana_logger::setup(); - const TEST_SUCCESS: u8 = 1; - const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2; - const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3; - const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4; - const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5; - const TEST_CAP_SEEDS: u8 = 6; - const TEST_CAP_SIGNERS: u8 = 7; - const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8; - const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9; - const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10; - const TEST_RETURN_ERROR: u8 = 11; - const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12; - const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13; - const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14; - const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15; - const TEST_EXECUTABLE_LAMPORTS: u8 = 16; - const TEST_CALL_PRECOMPILE: u8 = 17; - // const ADD_LAMPORTS: u8 = 18; - const TEST_RETURN_DATA_TOO_LARGE: u8 = 19; - #[allow(dead_code)] #[derive(Debug)] enum Languages { @@ -1103,12 +1084,6 @@ fn test_program_bpf_invoke_sanity() { &[], ); - do_invoke_failure_test_local( - TEST_RETURN_DATA_TOO_LARGE, - TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete), - &[], - ); - // Check resulting state assert_eq!(43, bank.get_balance(&derived_key1)); @@ -2614,7 +2589,6 @@ fn test_program_bpf_ro_account_modify() { let instruction = Instruction::new_with_bytes(program_id, &[0], account_metas.clone()); let message = Message::new(&[instruction], Some(&mint_pubkey)); let result = bank_client.send_and_confirm_message(&[&mint_keypair], message); - println!("result: {:?}", result); assert_eq!( result.unwrap_err().unwrap(), TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified) @@ -2623,7 +2597,6 @@ fn test_program_bpf_ro_account_modify() { let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone()); let message = Message::new(&[instruction], Some(&mint_pubkey)); let result = bank_client.send_and_confirm_message(&[&mint_keypair], message); - println!("result: {:?}", result); assert_eq!( result.unwrap_err().unwrap(), TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified) @@ -2632,7 +2605,6 @@ fn test_program_bpf_ro_account_modify() { let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone()); let message = Message::new(&[instruction], Some(&mint_pubkey)); let result = bank_client.send_and_confirm_message(&[&mint_keypair], message); - println!("result: {:?}", result); assert_eq!( result.unwrap_err().unwrap(), TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)