diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 8478abcc3ca6ee..a730e12fcf19e0 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -265,11 +265,6 @@ fn create_vm<'a, 'b>( .touch(index_in_transaction as IndexOfAccount) .map_err(|_| ())?; - if account.is_shared() { - // See BorrowedAccount::make_data_mut() as to why we reserve extra - // MAX_PERMITTED_DATA_INCREASE bytes here. - account.reserve(MAX_PERMITTED_DATA_INCREASE); - } Ok(account.data_as_mut_slice().as_mut_ptr() as u64) })), )?; diff --git a/programs/bpf_loader/src/serialization.rs b/programs/bpf_loader/src/serialization.rs index 7511a68d271a6f..614918249ac29b 100644 --- a/programs/bpf_loader/src/serialization.rs +++ b/programs/bpf_loader/src/serialization.rs @@ -579,6 +579,13 @@ fn deserialize_parameters_aligned>( borrowed_account.set_data_length(post_len)?; let allocated_bytes = post_len.saturating_sub(pre_len); if allocated_bytes > 0 { + // Just in time resize of data field within the internal AccountSharedData + // + // NOTE: this is an optimization for CoW account mappings, where the account's + // data is not immediately resized with an extra MAX_PERMITTED_DATA_INCREASE, + // but if the realloc did occur (and this branch was taken) then we need to reserve + // max permitted amount of extra space so the subsequent code will work correctly + borrowed_account.reserve(MAX_PERMITTED_DATA_INCREASE)?; borrowed_account .get_data_mut()? .get_mut(pre_len..pre_len.saturating_add(allocated_bytes)) diff --git a/sdk/transaction-context/src/lib.rs b/sdk/transaction-context/src/lib.rs index 1ba52fdaf874a7..1b1793dec5735f 100644 --- a/sdk/transaction-context/src/lib.rs +++ b/sdk/transaction-context/src/lib.rs @@ -974,7 +974,12 @@ impl BorrowedAccount<'_> { // memory that holds the account but it doesn't actually change content // nor length of the account. self.make_data_mut(); - self.account.reserve(additional); + // NOTE: we don't call reserve unnecessarily, as the first call to make_data_mut + // has already resized the data to an extra MAX_PERMITTED_DATA_INCREASE bytes + if additional > MAX_PERMITTED_DATA_INCREASE { + self.account + .reserve(additional.saturating_sub(MAX_PERMITTED_DATA_INCREASE)); + } Ok(()) } @@ -1004,9 +1009,6 @@ impl BorrowedAccount<'_> { // buffer with MAX_PERMITTED_DATA_INCREASE capacity so that if the // transaction reallocs, we don't have to copy the whole account data a // second time to fullfill the realloc. - // - // NOTE: The account memory region CoW code in bpf_loader::create_vm() implements the same - // logic and must be kept in sync. if self.account.is_shared() { self.account.reserve(MAX_PERMITTED_DATA_INCREASE); }