diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index b338999504..bc003efd29 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -136,8 +136,7 @@ pub fn deserialize_parameters_unaligned( let end = start + keyed_account.data_len()?; keyed_account .try_account_ref_mut()? - .data_as_mut_slice() - .clone_from_slice(&buffer[start..end]); + .set_data_from_slice(&buffer[start..end]); start += keyed_account.data_len()? // data + size_of::() // owner + size_of::() // executable @@ -263,12 +262,10 @@ pub fn deserialize_parameters_aligned( 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_as_mut_slice() - .clone_from_slice(&buffer[start..data_end]); + + account.set_data_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 diff --git a/sdk/src/account.rs b/sdk/src/account.rs index 138d4fbaef..17f3b1a95a 100644 --- a/sdk/src/account.rs +++ b/sdk/src/account.rs @@ -394,6 +394,20 @@ impl Account { } impl AccountSharedData { + /// make account's data equal to 'data'. This may require resizing and copying data. + pub fn set_data_from_slice(&mut self, data: &[u8]) { + let len = self.data.len(); + let len_different = len != data.len(); + if len_different { + // if the resize causes a reallocation and copy, it would be better to create a new copy of the final data + // rather than resize (+ copy current) and then copy over below. + // however, the implementation of account's data is soon to be copy on write, so the tradeoffs will soon be different. + self.data.resize(data.len(), 0); + } + // we could compare here to determine whether we need to modify the original data or not. In the current implementation, that would + // not make a positive difference. + self.data.copy_from_slice(data); + } pub fn set_data(&mut self, data: Vec) { self.data = data; } @@ -533,6 +547,27 @@ pub mod tests { (account1, account2) } + #[test] + fn test_account_set_data_from_slice() { + let key = Pubkey::new_unique(); + let (_, mut account) = make_two_accounts(&key); + assert_eq!(account.data(), &vec![0, 0]); + account.set_data_from_slice(&[1, 2]); + assert_eq!(account.data(), &vec![1, 2]); + account.set_data_from_slice(&[1, 2, 3]); + assert_eq!(account.data(), &vec![1, 2, 3]); + account.set_data_from_slice(&[4, 5, 6]); + assert_eq!(account.data(), &vec![4, 5, 6]); + account.set_data_from_slice(&[4, 5, 6, 0]); + assert_eq!(account.data(), &vec![4, 5, 6, 0]); + account.set_data_from_slice(&[]); + assert_eq!(account.data().len(), 0); + account.set_data_from_slice(&[44]); + assert_eq!(account.data(), &vec![44]); + account.set_data_from_slice(&[44]); + assert_eq!(account.data(), &vec![44]); + } + #[test] fn test_account_data_set_data() { let key = Pubkey::new_unique();