diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 330fc1d5fb..e0889766ee 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -374,6 +374,32 @@ fn process_instruction( accounts[INVOKED_ARGUMENT_INDEX].data.borrow_mut()[..NUM_BYTES] ); } + + 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); + } + } } TEST_PRIVILEGE_ESCALATION_SIGNER => { msg!("Test privilege escalation signer"); diff --git a/programs/bpf/rust/invoked/src/instruction.rs b/programs/bpf/rust/invoked/src/instruction.rs index 770ea08619..a6c16dc8cc 100644 --- a/programs/bpf/rust/invoked/src/instruction.rs +++ b/programs/bpf/rust/invoked/src/instruction.rs @@ -17,6 +17,7 @@ pub const VERIFY_PRIVILEGE_DEESCALATION: u8 = 8; pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 9; pub const VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 10; pub const WRITE_ACCOUNT: u8 = 11; +pub const CREATE_AND_INIT: u8 = 12; pub fn create_instruction( program_id: Pubkey, diff --git a/programs/bpf/rust/invoked/src/processor.rs b/programs/bpf/rust/invoked/src/processor.rs index 1c9c8c4948..062ca26c56 100644 --- a/programs/bpf/rust/invoked/src/processor.rs +++ b/programs/bpf/rust/invoked/src/processor.rs @@ -6,11 +6,12 @@ use crate::instruction::*; use solana_program::{ account_info::AccountInfo, bpf_loader, entrypoint, - entrypoint::ProgramResult, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, msg, program::{invoke, invoke_signed}, program_error::ProgramError, pubkey::Pubkey, + system_instruction, }; entrypoint!(process_instruction); @@ -236,6 +237,52 @@ fn process_instruction( 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; + } + } + } _ => panic!(), } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 83b9ebeadf..d5d39e2a6f 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -876,6 +876,8 @@ fn test_program_bpf_invoke_sanity() { invoked_program_id.clone(), invoked_program_id.clone(), invoked_program_id.clone(), + invoked_program_id.clone(), + solana_sdk::system_program::id(), ], }; assert_eq!(invoked_programs.len(), expected_invoked_programs.len()); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index aeaa047843..f18cc9f77b 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -21,6 +21,7 @@ use solana_sdk::{ feature_set::{ cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks, enforce_aligned_host_addrs, set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall, + update_data_on_realloc, }, hash::{Hasher, HASH_BYTES}, ic_msg, @@ -1989,7 +1990,7 @@ fn call<'a>( let invoke_context = syscall.get_context()?; for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() { let account = account.borrow(); - if let Some(account_ref) = account_ref { + if let Some(mut account_ref) = account_ref { if message.is_writable(i, demote_sysvar_write_locks) && !account.executable() { *account_ref.lamports = account.lamports; *account_ref.owner = *account.owner(); @@ -2019,12 +2020,22 @@ fn call<'a>( ) .into()); } - let _ = translate( - memory_mapping, - AccessType::Store, - account_ref.vm_data_addr, - account.data().len() as u64, - )?; + if invoke_context.is_feature_active(&update_data_on_realloc::id()) { + account_ref.data = translate_slice_mut::( + memory_mapping, + account_ref.vm_data_addr, + account.data().len() as u64, + &bpf_loader_deprecated::id(), // Don't care since it is byte aligned + true, + )?; + } else { + let _ = translate( + memory_mapping, + AccessType::Store, + account_ref.vm_data_addr, + account.data().len() as u64, + )?; + } *account_ref.ref_to_len_in_vm = account.data().len() as u64; *account_ref.serialized_len_ptr = account.data().len() as u64; } diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 31d771a563..f886834cc4 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -126,10 +126,15 @@ pub mod check_duplicates_by_hash { pub mod enforce_aligned_host_addrs { solana_sdk::declare_id!("6Qob9Z4RwGdf599FDVCqsjuKjR8ZFR3oVs2ByRLWBsua"); } + pub mod set_upgrade_authority_via_cpi_enabled { solana_sdk::declare_id!("GQdjCCptpGECG7QfE35hKTAopB1umGoSrdKfax2VmZWy"); } +pub mod update_data_on_realloc { + solana_sdk::declare_id!("BkPcYCrwHXBoTsv9vMhiRF9gteZmDj3Uwisz9CDjoMKp"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -163,6 +168,7 @@ lazy_static! { (check_duplicates_by_hash::id(), "use transaction message hash for duplicate check"), (enforce_aligned_host_addrs::id(), "enforce aligned host addresses"), (set_upgrade_authority_via_cpi_enabled::id(), "set upgrade authority instruction via cpi calls for upgradable programs"), + (update_data_on_realloc::id(), "Retain updated data values modified after realloc via CPI"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()