From 39eeb0142e5f3d6dbe26518d3eaa4b88fc436739 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 19:52:27 +0000 Subject: [PATCH] Add SystemInstruction::CreateAccount support to CPI (bp #11649) (#11831) * Add SystemInstruction::CreateAccount support to CPI (#11649) (cherry picked from commit e9b610b8df7053970f4d41c6cbf312fb0e9445fa) # Conflicts: # programs/bpf_loader/src/syscalls.rs # runtime/src/bank.rs # sdk/src/instruction.rs * resolve conflicts Co-authored-by: Jack May --- programs/bpf/c/src/invoke/invoke.c | 60 ++++++++++++--- programs/bpf/rust/invoke/src/lib.rs | 48 ++++++++++-- programs/bpf/tests/programs.rs | 16 +++- programs/bpf_loader/src/serialization.rs | 88 ++++++++++++---------- programs/bpf_loader/src/syscalls.rs | 95 ++++++++++++++++++------ runtime/src/message_processor.rs | 3 +- sdk/bpf/c/inc/solana_sdk.h | 7 ++ sdk/src/entrypoint.rs | 5 +- 8 files changed, 238 insertions(+), 84 deletions(-) diff --git a/programs/bpf/c/src/invoke/invoke.c b/programs/bpf/c/src/invoke/invoke.c index 1bc59379f3..4c67e36a66 100644 --- a/programs/bpf/c/src/invoke/invoke.c +++ b/programs/bpf/c/src/invoke/invoke.c @@ -36,21 +36,62 @@ extern uint64_t entrypoint(const uint8_t *input) { switch (params.data[0]) { case TEST_SUCCESS: { - sol_log("Call system program"); + sol_log("Call system program create account"); { - sol_assert(*accounts[FROM_INDEX].lamports = 43); - sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41); + uint64_t from_lamports = *accounts[FROM_INDEX].lamports; + uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports; SolAccountMeta arguments[] = { - {accounts[FROM_INDEX].key, false, true}, - {accounts[ARGUMENT_INDEX].key, false, false}}; + {accounts[FROM_INDEX].key, true, true}, + {accounts[DERIVED_KEY1_INDEX].key, true, true}}; + uint8_t data[4 + 8 + 8 + 32]; + *(uint64_t *)(data + 4) = 42; + *(uint64_t *)(data + 4 + 8) = MAX_PERMITTED_DATA_INCREASE; + sol_memcpy(data + 4 + 8 + 8, params.program_id, SIZE_PUBKEY); + const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, + arguments, SOL_ARRAY_SIZE(arguments), + data, SOL_ARRAY_SIZE(data)}; + uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's', + ' ', 'b', 'u', 't', 't', 'e', 'r'}; + const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}, + {&nonce1, 1}}; + const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}}; + sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts, + SOL_ARRAY_SIZE(accounts), + signers_seeds, + SOL_ARRAY_SIZE(signers_seeds))); + sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 42); + sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 42); + sol_assert(SolPubkey_same(accounts[DERIVED_KEY1_INDEX].owner, + params.program_id)); + sol_assert(accounts[DERIVED_KEY1_INDEX].data_len == + MAX_PERMITTED_DATA_INCREASE); + sol_assert( + accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] == + 0); + accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f; + sol_assert( + accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] == + 0x0f); + for (uint8_t i = 0; i < 20; i++) { + accounts[DERIVED_KEY1_INDEX].data[i] = i; + } + } + + sol_log("Call system program transfer"); + { + uint64_t from_lamports = *accounts[FROM_INDEX].lamports; + uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports; + SolAccountMeta arguments[] = { + {accounts[FROM_INDEX].key, true, true}, + {accounts[DERIVED_KEY1_INDEX].key, true, false}}; uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, arguments, SOL_ARRAY_SIZE(arguments), data, SOL_ARRAY_SIZE(data)}; sol_assert(SUCCESS == sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); - sol_assert(*accounts[FROM_INDEX].lamports = 42); - sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42); + sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 1); + sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 1); } sol_log("Test data translation"); @@ -92,8 +133,9 @@ extern uint64_t entrypoint(const uint8_t *input) { const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}, {&nonce1, 1}}; SolPubkey address; - sol_assert(SUCCESS == sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1), - params.program_id, &address)); + sol_assert(SUCCESS == + sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1), + params.program_id, &address)); sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key)); } diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 3338f61a03..561d6403f2 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -8,7 +8,7 @@ use solana_bpf_rust_invoked::instruction::*; use solana_sdk::{ account_info::AccountInfo, entrypoint, - entrypoint::ProgramResult, + entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, info, program::{invoke, invoke_signed}, program_error::ProgramError, @@ -46,18 +46,52 @@ fn process_instruction( match instruction_data[0] { TEST_SUCCESS => { - info!("Call system program"); + info!("Call system program create account"); { - assert_eq!(accounts[FROM_INDEX].lamports(), 43); - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41); + 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_sdk::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", &[nonce1]]])?; + + 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; + } + } + + info!("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[ARGUMENT_INDEX].key, + accounts[DERIVED_KEY1_INDEX].key, 1, ); invoke(&instruction, accounts)?; - assert_eq!(accounts[FROM_INDEX].lamports(), 42); - assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); + assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1); + assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 1); } info!("Test data translation"); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 673ad1cd48..eae89781e3 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -14,6 +14,7 @@ use solana_sdk::{ bpf_loader, client::SyncClient, clock::DEFAULT_SLOTS_PER_EPOCH, + entrypoint::MAX_PERMITTED_DATA_INCREASE, instruction::{AccountMeta, Instruction, InstructionError}, message::Message, pubkey::Pubkey, @@ -345,7 +346,7 @@ fn test_program_bpf_invoke() { let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1); let argument_keypair = Keypair::new(); - let account = Account::new(41, 100, &invoke_program_id); + let account = Account::new(42, 100, &invoke_program_id); bank.store_account(&argument_keypair.pubkey(), &account); let invoked_argument_keypair = Keypair::new(); @@ -353,7 +354,7 @@ fn test_program_bpf_invoke() { bank.store_account(&invoked_argument_keypair.pubkey(), &account); let from_keypair = Keypair::new(); - let account = Account::new(43, 0, &solana_sdk::system_program::id()); + let account = Account::new(84, 0, &solana_sdk::system_program::id()); bank.store_account(&from_keypair.pubkey(), &account); let (derived_key1, nonce1) = @@ -443,5 +444,16 @@ fn test_program_bpf_invoke() { .unwrap(), TransactionError::InstructionError(0, InstructionError::Custom(194969602)) ); + + assert_eq!(43, bank.get_balance(&derived_key1)); + let account = bank.get_account(&derived_key1).unwrap(); + assert_eq!(invoke_program_id, account.owner); + assert_eq!( + MAX_PERMITTED_DATA_INCREASE, + bank.get_account(&derived_key1).unwrap().data.len() + ); + for i in 0..20 { + assert_eq!(i as u8, account.data[i]); + } } } diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index 25ac540b5b..740ab89e4c 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -1,10 +1,11 @@ use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use solana_sdk::{ - account::KeyedAccount, bpf_loader_deprecated, instruction::InstructionError, pubkey::Pubkey, + account::KeyedAccount, bpf_loader_deprecated, entrypoint::MAX_PERMITTED_DATA_INCREASE, + instruction::InstructionError, pubkey::Pubkey, }; use std::{ io::prelude::*, - mem::{self, align_of}, + mem::{align_of, size_of}, }; /// Look for a duplicate account and return its position if found @@ -47,7 +48,7 @@ pub fn serialize_parameters_unaligned( keyed_accounts: &[KeyedAccount], instruction_data: &[u8], ) -> Result, InstructionError> { - assert_eq!(32, mem::size_of::()); + assert_eq!(32, size_of::()); let mut v: Vec = Vec::new(); v.write_u64::(keyed_accounts.len() as u64) @@ -84,29 +85,29 @@ pub fn deserialize_parameters_unaligned( keyed_accounts: &[KeyedAccount], buffer: &[u8], ) -> Result<(), InstructionError> { - assert_eq!(32, mem::size_of::()); + assert_eq!(32, size_of::()); - let mut start = mem::size_of::(); // number of accounts + let mut start = size_of::(); // number of accounts for (i, keyed_account) in keyed_accounts.iter().enumerate() { let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); start += 1; // is_dup if !is_dup { - start += mem::size_of::(); // is_signer - start += mem::size_of::(); // is_writable - start += mem::size_of::(); // pubkey + start += size_of::(); // is_signer + start += size_of::(); // is_writable + start += size_of::(); // pubkey keyed_account.try_account_ref_mut()?.lamports = LittleEndian::read_u64(&buffer[start..]); - start += mem::size_of::() // lamports - + mem::size_of::(); // data length + start += size_of::() // lamports + + size_of::(); // data length let end = start + keyed_account.data_len()?; keyed_account .try_account_ref_mut()? .data .clone_from_slice(&buffer[start..end]); start += keyed_account.data_len()? // data - + mem::size_of::() // owner - + mem::size_of::() // executable - + mem::size_of::(); // rent_epoch + + size_of::() // owner + + size_of::() // executable + + size_of::(); // rent_epoch } } Ok(()) @@ -117,14 +118,12 @@ pub fn serialize_parameters_aligned( keyed_accounts: &[KeyedAccount], instruction_data: &[u8], ) -> Result, InstructionError> { - assert_eq!(32, mem::size_of::()); + assert_eq!(32, size_of::()); - // TODO use with capacity would be nice, but don't know account data sizes... let mut v: Vec = Vec::new(); v.write_u64::(keyed_accounts.len() as u64) .unwrap(); - // TODO panic? if v.as_ptr().align_offset(align_of::()) != 0 { panic!(); } @@ -148,7 +147,9 @@ pub fn serialize_parameters_aligned( .unwrap(); v.write_all(&keyed_account.try_account_ref()?.data).unwrap(); v.resize( - v.len() + (v.len() as *const u8).align_offset(align_of::()), + v.len() + + MAX_PERMITTED_DATA_INCREASE + + (v.len() as *const u8).align_offset(align_of::()), 0, ); v.write_u64::(keyed_account.rent_epoch()? as u64) @@ -166,33 +167,39 @@ pub fn deserialize_parameters_aligned( keyed_accounts: &[KeyedAccount], buffer: &[u8], ) -> Result<(), InstructionError> { - assert_eq!(32, mem::size_of::()); + assert_eq!(32, size_of::()); - let mut start = mem::size_of::(); // number of accounts + let mut start = size_of::(); // number of accounts for (i, keyed_account) in keyed_accounts.iter().enumerate() { let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); - start += 1; // is_dup - if !is_dup { - start += mem::size_of::() // is_signer - + mem::size_of::() // is_writable - + mem::size_of::() // executable - + 4 // padding - + mem::size_of::() // pubkey - + mem::size_of::(); // owner - keyed_account.try_account_ref_mut()?.lamports = - LittleEndian::read_u64(&buffer[start..]); - start += mem::size_of::() // lamports - + mem::size_of::(); // data length - let end = start + keyed_account.data_len()?; - keyed_account - .try_account_ref_mut()? - .data - .clone_from_slice(&buffer[start..end]); - start += keyed_account.data_len()?; // data - start += (start as *const u8).align_offset(align_of::()); - start += mem::size_of::(); // rent_epoch + start += size_of::(); // position + if is_dup { + start += 7; // padding to 64-bit aligned } else { - start += 7; // padding + let mut account = keyed_account.try_account_ref_mut()?; + start += size_of::() // is_signer + + size_of::() // is_writable + + size_of::() // executable + + 4 // padding to 128-bit aligned + + size_of::(); // key + account.owner = Pubkey::new(&buffer[start..start + size_of::()]); + start += size_of::(); // owner + account.lamports = LittleEndian::read_u64(&buffer[start..]); + start += size_of::(); // lamports + let pre_len = account.data.len(); + let post_len = LittleEndian::read_u64(&buffer[start..]) as usize; + start += size_of::(); // data length + let mut data_end = start + pre_len; + if post_len != pre_len + && (post_len.saturating_sub(pre_len)) <= MAX_PERMITTED_DATA_INCREASE + { + account.data.resize(post_len, 0); + data_end = start + post_len; + } + account.data.clone_from_slice(&buffer[start..data_end]); + start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data + start += (start as *const u8).align_offset(align_of::()); + start += size_of::(); // rent_epoch } } Ok(()) @@ -206,7 +213,6 @@ mod tests { }; use std::{ cell::RefCell, - mem::size_of, rc::Rc, // Hide Result from bindgen gets confused about generics in non-generic type declarations slice::{from_raw_parts, from_raw_parts_mut}, diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index e2588cdc3e..bd14ee5338 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -11,7 +11,7 @@ use solana_sdk::{ account::KeyedAccount, account_info::AccountInfo, bpf_loader, bpf_loader_deprecated, - entrypoint::SUCCESS, + entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, entrypoint_native::{ComputeMeter, InvokeContext, Logger}, instruction::{AccountMeta, Instruction, InstructionError}, message::Message, @@ -430,7 +430,14 @@ impl SyscallObject for SyscallCreateProgramAddress { // Cross-program invocation syscalls -pub type TranslatedAccounts<'a> = (Vec>>, Vec<(&'a mut u64, &'a mut [u8])>); +struct AccountReferences<'a> { + lamports: &'a mut u64, + owner: &'a mut Pubkey, + data: &'a mut [u8], + ref_to_len_in_vm: &'a mut u64, + serialized_len_ptr: &'a mut u64, +} +type TranslatedAccounts<'a> = (Vec>>, Vec>); /// Implemented by language specific data structure translators trait SyscallProcessInstruction<'a> { @@ -518,27 +525,43 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> { for account_info in account_infos.iter() { let key = translate_type!(Pubkey, account_info.key as *const _, ro_regions)?; if account_key == key { - let lamports_ref = { + let lamports = { // Double translate lamports out of RefCell let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_regions)?; translate_type_mut!(u64, *ptr, rw_regions)? }; - let data = { + let owner = + translate_type_mut!(Pubkey, account_info.owner as *const _, ro_regions)?; + let (data, ref_to_len_in_vm, serialized_len_ptr) = { // Double translate data out of RefCell let data = *translate_type!(&[u8], account_info.data.as_ptr(), ro_regions)?; - translate_slice_mut!(u8, data.as_ptr(), data.len(), rw_regions)? + let translated = + translate!(account_info.data.as_ptr(), 8, ro_regions)? as *mut u64; + let ref_to_len_in_vm = unsafe { &mut *translated.offset(1) }; + let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) }; + let serialized_len_ptr = + translate_type_mut!(u64, ref_of_len_in_input_buffer, rw_regions)?; + ( + translate_slice_mut!(u8, data.as_ptr(), data.len(), rw_regions)?, + ref_to_len_in_vm, + serialized_len_ptr, + ) }; - let owner = - translate_type!(Pubkey, account_info.owner as *const _, ro_regions)?; accounts.push(Rc::new(RefCell::new(Account { - lamports: *lamports_ref, + lamports: *lamports, data: data.to_vec(), executable: account_info.executable, owner: *owner, rent_epoch: account_info.rent_epoch, }))); - refs.push((lamports_ref, data)); + refs.push(AccountReferences { + lamports, + owner, + data, + ref_to_len_in_vm, + serialized_len_ptr, + }); continue 'root; } } @@ -630,7 +653,7 @@ struct SolAccountMeta { struct SolAccountInfo { key_addr: u64, lamports_addr: u64, - data_len: usize, + data_len: u64, data_addr: u64, owner_addr: u64, rent_epoch: u64, @@ -720,24 +743,36 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> { for account_info in account_infos.iter() { let key = translate_type!(Pubkey, account_info.key_addr, ro_regions)?; if account_key == key { - let lamports_ref = + let lamports = translate_type_mut!(u64, account_info.lamports_addr, rw_regions)?; + let owner = translate_type_mut!(Pubkey, account_info.owner_addr, ro_regions)?; let data = translate_slice_mut!( u8, account_info.data_addr, account_info.data_len, rw_regions )?; - let owner = translate_type!(Pubkey, account_info.owner_addr, ro_regions)?; + let ref_to_len_in_vm = + unsafe { &mut *(&account_info.data_len as *const u64 as u64 as *mut u64) }; + let ref_of_len_in_input_buffer = + unsafe { (account_info.data_addr as *mut u8).offset(-8) }; + let serialized_len_ptr = + translate_type_mut!(u64, ref_of_len_in_input_buffer, rw_regions)?; accounts.push(Rc::new(RefCell::new(Account { - lamports: *lamports_ref, + lamports: *lamports, data: data.to_vec(), executable: account_info.executable, owner: *owner, rent_epoch: account_info.rent_epoch, }))); - refs.push((lamports_ref, data)); + refs.push(AccountReferences { + lamports, + owner, + data, + ref_to_len_in_vm, + serialized_len_ptr, + }); continue 'root; } } @@ -877,7 +912,7 @@ fn call<'a>( let message = Message::new(&[instruction], None); let callee_program_id_index = message.instructions[0].program_id_index as usize; let callee_program_id = message.account_keys[callee_program_id_index]; - let (accounts, refs) = syscall.translate_accounts( + let (accounts, account_refs) = syscall.translate_accounts( &message, account_infos_addr, account_infos_len as usize, @@ -912,17 +947,31 @@ fn call<'a>( } // Copy results back into caller's AccountInfos - for (i, (account, (lamport_ref, data))) in accounts.iter().zip(refs).enumerate() { + + for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() { let account = account.borrow(); if message.is_writable(i) && !account.executable { - *lamport_ref = account.lamports; - if data.len() != account.data.len() { - return Err(SyscallError::InstructionError( - InstructionError::AccountDataSizeChanged, - ) - .into()); + *account_ref.lamports = account.lamports; + *account_ref.owner = account.owner; + if account_ref.data.len() != account.data.len() { + *account_ref.ref_to_len_in_vm = account.data.len() as u64; + *account_ref.serialized_len_ptr = account.data.len() as u64; + if !account_ref.data.is_empty() { + // Only support for `CreateAccount` at this time. + // Need a way to limit total realloc size accross multiple CPI calls + return Err( + SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), + ); + } + if account.data.len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE { + return Err( + SyscallError::InstructionError(InstructionError::InvalidRealloc).into(), + ); + } } - data.clone_from_slice(&account.data); + account_ref + .data + .clone_from_slice(&account.data[0..account_ref.data.len()]); } } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 1489131b2d..fa488e3a6f 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -69,7 +69,7 @@ impl PreAccount { // An account not assigned to the program cannot have its balance decrease. if *program_id != self.owner // line coverage used to get branch coverage - && self.lamports > post.lamports + && self.lamports > post.lamports { return Err(InstructionError::ExternalAccountLamportSpend); } @@ -133,6 +133,7 @@ impl PreAccount { pub fn update(&mut self, account: &Account) { self.lamports = account.lamports; + self.owner = account.owner; if self.data.len() != account.data.len() { // Only system account can change data size, copy with alloc self.data = account.data.clone(); diff --git a/sdk/bpf/c/inc/solana_sdk.h b/sdk/bpf/c/inc/solana_sdk.h index db86969657..183bf60bb0 100644 --- a/sdk/bpf/c/inc/solana_sdk.h +++ b/sdk/bpf/c/inc/solana_sdk.h @@ -256,6 +256,11 @@ typedef struct { const SolPubkey *program_id; /** program_id of the currently executing program */ } SolParameters; +/** + * Maximum number of bytes a program may add to an account during a single realloc + */ +#define MAX_PERMITTED_DATA_INCREASE (1024 * 10) + /** * De-serializes the input parameters into usable types * @@ -297,6 +302,7 @@ static bool sol_deserialize( uint64_t data_len = *(uint64_t *) input; input += sizeof(uint64_t); input += data_len; + input += MAX_PERMITTED_DATA_INCREASE; input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding input += sizeof(uint64_t); } @@ -334,6 +340,7 @@ static bool sol_deserialize( input += sizeof(uint64_t); params->ka[i].data = (uint8_t *) input; input += params->ka[i].data_len; + input += MAX_PERMITTED_DATA_INCREASE; input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding // rent epoch diff --git a/sdk/src/entrypoint.rs b/sdk/src/entrypoint.rs index 2f488a3bd2..2917bcda77 100644 --- a/sdk/src/entrypoint.rs +++ b/sdk/src/entrypoint.rs @@ -48,6 +48,9 @@ macro_rules! entrypoint { }; } +/// Maximum number of bytes a program may add to an account during a single realloc +pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10; + /// Deserialize the input arguments /// /// # Safety @@ -99,7 +102,7 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec()); // padding #[allow(clippy::cast_ptr_alignment)]