2023-09-01 00:26:13 -07:00
|
|
|
#![deny(clippy::arithmetic_side_effects)]
|
2022-03-10 11:48:33 -08:00
|
|
|
#![deny(clippy::indexing_slicing)]
|
|
|
|
|
2020-08-11 16:11:52 -07:00
|
|
|
pub mod serialization;
|
2020-04-30 01:43:11 -07:00
|
|
|
pub mod syscalls;
|
2018-10-16 16:33:31 -07:00
|
|
|
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
solana_measure::measure::Measure,
|
|
|
|
solana_program_runtime::{
|
|
|
|
ic_logger_msg, ic_msg,
|
2023-07-28 04:34:27 -07:00
|
|
|
invoke_context::{BpfAllocator, InvokeContext, SerializedAccountMetadata, SyscallContext},
|
2023-05-09 15:44:38 -07:00
|
|
|
loaded_programs::{
|
|
|
|
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
log_collector::LogCollector,
|
|
|
|
stable_log,
|
2022-03-16 03:30:01 -07:00
|
|
|
sysvar_cache::get_sysvar_with_account_check,
|
2021-08-24 10:05:54 -07:00
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_rbpf::{
|
|
|
|
aligned_memory::AlignedMemory,
|
2023-03-10 10:05:29 -08:00
|
|
|
ebpf::{self, HOST_ALIGN, MM_HEAP_START},
|
|
|
|
elf::Executable,
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
error::EbpfError,
|
|
|
|
memory_region::{AccessType, MemoryCowCallback, MemoryMapping, MemoryRegion},
|
2023-04-05 06:50:34 -07:00
|
|
|
verifier::RequisiteVerifier,
|
2023-06-02 07:34:01 -07:00
|
|
|
vm::{BuiltinProgram, ContextObject, EbpfVm, ProgramResult},
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
solana_sdk::{
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
account::WritableAccount,
|
2021-12-03 09:00:31 -08:00
|
|
|
bpf_loader, bpf_loader_deprecated,
|
|
|
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
2023-02-15 09:05:26 -08:00
|
|
|
clock::Slot,
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
2021-12-03 09:00:31 -08:00
|
|
|
feature_set::{
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
bpf_account_data_direct_mapping, cap_accounts_data_allocations_per_transaction,
|
|
|
|
cap_bpf_program_instruction_accounts, delay_visibility_of_program_deployment,
|
2023-05-12 09:07:13 -07:00
|
|
|
enable_bpf_loader_extend_program_ix, enable_bpf_loader_set_authority_checked_ix,
|
|
|
|
enable_program_redeployment_cooldown, limit_max_instruction_trace_length,
|
|
|
|
native_programs_consume_cu, remove_bpf_loader_incorrect_program_id, FeatureSet,
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
instruction::{AccountMeta, InstructionError},
|
|
|
|
loader_instruction::LoaderInstruction,
|
|
|
|
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
|
2023-03-06 08:37:37 -08:00
|
|
|
native_loader,
|
2022-09-26 01:47:16 -07:00
|
|
|
program_error::{
|
|
|
|
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED, MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
program_utils::limited_deserialize,
|
|
|
|
pubkey::Pubkey,
|
2022-01-05 16:08:35 -08:00
|
|
|
saturating_add_assign,
|
2021-12-03 09:00:31 -08:00
|
|
|
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
|
2022-09-06 02:31:40 -07:00
|
|
|
transaction_context::{
|
|
|
|
BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
|
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
2022-10-18 01:22:39 -07:00
|
|
|
std::{
|
2023-05-02 14:39:27 -07:00
|
|
|
cell::RefCell,
|
2023-03-10 10:05:29 -08:00
|
|
|
mem,
|
2022-10-18 01:22:39 -07:00
|
|
|
rc::Rc,
|
2023-03-27 06:48:05 -07:00
|
|
|
sync::{atomic::Ordering, Arc},
|
2022-10-18 01:22:39 -07:00
|
|
|
},
|
2023-08-11 11:58:22 -07:00
|
|
|
syscalls::create_program_runtime_environment_v1,
|
2020-01-02 18:18:56 -08:00
|
|
|
};
|
2019-05-24 16:21:42 -07:00
|
|
|
|
2023-06-02 08:22:22 -07:00
|
|
|
pub const DEFAULT_LOADER_COMPUTE_UNITS: u64 = 570;
|
|
|
|
pub const DEPRECATED_LOADER_COMPUTE_UNITS: u64 = 1_140;
|
|
|
|
pub const UPGRADEABLE_LOADER_COMPUTE_UNITS: u64 = 2_370;
|
|
|
|
|
2023-02-17 03:47:45 -08:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub fn load_program_from_bytes(
|
2022-10-18 01:22:39 -07:00
|
|
|
feature_set: &FeatureSet,
|
|
|
|
log_collector: Option<Rc<RefCell<LogCollector>>>,
|
2023-02-17 03:47:45 -08:00
|
|
|
load_program_metrics: &mut LoadProgramMetrics,
|
2022-10-18 01:22:39 -07:00
|
|
|
programdata: &[u8],
|
2023-02-17 03:47:45 -08:00
|
|
|
loader_key: &Pubkey,
|
|
|
|
account_size: usize,
|
|
|
|
deployment_slot: Slot,
|
2023-06-02 07:34:01 -07:00
|
|
|
program_runtime_environment: Arc<BuiltinProgram<InvokeContext<'static>>>,
|
2023-02-17 03:47:45 -08:00
|
|
|
) -> Result<LoadedProgram, InstructionError> {
|
2023-03-29 04:47:49 -07:00
|
|
|
let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) {
|
2023-05-09 15:44:38 -07:00
|
|
|
deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET)
|
2023-03-29 04:47:49 -07:00
|
|
|
} else {
|
|
|
|
deployment_slot
|
|
|
|
};
|
2023-02-17 03:47:45 -08:00
|
|
|
let loaded_program = LoadedProgram::new(
|
|
|
|
loader_key,
|
2023-05-20 11:28:00 -07:00
|
|
|
program_runtime_environment,
|
2023-02-17 03:47:45 -08:00
|
|
|
deployment_slot,
|
2023-03-29 04:47:49 -07:00
|
|
|
effective_slot,
|
2023-04-05 09:26:28 -07:00
|
|
|
None,
|
2023-02-17 03:47:45 -08:00
|
|
|
programdata,
|
|
|
|
account_size,
|
|
|
|
load_program_metrics,
|
|
|
|
)
|
|
|
|
.map_err(|err| {
|
2022-12-11 23:47:09 -08:00
|
|
|
ic_logger_msg!(log_collector, "{}", err);
|
|
|
|
InstructionError::InvalidAccountData
|
2023-02-17 03:47:45 -08:00
|
|
|
})?;
|
|
|
|
Ok(loaded_program)
|
2020-08-21 15:31:19 -07:00
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
|
2023-02-11 02:18:25 -08:00
|
|
|
macro_rules! deploy_program {
|
2023-04-25 10:04:11 -07:00
|
|
|
($invoke_context:expr, $program_id:expr, $loader_key:expr,
|
2023-02-17 03:47:45 -08:00
|
|
|
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
|
|
|
|
let mut load_program_metrics = LoadProgramMetrics::default();
|
2023-05-20 11:28:00 -07:00
|
|
|
let mut register_syscalls_time = Measure::start("register_syscalls_time");
|
2023-08-11 11:58:22 -07:00
|
|
|
let program_runtime_environment = create_program_runtime_environment_v1(
|
2023-02-11 02:18:25 -08:00
|
|
|
&$invoke_context.feature_set,
|
|
|
|
$invoke_context.get_compute_budget(),
|
2023-05-20 11:28:00 -07:00
|
|
|
true, /* deployment */
|
|
|
|
false, /* debugging_features */
|
|
|
|
).map_err(|e| {
|
|
|
|
ic_msg!($invoke_context, "Failed to register syscalls: {}", e);
|
|
|
|
InstructionError::ProgramEnvironmentSetupFailure
|
|
|
|
})?;
|
|
|
|
register_syscalls_time.stop();
|
|
|
|
load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
|
|
|
|
let executor = load_program_from_bytes(
|
|
|
|
&$invoke_context.feature_set,
|
2023-02-11 02:18:25 -08:00
|
|
|
$invoke_context.get_log_collector(),
|
2023-02-17 03:47:45 -08:00
|
|
|
&mut load_program_metrics,
|
2023-02-11 02:18:25 -08:00
|
|
|
$new_programdata,
|
2023-02-17 03:47:45 -08:00
|
|
|
$loader_key,
|
|
|
|
$account_size,
|
|
|
|
$slot,
|
2023-05-20 11:28:00 -07:00
|
|
|
Arc::new(program_runtime_environment),
|
2023-02-11 02:18:25 -08:00
|
|
|
)?;
|
2023-08-16 10:50:23 -07:00
|
|
|
if let Some(old_entry) = $invoke_context.find_program_in_cache(&$program_id) {
|
2023-06-02 11:26:56 -07:00
|
|
|
executor.tx_usage_counter.store(
|
|
|
|
old_entry.tx_usage_counter.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed
|
|
|
|
);
|
|
|
|
executor.ix_usage_counter.store(
|
|
|
|
old_entry.ix_usage_counter.load(Ordering::Relaxed),
|
|
|
|
Ordering::Relaxed
|
|
|
|
);
|
2023-04-11 09:43:42 -07:00
|
|
|
}
|
2023-02-11 02:18:25 -08:00
|
|
|
$drop
|
2023-02-17 03:47:45 -08:00
|
|
|
load_program_metrics.program_id = $program_id.to_string();
|
|
|
|
load_program_metrics.submit_datapoint(&mut $invoke_context.timings);
|
2023-05-17 06:10:50 -07:00
|
|
|
$invoke_context.programs_modified_by_tx.replenish($program_id, Arc::new(executor));
|
2023-02-11 02:18:25 -08:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2022-03-16 03:30:01 -07:00
|
|
|
fn write_program_data(
|
2021-04-19 09:48:48 -07:00
|
|
|
program_data_offset: usize,
|
2020-12-14 15:35:10 -08:00
|
|
|
bytes: &[u8],
|
2022-03-16 03:30:01 -07:00
|
|
|
invoke_context: &mut InvokeContext,
|
2020-12-14 15:35:10 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2022-03-30 00:17:55 -07:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2023-03-08 07:54:19 -08:00
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
2022-05-21 08:47:09 -07:00
|
|
|
let data = program.get_data_mut()?;
|
2022-03-30 00:17:55 -07:00
|
|
|
let write_offset = program_data_offset.saturating_add(bytes.len());
|
|
|
|
if data.len() < write_offset {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
2021-01-21 09:57:59 -08:00
|
|
|
"Write overflow: {} < {}",
|
|
|
|
data.len(),
|
2022-03-30 00:17:55 -07:00
|
|
|
write_offset,
|
2021-01-21 09:57:59 -08:00
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2022-03-30 00:17:55 -07:00
|
|
|
data.get_mut(program_data_offset..write_offset)
|
2022-03-10 11:48:33 -08:00
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?
|
|
|
|
.copy_from_slice(bytes);
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-07-21 13:43:46 -07:00
|
|
|
pub fn check_loader_id(id: &Pubkey) -> bool {
|
2020-12-23 19:04:48 -08:00
|
|
|
bpf_loader::check_id(id)
|
|
|
|
|| bpf_loader_deprecated::check_id(id)
|
|
|
|
|| bpf_loader_upgradeable::check_id(id)
|
|
|
|
}
|
|
|
|
|
2023-04-14 06:20:08 -07:00
|
|
|
/// Only used in macro, do not use directly!
|
|
|
|
pub fn calculate_heap_cost(heap_size: u64, heap_cost: u64, enable_rounding_fix: bool) -> u64 {
|
2023-03-15 10:03:04 -07:00
|
|
|
const KIBIBYTE: u64 = 1024;
|
|
|
|
const PAGE_SIZE_KB: u64 = 32;
|
|
|
|
let mut rounded_heap_size = heap_size;
|
|
|
|
if enable_rounding_fix {
|
|
|
|
rounded_heap_size = rounded_heap_size
|
|
|
|
.saturating_add(PAGE_SIZE_KB.saturating_mul(KIBIBYTE).saturating_sub(1));
|
|
|
|
}
|
|
|
|
rounded_heap_size
|
2023-08-29 11:58:53 -07:00
|
|
|
.checked_div(PAGE_SIZE_KB.saturating_mul(KIBIBYTE))
|
|
|
|
.expect("PAGE_SIZE_KB * KIBIBYTE > 0")
|
2023-03-15 10:03:04 -07:00
|
|
|
.saturating_sub(1)
|
|
|
|
.saturating_mul(heap_cost)
|
|
|
|
}
|
|
|
|
|
2023-04-14 06:20:08 -07:00
|
|
|
/// Only used in macro, do not use directly!
|
|
|
|
pub fn create_vm<'a, 'b>(
|
2023-05-12 09:07:13 -07:00
|
|
|
program: &'a Executable<RequisiteVerifier, InvokeContext<'b>>,
|
2022-10-06 23:45:05 -07:00
|
|
|
regions: Vec<MemoryRegion>,
|
2023-07-28 04:34:27 -07:00
|
|
|
accounts_metadata: Vec<SerializedAccountMetadata>,
|
2021-12-02 09:47:16 -08:00
|
|
|
invoke_context: &'a mut InvokeContext<'b>,
|
2023-04-14 06:20:08 -07:00
|
|
|
stack: &mut AlignedMemory<HOST_ALIGN>,
|
|
|
|
heap: &mut AlignedMemory<HOST_ALIGN>,
|
2023-07-05 10:46:21 -07:00
|
|
|
) -> Result<EbpfVm<'a, InvokeContext<'b>>, Box<dyn std::error::Error>> {
|
2023-04-14 06:20:08 -07:00
|
|
|
let stack_size = stack.len();
|
|
|
|
let heap_size = heap.len();
|
2023-08-24 15:54:06 -07:00
|
|
|
let accounts = Rc::clone(invoke_context.transaction_context.accounts());
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
let memory_mapping = create_memory_mapping(
|
2023-05-12 09:07:13 -07:00
|
|
|
program,
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
stack,
|
|
|
|
heap,
|
|
|
|
regions,
|
|
|
|
Some(Box::new(move |index_in_transaction| {
|
|
|
|
// The two calls below can't really fail. If they fail because of a bug,
|
|
|
|
// whatever is writing will trigger an EbpfError::AccessViolation like
|
|
|
|
// if the region was readonly, and the transaction will fail gracefully.
|
|
|
|
let mut account = accounts
|
|
|
|
.try_borrow_mut(index_in_transaction as IndexOfAccount)
|
|
|
|
.map_err(|_| ())?;
|
|
|
|
accounts
|
|
|
|
.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)
|
|
|
|
})),
|
|
|
|
)?;
|
2023-04-14 06:20:08 -07:00
|
|
|
invoke_context.set_syscall_context(SyscallContext {
|
|
|
|
allocator: BpfAllocator::new(heap_size as u64),
|
2023-07-28 04:34:27 -07:00
|
|
|
accounts_metadata,
|
2023-04-14 06:20:08 -07:00
|
|
|
trace_log: Vec::new(),
|
|
|
|
})?;
|
2023-04-05 06:50:34 -07:00
|
|
|
Ok(EbpfVm::new(
|
2023-07-05 10:46:21 -07:00
|
|
|
program.get_config(),
|
|
|
|
program.get_sbpf_version(),
|
2023-04-05 06:50:34 -07:00
|
|
|
invoke_context,
|
|
|
|
memory_mapping,
|
2023-04-14 06:20:08 -07:00
|
|
|
stack_size,
|
2023-04-05 06:50:34 -07:00
|
|
|
))
|
2023-03-10 10:05:29 -08:00
|
|
|
}
|
|
|
|
|
2023-04-14 06:20:08 -07:00
|
|
|
/// Create the SBF virtual machine
|
2023-03-10 10:05:29 -08:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! create_vm {
|
2023-07-28 04:34:27 -07:00
|
|
|
($vm:ident, $program:expr, $regions:expr, $accounts_metadata:expr, $invoke_context:expr $(,)?) => {
|
2023-04-14 06:20:08 -07:00
|
|
|
let invoke_context = &*$invoke_context;
|
2023-05-12 09:07:13 -07:00
|
|
|
let stack_size = $program.get_config().stack_size();
|
2023-04-14 06:20:08 -07:00
|
|
|
let heap_size = invoke_context
|
2023-03-10 10:05:29 -08:00
|
|
|
.get_compute_budget()
|
|
|
|
.heap_size
|
|
|
|
.unwrap_or(solana_sdk::entrypoint::HEAP_LENGTH);
|
2023-04-14 06:20:08 -07:00
|
|
|
let round_up_heap_size = invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&solana_sdk::feature_set::round_up_heap_size::id());
|
|
|
|
let mut heap_cost_result = invoke_context.consume_checked($crate::calculate_heap_cost(
|
|
|
|
heap_size as u64,
|
|
|
|
invoke_context.get_compute_budget().heap_cost,
|
|
|
|
round_up_heap_size,
|
|
|
|
));
|
|
|
|
if !round_up_heap_size {
|
|
|
|
heap_cost_result = Ok(());
|
|
|
|
}
|
|
|
|
let mut allocations = None;
|
|
|
|
let $vm = heap_cost_result.and_then(|_| {
|
|
|
|
let mut stack = solana_rbpf::aligned_memory::AlignedMemory::<
|
|
|
|
{ solana_rbpf::ebpf::HOST_ALIGN },
|
|
|
|
>::zero_filled(stack_size);
|
|
|
|
let mut heap = solana_rbpf::aligned_memory::AlignedMemory::<
|
|
|
|
{ solana_rbpf::ebpf::HOST_ALIGN },
|
|
|
|
>::zero_filled(heap_size);
|
|
|
|
let vm = $crate::create_vm(
|
|
|
|
$program,
|
|
|
|
$regions,
|
2023-07-28 04:34:27 -07:00
|
|
|
$accounts_metadata,
|
2023-04-14 06:20:08 -07:00
|
|
|
$invoke_context,
|
|
|
|
&mut stack,
|
|
|
|
&mut heap,
|
|
|
|
);
|
|
|
|
allocations = Some((stack, heap));
|
|
|
|
vm
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
2023-03-10 10:05:29 -08:00
|
|
|
|
2023-04-14 06:20:08 -07:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! mock_create_vm {
|
2023-07-28 04:34:27 -07:00
|
|
|
($vm:ident, $additional_regions:expr, $accounts_metadata:expr, $invoke_context:expr $(,)?) => {
|
2023-06-02 07:34:01 -07:00
|
|
|
let loader = std::sync::Arc::new(BuiltinProgram::new_loader(
|
2023-04-14 06:20:08 -07:00
|
|
|
solana_rbpf::vm::Config::default(),
|
|
|
|
));
|
|
|
|
let function_registry = solana_rbpf::vm::FunctionRegistry::default();
|
2023-05-12 09:07:13 -07:00
|
|
|
let executable = solana_rbpf::elf::Executable::<
|
|
|
|
solana_rbpf::verifier::TautologyVerifier,
|
2023-04-14 06:20:08 -07:00
|
|
|
InvokeContext,
|
2023-05-12 09:07:13 -07:00
|
|
|
>::from_text_bytes(
|
2023-07-05 10:46:21 -07:00
|
|
|
&[0x95, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
loader,
|
|
|
|
SBPFVersion::V2,
|
|
|
|
function_registry,
|
2023-05-12 09:07:13 -07:00
|
|
|
)
|
2023-04-14 06:20:08 -07:00
|
|
|
.unwrap();
|
2023-05-12 09:07:13 -07:00
|
|
|
let verified_executable = solana_rbpf::elf::Executable::verified(executable).unwrap();
|
2023-04-14 06:20:08 -07:00
|
|
|
$crate::create_vm!(
|
|
|
|
$vm,
|
|
|
|
&verified_executable,
|
2023-03-10 10:05:29 -08:00
|
|
|
$additional_regions,
|
2023-07-28 04:34:27 -07:00
|
|
|
$accounts_metadata,
|
2023-03-10 10:05:29 -08:00
|
|
|
$invoke_context,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_memory_mapping<'a, 'b, C: ContextObject>(
|
2023-05-12 09:07:13 -07:00
|
|
|
executable: &'a Executable<RequisiteVerifier, C>,
|
2023-03-10 10:05:29 -08:00
|
|
|
stack: &'b mut AlignedMemory<{ HOST_ALIGN }>,
|
|
|
|
heap: &'b mut AlignedMemory<{ HOST_ALIGN }>,
|
|
|
|
additional_regions: Vec<MemoryRegion>,
|
|
|
|
cow_cb: Option<MemoryCowCallback>,
|
2023-04-05 06:50:34 -07:00
|
|
|
) -> Result<MemoryMapping<'a>, Box<dyn std::error::Error>> {
|
2023-03-10 10:05:29 -08:00
|
|
|
let config = executable.get_config();
|
2023-07-05 10:46:21 -07:00
|
|
|
let sbpf_version = executable.get_sbpf_version();
|
2023-03-10 10:05:29 -08:00
|
|
|
let regions: Vec<MemoryRegion> = vec![
|
|
|
|
executable.get_ro_region(),
|
|
|
|
MemoryRegion::new_writable_gapped(
|
|
|
|
stack.as_slice_mut(),
|
|
|
|
ebpf::MM_STACK_START,
|
2023-07-05 10:46:21 -07:00
|
|
|
if !sbpf_version.dynamic_stack_frames() && config.enable_stack_frame_gaps {
|
2023-03-10 10:05:29 -08:00
|
|
|
config.stack_frame_size as u64
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
},
|
|
|
|
),
|
2023-04-14 06:20:08 -07:00
|
|
|
MemoryRegion::new_writable(heap.as_slice_mut(), MM_HEAP_START),
|
2023-03-10 10:05:29 -08:00
|
|
|
]
|
|
|
|
.into_iter()
|
2023-08-24 12:44:19 -07:00
|
|
|
.chain(additional_regions)
|
2023-03-10 10:05:29 -08:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(if let Some(cow_cb) = cow_cb {
|
2023-07-05 10:46:21 -07:00
|
|
|
MemoryMapping::new_with_cow(regions, cow_cb, config, sbpf_version)?
|
2023-03-10 10:05:29 -08:00
|
|
|
} else {
|
2023-07-05 10:46:21 -07:00
|
|
|
MemoryMapping::new(regions, config, sbpf_version)?
|
2023-03-10 10:05:29 -08:00
|
|
|
})
|
2020-08-21 15:31:19 -07:00
|
|
|
}
|
|
|
|
|
2023-04-05 06:50:34 -07:00
|
|
|
pub fn process_instruction(
|
|
|
|
invoke_context: &mut InvokeContext,
|
2023-04-21 09:08:32 -07:00
|
|
|
_arg0: u64,
|
|
|
|
_arg1: u64,
|
|
|
|
_arg2: u64,
|
|
|
|
_arg3: u64,
|
|
|
|
_arg4: u64,
|
|
|
|
_memory_mapping: &mut MemoryMapping,
|
|
|
|
result: &mut ProgramResult,
|
|
|
|
) {
|
2023-04-25 10:04:11 -07:00
|
|
|
*result = process_instruction_inner(invoke_context).into();
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2020-03-05 10:57:35 -08:00
|
|
|
|
2023-04-05 06:50:34 -07:00
|
|
|
fn process_instruction_inner(
|
2021-12-02 09:47:16 -08:00
|
|
|
invoke_context: &mut InvokeContext,
|
2023-04-21 09:08:32 -07:00
|
|
|
) -> Result<u64, Box<dyn std::error::Error>> {
|
2021-11-23 04:23:40 -08:00
|
|
|
let log_collector = invoke_context.get_log_collector();
|
2022-02-17 01:16:28 -08:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2023-03-21 03:08:41 -07:00
|
|
|
|
|
|
|
if !invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&remove_bpf_loader_incorrect_program_id::id())
|
|
|
|
{
|
|
|
|
fn get_index_in_transaction(
|
|
|
|
instruction_context: &InstructionContext,
|
|
|
|
index_in_instruction: IndexOfAccount,
|
|
|
|
) -> Result<IndexOfAccount, InstructionError> {
|
|
|
|
if index_in_instruction < instruction_context.get_number_of_program_accounts() {
|
|
|
|
instruction_context
|
|
|
|
.get_index_of_program_account_in_transaction(index_in_instruction)
|
|
|
|
} else {
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(
|
|
|
|
index_in_instruction
|
|
|
|
.saturating_sub(instruction_context.get_number_of_program_accounts()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_borrow_account<'a>(
|
|
|
|
transaction_context: &'a TransactionContext,
|
|
|
|
instruction_context: &'a InstructionContext,
|
|
|
|
index_in_instruction: IndexOfAccount,
|
|
|
|
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
|
|
|
if index_in_instruction < instruction_context.get_number_of_program_accounts() {
|
|
|
|
instruction_context
|
|
|
|
.try_borrow_program_account(transaction_context, index_in_instruction)
|
|
|
|
} else {
|
|
|
|
instruction_context.try_borrow_instruction_account(
|
|
|
|
transaction_context,
|
|
|
|
index_in_instruction
|
|
|
|
.saturating_sub(instruction_context.get_number_of_program_accounts()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let first_instruction_account = {
|
|
|
|
let borrowed_root_account =
|
|
|
|
instruction_context.try_borrow_program_account(transaction_context, 0)?;
|
|
|
|
let owner_id = borrowed_root_account.get_owner();
|
|
|
|
if native_loader::check_id(owner_id) {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let first_account_key = transaction_context.get_key_of_account_at_index(
|
|
|
|
get_index_in_transaction(instruction_context, first_instruction_account)?,
|
|
|
|
)?;
|
|
|
|
let second_account_key = get_index_in_transaction(
|
|
|
|
instruction_context,
|
|
|
|
first_instruction_account.saturating_add(1),
|
|
|
|
)
|
|
|
|
.and_then(|index_in_transaction| {
|
|
|
|
transaction_context.get_key_of_account_at_index(index_in_transaction)
|
|
|
|
});
|
|
|
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
2023-08-03 06:26:02 -07:00
|
|
|
let program_account_index = if first_account_key == program_id {
|
|
|
|
first_instruction_account
|
|
|
|
} else if second_account_key
|
|
|
|
.map(|key| key == program_id)
|
|
|
|
.unwrap_or(false)
|
2023-03-21 03:08:41 -07:00
|
|
|
{
|
2023-08-03 06:26:02 -07:00
|
|
|
first_instruction_account.saturating_add(1)
|
2023-03-21 03:08:41 -07:00
|
|
|
} else {
|
|
|
|
let first_account = try_borrow_account(
|
|
|
|
transaction_context,
|
|
|
|
instruction_context,
|
|
|
|
first_instruction_account,
|
|
|
|
)?;
|
|
|
|
if first_account.is_executable() {
|
|
|
|
ic_logger_msg!(log_collector, "BPF loader is executable");
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::IncorrectProgramId));
|
2023-03-21 03:08:41 -07:00
|
|
|
}
|
2023-08-03 06:26:02 -07:00
|
|
|
first_instruction_account
|
|
|
|
};
|
|
|
|
let program = try_borrow_account(
|
|
|
|
transaction_context,
|
|
|
|
instruction_context,
|
|
|
|
program_account_index,
|
|
|
|
)?;
|
|
|
|
if program.is_executable() && !check_loader_id(program.get_owner()) {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Executable account not owned by the BPF loader"
|
|
|
|
);
|
|
|
|
return Err(Box::new(InstructionError::IncorrectProgramId));
|
2023-03-21 03:08:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 07:54:19 -08:00
|
|
|
let program_account =
|
|
|
|
instruction_context.try_borrow_last_program_account(transaction_context)?;
|
|
|
|
|
2023-03-24 09:31:01 -07:00
|
|
|
// Consume compute units if feature `native_programs_consume_cu` is activated
|
|
|
|
let native_programs_consume_cu = invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&native_programs_consume_cu::id());
|
|
|
|
|
2023-03-08 07:54:19 -08:00
|
|
|
// Program Management Instruction
|
|
|
|
if native_loader::check_id(program_account.get_owner()) {
|
|
|
|
drop(program_account);
|
|
|
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
|
|
|
return if bpf_loader_upgradeable::check_id(program_id) {
|
2023-03-24 09:31:01 -07:00
|
|
|
if native_programs_consume_cu {
|
2023-06-02 08:22:22 -07:00
|
|
|
invoke_context.consume_checked(UPGRADEABLE_LOADER_COMPUTE_UNITS)?;
|
2023-03-24 09:31:01 -07:00
|
|
|
}
|
2023-04-25 10:04:11 -07:00
|
|
|
process_loader_upgradeable_instruction(invoke_context)
|
2023-01-14 04:02:10 -08:00
|
|
|
} else if bpf_loader::check_id(program_id) {
|
2023-03-24 09:31:01 -07:00
|
|
|
if native_programs_consume_cu {
|
2023-06-02 08:22:22 -07:00
|
|
|
invoke_context.consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?;
|
2023-03-24 09:31:01 -07:00
|
|
|
}
|
2023-04-25 10:04:11 -07:00
|
|
|
process_loader_instruction(invoke_context)
|
2023-01-14 04:02:10 -08:00
|
|
|
} else if bpf_loader_deprecated::check_id(program_id) {
|
2023-03-24 09:31:01 -07:00
|
|
|
if native_programs_consume_cu {
|
2023-06-02 08:22:22 -07:00
|
|
|
invoke_context.consume_checked(DEPRECATED_LOADER_COMPUTE_UNITS)?;
|
2023-03-24 09:31:01 -07:00
|
|
|
}
|
2022-03-18 14:29:49 -07:00
|
|
|
ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
|
|
|
|
Err(InstructionError::UnsupportedProgramId)
|
2021-10-12 23:58:20 -07:00
|
|
|
} else {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid BPF loader id");
|
2021-10-12 23:58:20 -07:00
|
|
|
Err(InstructionError::IncorrectProgramId)
|
2023-04-05 06:50:34 -07:00
|
|
|
}
|
2023-04-21 09:08:32 -07:00
|
|
|
.map(|_| 0)
|
2023-04-05 06:50:34 -07:00
|
|
|
.map_err(|error| Box::new(error) as Box<dyn std::error::Error>);
|
2023-03-08 07:54:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Program Invocation
|
|
|
|
if !program_account.is_executable() {
|
|
|
|
ic_logger_msg!(log_collector, "Program is not executable");
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::IncorrectProgramId));
|
2023-03-08 07:54:19 -08:00
|
|
|
}
|
2023-03-28 11:49:56 -07:00
|
|
|
|
2023-03-08 07:54:19 -08:00
|
|
|
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
|
2023-08-16 10:50:23 -07:00
|
|
|
let executor = invoke_context
|
|
|
|
.find_program_in_cache(program_account.get_key())
|
2023-05-02 14:39:27 -07:00
|
|
|
.ok_or(InstructionError::InvalidAccountData)?;
|
|
|
|
|
|
|
|
if executor.is_tombstone() {
|
|
|
|
return Err(Box::new(InstructionError::InvalidAccountData));
|
|
|
|
}
|
|
|
|
|
2023-03-08 07:54:19 -08:00
|
|
|
drop(program_account);
|
|
|
|
get_or_create_executor_time.stop();
|
|
|
|
saturating_add_assign!(
|
|
|
|
invoke_context.timings.get_or_create_executor_us,
|
|
|
|
get_or_create_executor_time.as_us()
|
|
|
|
);
|
2023-03-27 06:48:05 -07:00
|
|
|
|
2023-06-02 11:26:56 -07:00
|
|
|
executor.ix_usage_counter.fetch_add(1, Ordering::Relaxed);
|
2023-03-08 07:54:19 -08:00
|
|
|
match &executor.program {
|
2023-06-06 15:24:39 -07:00
|
|
|
LoadedProgramType::FailedVerification(_)
|
2023-03-28 11:49:56 -07:00
|
|
|
| LoadedProgramType::Closed
|
2023-04-21 09:08:32 -07:00
|
|
|
| LoadedProgramType::DelayVisibility => {
|
|
|
|
Err(Box::new(InstructionError::InvalidAccountData) as Box<dyn std::error::Error>)
|
|
|
|
}
|
2023-03-08 07:54:19 -08:00
|
|
|
LoadedProgramType::LegacyV0(executable) => execute(executable, invoke_context),
|
|
|
|
LoadedProgramType::LegacyV1(executable) => execute(executable, invoke_context),
|
2023-04-21 09:08:32 -07:00
|
|
|
_ => Err(Box::new(InstructionError::IncorrectProgramId) as Box<dyn std::error::Error>),
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2023-04-21 09:08:32 -07:00
|
|
|
.map(|_| 0)
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn process_loader_upgradeable_instruction(
|
2021-12-02 09:47:16 -08:00
|
|
|
invoke_context: &mut InvokeContext,
|
2020-12-14 15:35:10 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2021-11-23 04:23:40 -08:00
|
|
|
let log_collector = invoke_context.get_log_collector();
|
2022-02-17 01:16:28 -08:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2022-04-01 06:48:05 -07:00
|
|
|
let instruction_data = instruction_context.get_instruction_data();
|
2022-06-16 09:46:17 -07:00
|
|
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
match limited_deserialize(instruction_data)? {
|
|
|
|
UpgradeableLoaderInstruction::InitializeBuffer => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
if UpgradeableLoaderState::Uninitialized != buffer.get_state()? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer account already initialized");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::AccountAlreadyInitialized);
|
|
|
|
}
|
2021-01-29 12:43:42 -08:00
|
|
|
|
2022-06-16 09:46:17 -07:00
|
|
|
let authority_key = Some(*transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
|
|
|
|
)?);
|
2022-03-16 03:30:01 -07:00
|
|
|
|
|
|
|
buffer.set_state(&UpgradeableLoaderState::Buffer {
|
2022-04-19 02:35:40 -07:00
|
|
|
authority_address: authority_key,
|
2022-03-16 03:30:01 -07:00
|
|
|
})?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::Write { offset, bytes } => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
|
2022-03-16 03:30:01 -07:00
|
|
|
if authority_address.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer is immutable");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::Immutable); // TODO better error code
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
let authority_key = Some(*transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
|
|
|
|
)?);
|
2022-04-19 02:35:40 -07:00
|
|
|
if authority_address != authority_key {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Buffer authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
} else {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid Buffer account");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2022-03-01 13:55:26 -08:00
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(buffer);
|
2020-12-14 15:35:10 -08:00
|
|
|
write_program_data(
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_buffer_metadata().saturating_add(offset as usize),
|
2020-12-14 15:35:10 -08:00
|
|
|
&bytes,
|
2022-03-16 03:30:01 -07:00
|
|
|
invoke_context,
|
2020-12-14 15:35:10 -08:00
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(4)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let payer_key = *transaction_context.get_key_of_account_at_index(
|
2022-06-16 09:46:17 -07:00
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(0)?,
|
2022-03-16 03:30:01 -07:00
|
|
|
)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let programdata_key = *transaction_context.get_key_of_account_at_index(
|
2022-06-16 09:46:17 -07:00
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
|
2022-03-16 03:30:01 -07:00
|
|
|
)?;
|
2022-03-24 11:30:42 -07:00
|
|
|
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?;
|
|
|
|
let clock =
|
|
|
|
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?;
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(8)?;
|
2022-06-16 09:46:17 -07:00
|
|
|
let authority_key = Some(*transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(7)?,
|
|
|
|
)?);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Verify Program account
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let program =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
|
|
|
if UpgradeableLoaderState::Uninitialized != program.get_state()? {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program account already initialized");
|
|
|
|
return Err(InstructionError::AccountAlreadyInitialized);
|
|
|
|
}
|
2022-05-11 07:22:59 -07:00
|
|
|
if program.get_data().len() < UpgradeableLoaderState::size_of_program() {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program account too small");
|
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if program.get_lamports() < rent.minimum_balance(program.get_data().len()) {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program account not rent-exempt");
|
|
|
|
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let new_program_id = *program.get_key();
|
|
|
|
drop(program);
|
2021-09-01 17:59:24 -07:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
// Verify Buffer account
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
|
|
|
|
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
|
|
|
|
if authority_address != authority_key {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
|
2021-03-10 09:48:41 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(7)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
|
2021-03-10 09:48:41 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2021-01-29 12:43:42 -08:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
} else {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid Buffer account");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer_key = *buffer.get_key();
|
2022-05-11 07:22:59 -07:00
|
|
|
let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
|
2022-05-11 07:22:59 -07:00
|
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
|
|
|
let programdata_len = UpgradeableLoaderState::size_of_programdata(max_data_len);
|
|
|
|
if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
|
2021-01-19 17:56:44 -08:00
|
|
|
|| buffer_data_len == 0
|
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer account too small");
|
2021-01-19 17:56:44 -08:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(buffer);
|
2020-12-14 15:35:10 -08:00
|
|
|
if max_data_len < buffer_data_len {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Max data length is too small to hold Buffer data"
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2021-02-03 09:16:25 -08:00
|
|
|
if programdata_len > MAX_PERMITTED_DATA_LENGTH as usize {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Max data length is too large");
|
2021-02-03 09:16:25 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Create ProgramData account
|
|
|
|
let (derived_address, bump_seed) =
|
2021-09-01 17:59:24 -07:00
|
|
|
Pubkey::find_program_address(&[new_program_id.as_ref()], program_id);
|
2022-04-19 02:35:40 -07:00
|
|
|
if derived_address != programdata_key {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "ProgramData address is not derived");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2022-07-15 08:08:14 -07:00
|
|
|
// Drain the Buffer account to payer before paying for programdata account
|
|
|
|
{
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
|
2022-09-13 10:58:19 -07:00
|
|
|
let mut payer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
payer.checked_add_lamports(buffer.get_lamports())?;
|
2022-05-21 08:47:09 -07:00
|
|
|
buffer.set_lamports(0)?;
|
2022-03-16 03:30:01 -07:00
|
|
|
}
|
2021-09-17 17:49:53 -07:00
|
|
|
|
2023-02-17 03:47:45 -08:00
|
|
|
let owner_id = *program_id;
|
2022-03-16 03:30:01 -07:00
|
|
|
let mut instruction = system_instruction::create_account(
|
2022-04-19 02:35:40 -07:00
|
|
|
&payer_key,
|
|
|
|
&programdata_key,
|
2022-03-16 03:30:01 -07:00
|
|
|
1.max(rent.minimum_balance(programdata_len)),
|
|
|
|
programdata_len as u64,
|
|
|
|
program_id,
|
|
|
|
);
|
2021-09-17 17:49:53 -07:00
|
|
|
|
|
|
|
// pass an extra account to avoid the overly strict UnbalancedInstruction error
|
|
|
|
instruction
|
|
|
|
.accounts
|
2022-04-19 02:35:40 -07:00
|
|
|
.push(AccountMeta::new(buffer_key, false));
|
2021-09-17 17:49:53 -07:00
|
|
|
|
2022-02-17 01:16:28 -08:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2022-06-16 09:46:17 -07:00
|
|
|
let caller_program_id =
|
|
|
|
instruction_context.get_last_program_key(transaction_context)?;
|
2022-10-19 14:22:57 -07:00
|
|
|
let signers = [[new_program_id.as_ref(), &[bump_seed]]]
|
2021-04-19 09:48:48 -07:00
|
|
|
.iter()
|
2022-10-19 14:22:57 -07:00
|
|
|
.map(|seeds| Pubkey::create_program_address(seeds, caller_program_id))
|
2021-04-19 09:48:48 -07:00
|
|
|
.collect::<Result<Vec<Pubkey>, solana_sdk::pubkey::PubkeyError>>()?;
|
2023-02-16 05:16:25 -08:00
|
|
|
invoke_context.native_invoke(instruction.into(), signers.as_slice())?;
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Load and verify the program bits
|
2022-10-18 01:22:39 -07:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
|
2023-02-11 02:18:25 -08:00
|
|
|
deploy_program!(
|
|
|
|
invoke_context,
|
|
|
|
new_program_id,
|
2023-02-17 03:47:45 -08:00
|
|
|
&owner_id,
|
|
|
|
UpgradeableLoaderState::size_of_program().saturating_add(programdata_len),
|
|
|
|
clock.slot,
|
2023-02-11 02:18:25 -08:00
|
|
|
{
|
|
|
|
drop(buffer);
|
|
|
|
},
|
2022-10-18 01:22:39 -07:00
|
|
|
buffer
|
|
|
|
.get_data()
|
|
|
|
.get(buffer_data_offset..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?,
|
2023-02-11 02:18:25 -08:00
|
|
|
);
|
2021-04-19 09:48:48 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Update the ProgramData account and record the program bits
|
2022-04-19 02:35:40 -07:00
|
|
|
{
|
|
|
|
let mut programdata =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
|
|
|
|
programdata.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: clock.slot,
|
|
|
|
upgrade_authority_address: authority_key,
|
|
|
|
})?;
|
|
|
|
let dst_slice = programdata
|
2022-05-21 08:47:09 -07:00
|
|
|
.get_data_mut()?
|
2022-04-19 02:35:40 -07:00
|
|
|
.get_mut(
|
|
|
|
programdata_data_offset
|
|
|
|
..programdata_data_offset.saturating_add(buffer_data_len),
|
|
|
|
)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?;
|
2023-02-01 09:07:34 -08:00
|
|
|
let mut buffer =
|
2022-04-19 02:35:40 -07:00
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
|
|
|
|
let src_slice = buffer
|
|
|
|
.get_data()
|
|
|
|
.get(buffer_data_offset..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?;
|
|
|
|
dst_slice.copy_from_slice(src_slice);
|
2023-02-01 09:07:34 -08:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_program_redeployment_cooldown::id())
|
|
|
|
{
|
|
|
|
buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-03-02 00:45:53 -08:00
|
|
|
// Update the Program account
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut program =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
2022-03-16 03:30:01 -07:00
|
|
|
program.set_state(&UpgradeableLoaderState::Program {
|
2022-04-19 02:35:40 -07:00
|
|
|
programdata_address: programdata_key,
|
2022-03-16 03:30:01 -07:00
|
|
|
})?;
|
2022-05-21 08:47:09 -07:00
|
|
|
program.set_executable(true)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(program);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::Upgrade => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(3)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let programdata_key = *transaction_context.get_key_of_account_at_index(
|
2022-06-16 09:46:17 -07:00
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(0)?,
|
2022-03-16 03:30:01 -07:00
|
|
|
)?;
|
2022-03-24 11:30:42 -07:00
|
|
|
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?;
|
|
|
|
let clock =
|
|
|
|
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?;
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(7)?;
|
2022-06-16 09:46:17 -07:00
|
|
|
let authority_key = Some(*transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(6)?,
|
|
|
|
)?);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Verify Program account
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let program =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
|
|
|
|
if !program.is_executable() {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program account not executable");
|
|
|
|
return Err(InstructionError::AccountNotExecutable);
|
|
|
|
}
|
|
|
|
if !program.is_writable() {
|
|
|
|
ic_logger_msg!(log_collector, "Program account not writeable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if program.get_owner() != program_id {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program account not owned by loader");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
if let UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
2022-04-19 02:35:40 -07:00
|
|
|
} = program.get_state()?
|
2022-03-16 03:30:01 -07:00
|
|
|
{
|
2022-04-19 02:35:40 -07:00
|
|
|
if programdata_address != programdata_key {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_logger_msg!(log_collector, "Program and ProgramData account mismatch");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2022-03-16 03:30:01 -07:00
|
|
|
} else {
|
|
|
|
ic_logger_msg!(log_collector, "Invalid Program account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let new_program_id = *program.get_key();
|
|
|
|
drop(program);
|
2021-09-01 17:59:24 -07:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
// Verify Buffer account
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
|
|
|
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
|
|
|
|
if authority_address != authority_key {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
|
2021-03-10 09:48:41 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(6)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
|
2021-03-10 09:48:41 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2021-01-29 12:43:42 -08:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
} else {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid Buffer account");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer_lamports = buffer.get_lamports();
|
2022-05-11 07:22:59 -07:00
|
|
|
let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
|
2022-04-19 02:35:40 -07:00
|
|
|
let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
|
2022-05-11 07:22:59 -07:00
|
|
|
if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
|
2021-01-19 17:56:44 -08:00
|
|
|
|| buffer_data_len == 0
|
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer account too small");
|
2021-01-19 17:56:44 -08:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(buffer);
|
2021-01-19 17:56:44 -08:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
// Verify ProgramData account
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let programdata =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
2022-05-11 07:22:59 -07:00
|
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
2022-04-19 02:35:40 -07:00
|
|
|
let programdata_balance_required =
|
|
|
|
1.max(rent.minimum_balance(programdata.get_data().len()));
|
|
|
|
if programdata.get_data().len()
|
2022-05-11 07:22:59 -07:00
|
|
|
< UpgradeableLoaderState::size_of_programdata(buffer_data_len)
|
2022-04-19 02:35:40 -07:00
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "ProgramData account not large enough");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if programdata.get_lamports().saturating_add(buffer_lamports)
|
2022-03-02 14:50:16 -08:00
|
|
|
< programdata_balance_required
|
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Buffer account balance too low to fund upgrade"
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InsufficientFunds);
|
|
|
|
}
|
2023-03-27 06:48:26 -07:00
|
|
|
if let UpgradeableLoaderState::ProgramData {
|
2022-12-13 13:54:24 -08:00
|
|
|
slot,
|
2020-12-14 15:35:10 -08:00
|
|
|
upgrade_authority_address,
|
2022-04-19 02:35:40 -07:00
|
|
|
} = programdata.get_state()?
|
2020-12-14 15:35:10 -08:00
|
|
|
{
|
2022-12-13 13:54:24 -08:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_program_redeployment_cooldown::id())
|
|
|
|
&& clock.slot == slot
|
|
|
|
{
|
|
|
|
ic_logger_msg!(log_collector, "Program was deployed in this block already");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2021-03-10 09:48:41 -08:00
|
|
|
if upgrade_authority_address.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Program not upgradeable");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::Immutable);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if upgrade_authority_address != authority_key {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(6)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
} else {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid ProgramData account");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2023-02-17 03:47:45 -08:00
|
|
|
};
|
|
|
|
let programdata_len = programdata.get_data().len();
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(programdata);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Load and verify the program bits
|
2022-10-18 01:22:39 -07:00
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
2023-02-11 02:18:25 -08:00
|
|
|
deploy_program!(
|
|
|
|
invoke_context,
|
|
|
|
new_program_id,
|
2023-02-17 03:47:45 -08:00
|
|
|
program_id,
|
|
|
|
UpgradeableLoaderState::size_of_program().saturating_add(programdata_len),
|
2023-03-27 06:48:26 -07:00
|
|
|
clock.slot,
|
2023-02-11 02:18:25 -08:00
|
|
|
{
|
|
|
|
drop(buffer);
|
|
|
|
},
|
2022-10-18 01:22:39 -07:00
|
|
|
buffer
|
|
|
|
.get_data()
|
|
|
|
.get(buffer_data_offset..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?,
|
2023-02-11 02:18:25 -08:00
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2022-03-16 03:30:01 -07:00
|
|
|
// Update the ProgramData account, record the upgraded data, and zero
|
|
|
|
// the rest
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut programdata =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
{
|
|
|
|
programdata.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: clock.slot,
|
|
|
|
upgrade_authority_address: authority_key,
|
|
|
|
})?;
|
|
|
|
let dst_slice = programdata
|
2022-05-21 08:47:09 -07:00
|
|
|
.get_data_mut()?
|
2022-04-19 02:35:40 -07:00
|
|
|
.get_mut(
|
|
|
|
programdata_data_offset
|
|
|
|
..programdata_data_offset.saturating_add(buffer_data_len),
|
|
|
|
)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?;
|
|
|
|
let buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
|
|
|
let src_slice = buffer
|
|
|
|
.get_data()
|
|
|
|
.get(buffer_data_offset..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?;
|
|
|
|
dst_slice.copy_from_slice(src_slice);
|
|
|
|
}
|
2022-03-10 11:48:33 -08:00
|
|
|
programdata
|
2022-05-21 08:47:09 -07:00
|
|
|
.get_data_mut()?
|
2022-03-10 11:48:33 -08:00
|
|
|
.get_mut(programdata_data_offset.saturating_add(buffer_data_len)..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?
|
2022-03-02 14:50:16 -08:00
|
|
|
.fill(0);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Fund ProgramData to rent-exemption, spill the rest
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut buffer =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
|
|
|
|
let mut spill =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
|
|
|
|
spill.checked_add_lamports(
|
2022-09-13 10:58:19 -07:00
|
|
|
programdata
|
|
|
|
.get_lamports()
|
2022-04-19 02:35:40 -07:00
|
|
|
.saturating_add(buffer_lamports)
|
2022-03-02 14:50:16 -08:00
|
|
|
.saturating_sub(programdata_balance_required),
|
2022-03-16 03:30:01 -07:00
|
|
|
)?;
|
2022-09-13 10:58:19 -07:00
|
|
|
buffer.set_lamports(0)?;
|
|
|
|
programdata.set_lamports(programdata_balance_required)?;
|
2023-02-01 09:07:34 -08:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_program_redeployment_cooldown::id())
|
|
|
|
{
|
|
|
|
buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::SetAuthority => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut account =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let present_authority_key = transaction_context.get_key_of_account_at_index(
|
2022-06-16 09:46:17 -07:00
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
|
2022-03-01 13:55:26 -08:00
|
|
|
)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
let new_authority = instruction_context
|
2022-06-16 09:46:17 -07:00
|
|
|
.get_index_of_instruction_account_in_transaction(2)
|
2022-04-19 02:35:40 -07:00
|
|
|
.and_then(|index_in_transaction| {
|
|
|
|
transaction_context.get_key_of_account_at_index(index_in_transaction)
|
|
|
|
})
|
|
|
|
.ok();
|
|
|
|
|
|
|
|
match account.get_state()? {
|
2021-01-08 09:37:57 -08:00
|
|
|
UpgradeableLoaderState::Buffer { authority_address } => {
|
2021-03-10 09:48:41 -08:00
|
|
|
if new_authority.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer authority is not optional");
|
2021-01-29 12:43:42 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2021-03-10 09:48:41 -08:00
|
|
|
if authority_address.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer is immutable");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if authority_address != Some(*present_authority_key) {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Buffer authority did not sign");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::Buffer {
|
2022-03-16 03:30:01 -07:00
|
|
|
authority_address: new_authority.cloned(),
|
2021-01-08 09:37:57 -08:00
|
|
|
})?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address,
|
|
|
|
} => {
|
2021-03-10 09:48:41 -08:00
|
|
|
if upgrade_authority_address.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Program not upgradeable");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if upgrade_authority_address != Some(*present_authority_key) {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
2022-03-16 03:30:01 -07:00
|
|
|
upgrade_authority_address: new_authority.cloned(),
|
2021-01-08 09:37:57 -08:00
|
|
|
})?;
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
_ => {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Account does not support authorities");
|
2021-03-17 21:39:29 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "New authority {:?}", new_authority);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2022-11-01 01:34:04 -07:00
|
|
|
UpgradeableLoaderInstruction::SetAuthorityChecked => {
|
|
|
|
if !invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_bpf_loader_set_authority_checked_ix::id())
|
|
|
|
{
|
|
|
|
return Err(InstructionError::InvalidInstructionData);
|
|
|
|
}
|
|
|
|
|
|
|
|
instruction_context.check_number_of_instruction_accounts(3)?;
|
|
|
|
let mut account =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let present_authority_key = transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
|
|
|
|
)?;
|
|
|
|
let new_authority_key = transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(2)?,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
match account.get_state()? {
|
|
|
|
UpgradeableLoaderState::Buffer { authority_address } => {
|
|
|
|
if authority_address.is_none() {
|
|
|
|
ic_logger_msg!(log_collector, "Buffer is immutable");
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
|
|
|
if authority_address != Some(*present_authority_key) {
|
|
|
|
ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
|
|
|
ic_logger_msg!(log_collector, "Buffer authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
if !instruction_context.is_instruction_account_signer(2)? {
|
|
|
|
ic_logger_msg!(log_collector, "New authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(*new_authority_key),
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address,
|
|
|
|
} => {
|
|
|
|
if upgrade_authority_address.is_none() {
|
|
|
|
ic_logger_msg!(log_collector, "Program not upgradeable");
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
|
|
|
if upgrade_authority_address != Some(*present_authority_key) {
|
|
|
|
ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
|
|
|
if !instruction_context.is_instruction_account_signer(1)? {
|
|
|
|
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
if !instruction_context.is_instruction_account_signer(2)? {
|
|
|
|
ic_logger_msg!(log_collector, "New authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(*new_authority_key),
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
ic_logger_msg!(log_collector, "Account does not support authorities");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ic_logger_msg!(log_collector, "New authority {:?}", new_authority_key);
|
|
|
|
}
|
2021-03-17 21:39:29 -07:00
|
|
|
UpgradeableLoaderInstruction::Close => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
2022-06-16 09:46:17 -07:00
|
|
|
if instruction_context.get_index_of_instruction_account_in_transaction(0)?
|
|
|
|
== instruction_context.get_index_of_instruction_account_in_transaction(1)?
|
2022-04-19 02:35:40 -07:00
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Recipient is the same as the account being closed"
|
|
|
|
);
|
2021-03-17 21:39:29 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut close_account =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let close_key = *close_account.get_key();
|
2022-12-13 13:54:24 -08:00
|
|
|
let close_account_state = close_account.get_state()?;
|
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_program_redeployment_cooldown::id())
|
|
|
|
{
|
|
|
|
close_account.set_data_length(UpgradeableLoaderState::size_of_uninitialized())?;
|
|
|
|
}
|
|
|
|
match close_account_state {
|
2021-08-24 10:05:54 -07:00
|
|
|
UpgradeableLoaderState::Uninitialized => {
|
2022-04-19 02:35:40 -07:00
|
|
|
let mut recipient_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, 1)?;
|
2022-09-13 10:58:19 -07:00
|
|
|
recipient_account.checked_add_lamports(close_account.get_lamports())?;
|
|
|
|
close_account.set_lamports(0)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
|
|
|
|
ic_logger_msg!(log_collector, "Closed Uninitialized {}", close_key);
|
2021-03-17 21:39:29 -07:00
|
|
|
}
|
2021-08-24 10:05:54 -07:00
|
|
|
UpgradeableLoaderState::Buffer { authority_address } => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(3)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(close_account);
|
2021-08-24 10:05:54 -07:00
|
|
|
common_close_account(
|
|
|
|
&authority_address,
|
2022-03-30 00:17:55 -07:00
|
|
|
transaction_context,
|
|
|
|
instruction_context,
|
2021-11-23 04:23:40 -08:00
|
|
|
&log_collector,
|
2021-08-24 10:05:54 -07:00
|
|
|
)?;
|
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
ic_logger_msg!(log_collector, "Closed Buffer {}", close_key);
|
2021-03-17 21:39:29 -07:00
|
|
|
}
|
2021-08-24 10:05:54 -07:00
|
|
|
UpgradeableLoaderState::ProgramData {
|
2023-02-01 09:07:34 -08:00
|
|
|
slot,
|
2021-08-24 10:05:54 -07:00
|
|
|
upgrade_authority_address: authority_address,
|
|
|
|
} => {
|
2022-03-29 10:06:50 -07:00
|
|
|
instruction_context.check_number_of_instruction_accounts(4)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(close_account);
|
|
|
|
let program_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, 3)?;
|
|
|
|
let program_key = *program_account.get_key();
|
2022-03-16 03:30:01 -07:00
|
|
|
|
|
|
|
if !program_account.is_writable() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Program account is not writable");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
if program_account.get_owner() != program_id {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Program account not owned by loader");
|
2021-08-27 08:44:38 -07:00
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
2023-02-01 09:07:34 -08:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&enable_program_redeployment_cooldown::id())
|
|
|
|
{
|
|
|
|
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
|
|
|
if clock.slot == slot {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program was deployed in this block already"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
}
|
2021-08-24 10:05:54 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
match program_account.get_state()? {
|
2021-08-24 10:05:54 -07:00
|
|
|
UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
|
|
|
} => {
|
2022-04-19 02:35:40 -07:00
|
|
|
if programdata_address != close_key {
|
2021-08-24 10:05:54 -07:00
|
|
|
ic_logger_msg!(
|
2021-11-23 04:23:40 -08:00
|
|
|
log_collector,
|
2021-08-24 10:05:54 -07:00
|
|
|
"ProgramData account does not match ProgramData account"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
drop(program_account);
|
2021-08-24 10:05:54 -07:00
|
|
|
common_close_account(
|
|
|
|
&authority_address,
|
2022-03-30 00:17:55 -07:00
|
|
|
transaction_context,
|
|
|
|
instruction_context,
|
2021-11-23 04:23:40 -08:00
|
|
|
&log_collector,
|
2021-08-24 10:05:54 -07:00
|
|
|
)?;
|
2023-05-09 15:44:38 -07:00
|
|
|
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
2023-02-11 02:18:25 -08:00
|
|
|
if invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&delay_visibility_of_program_deployment::id())
|
|
|
|
{
|
2023-05-17 06:10:50 -07:00
|
|
|
invoke_context.programs_modified_by_tx.replenish(
|
|
|
|
program_key,
|
|
|
|
Arc::new(LoadedProgram::new_tombstone(
|
|
|
|
clock.slot,
|
|
|
|
LoadedProgramType::Closed,
|
|
|
|
)),
|
|
|
|
);
|
2023-05-09 15:44:38 -07:00
|
|
|
} else {
|
|
|
|
invoke_context
|
|
|
|
.programs_updated_only_for_global_cache
|
|
|
|
.replenish(
|
|
|
|
program_key,
|
|
|
|
Arc::new(LoadedProgram::new_tombstone(
|
|
|
|
clock.slot,
|
|
|
|
LoadedProgramType::Closed,
|
|
|
|
)),
|
|
|
|
);
|
2023-02-11 02:18:25 -08:00
|
|
|
}
|
2021-08-24 10:05:54 -07:00
|
|
|
}
|
|
|
|
_ => {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Invalid Program account");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
}
|
2021-03-17 21:39:29 -07:00
|
|
|
|
2022-04-19 02:35:40 -07:00
|
|
|
ic_logger_msg!(log_collector, "Closed Program {}", program_key);
|
2021-08-24 10:05:54 -07:00
|
|
|
}
|
|
|
|
_ => {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Account does not support closing");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
2021-03-19 13:13:20 -07:00
|
|
|
}
|
2021-03-17 21:39:29 -07:00
|
|
|
}
|
|
|
|
}
|
2022-09-21 21:03:06 -07:00
|
|
|
UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => {
|
2022-07-11 14:46:32 -07:00
|
|
|
if !invoke_context
|
|
|
|
.feature_set
|
2022-09-21 21:03:06 -07:00
|
|
|
.is_active(&enable_bpf_loader_extend_program_ix::ID)
|
2022-07-11 14:46:32 -07:00
|
|
|
{
|
|
|
|
return Err(InstructionError::InvalidInstructionData);
|
|
|
|
}
|
|
|
|
|
|
|
|
if additional_bytes == 0 {
|
|
|
|
ic_logger_msg!(log_collector, "Additional bytes must be greater than 0");
|
|
|
|
return Err(InstructionError::InvalidInstructionData);
|
|
|
|
}
|
|
|
|
|
2022-09-06 02:31:40 -07:00
|
|
|
const PROGRAM_DATA_ACCOUNT_INDEX: IndexOfAccount = 0;
|
2022-09-21 21:03:06 -07:00
|
|
|
const PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 1;
|
2022-07-11 14:46:32 -07:00
|
|
|
#[allow(dead_code)]
|
|
|
|
// System program is only required when a CPI is performed
|
2022-09-21 21:03:06 -07:00
|
|
|
const OPTIONAL_SYSTEM_PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 2;
|
|
|
|
const OPTIONAL_PAYER_ACCOUNT_INDEX: IndexOfAccount = 3;
|
2022-07-11 14:46:32 -07:00
|
|
|
|
|
|
|
let programdata_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?;
|
|
|
|
let programdata_key = *programdata_account.get_key();
|
|
|
|
|
|
|
|
if program_id != programdata_account.get_owner() {
|
|
|
|
ic_logger_msg!(log_collector, "ProgramData owner is invalid");
|
|
|
|
return Err(InstructionError::InvalidAccountOwner);
|
|
|
|
}
|
|
|
|
if !programdata_account.is_writable() {
|
|
|
|
ic_logger_msg!(log_collector, "ProgramData is not writable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2022-09-21 21:03:06 -07:00
|
|
|
let program_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, PROGRAM_ACCOUNT_INDEX)?;
|
|
|
|
if !program_account.is_writable() {
|
|
|
|
ic_logger_msg!(log_collector, "Program account is not writable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
if program_account.get_owner() != program_id {
|
|
|
|
ic_logger_msg!(log_collector, "Program account not owned by loader");
|
|
|
|
return Err(InstructionError::InvalidAccountOwner);
|
|
|
|
}
|
2023-06-01 06:17:42 -07:00
|
|
|
let program_key = *program_account.get_key();
|
2022-09-21 21:03:06 -07:00
|
|
|
match program_account.get_state()? {
|
|
|
|
UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
|
|
|
} => {
|
|
|
|
if programdata_address != programdata_key {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program account does not match ProgramData account"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
ic_logger_msg!(log_collector, "Invalid Program account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drop(program_account);
|
|
|
|
|
2022-07-11 14:46:32 -07:00
|
|
|
let old_len = programdata_account.get_data().len();
|
|
|
|
let new_len = old_len.saturating_add(additional_bytes as usize);
|
|
|
|
if new_len > MAX_PERMITTED_DATA_LENGTH as usize {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Extended ProgramData length of {} bytes exceeds max account data length of {} bytes",
|
|
|
|
new_len,
|
|
|
|
MAX_PERMITTED_DATA_LENGTH
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidRealloc);
|
|
|
|
}
|
|
|
|
|
2023-06-01 06:17:42 -07:00
|
|
|
let clock_slot = invoke_context
|
|
|
|
.get_sysvar_cache()
|
|
|
|
.get_clock()
|
|
|
|
.map(|clock| clock.slot)?;
|
|
|
|
|
|
|
|
let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
2022-07-11 14:46:32 -07:00
|
|
|
upgrade_authority_address,
|
|
|
|
} = programdata_account.get_state()?
|
|
|
|
{
|
2023-06-01 06:17:42 -07:00
|
|
|
if clock_slot == slot {
|
|
|
|
ic_logger_msg!(log_collector, "Program was extended in this block already");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2022-07-11 14:46:32 -07:00
|
|
|
if upgrade_authority_address.is_none() {
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Cannot extend ProgramData accounts that are not upgradeable"
|
|
|
|
);
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
2023-06-01 06:17:42 -07:00
|
|
|
upgrade_authority_address
|
2022-07-11 14:46:32 -07:00
|
|
|
} else {
|
|
|
|
ic_logger_msg!(log_collector, "ProgramData state is invalid");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2023-06-01 06:17:42 -07:00
|
|
|
};
|
2022-07-11 14:46:32 -07:00
|
|
|
|
|
|
|
let required_payment = {
|
|
|
|
let balance = programdata_account.get_lamports();
|
|
|
|
let rent = invoke_context.get_sysvar_cache().get_rent()?;
|
|
|
|
let min_balance = rent.minimum_balance(new_len).max(1);
|
|
|
|
min_balance.saturating_sub(balance)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Borrowed accounts need to be dropped before native_invoke
|
|
|
|
drop(programdata_account);
|
|
|
|
|
2023-06-01 06:17:42 -07:00
|
|
|
// Dereference the program ID to prevent overlapping mutable/immutable borrow of invoke context
|
|
|
|
let program_id = *program_id;
|
2022-07-11 14:46:32 -07:00
|
|
|
if required_payment > 0 {
|
|
|
|
let payer_key = *transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(
|
|
|
|
OPTIONAL_PAYER_ACCOUNT_INDEX,
|
|
|
|
)?,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
invoke_context.native_invoke(
|
2023-02-16 05:16:25 -08:00
|
|
|
system_instruction::transfer(&payer_key, &programdata_key, required_payment)
|
|
|
|
.into(),
|
2022-07-11 14:46:32 -07:00
|
|
|
&[],
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
|
|
|
let mut programdata_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?;
|
|
|
|
programdata_account.set_data_length(new_len)?;
|
|
|
|
|
2023-06-01 06:17:42 -07:00
|
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
|
|
|
|
|
|
|
deploy_program!(
|
|
|
|
invoke_context,
|
|
|
|
program_key,
|
|
|
|
&program_id,
|
|
|
|
UpgradeableLoaderState::size_of_program().saturating_add(new_len),
|
|
|
|
clock_slot,
|
|
|
|
{
|
|
|
|
drop(programdata_account);
|
|
|
|
},
|
|
|
|
programdata_account
|
|
|
|
.get_data()
|
|
|
|
.get(programdata_data_offset..)
|
|
|
|
.ok_or(InstructionError::AccountDataTooSmall)?,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut programdata_account = instruction_context
|
|
|
|
.try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?;
|
|
|
|
programdata_account.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: clock_slot,
|
|
|
|
upgrade_authority_address,
|
|
|
|
})?;
|
|
|
|
|
2022-07-11 14:46:32 -07:00
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Extended ProgramData account by {} bytes",
|
|
|
|
additional_bytes
|
|
|
|
);
|
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-08-24 10:05:54 -07:00
|
|
|
fn common_close_account(
|
|
|
|
authority_address: &Option<Pubkey>,
|
2022-03-30 00:17:55 -07:00
|
|
|
transaction_context: &TransactionContext,
|
|
|
|
instruction_context: &InstructionContext,
|
2021-11-23 04:23:40 -08:00
|
|
|
log_collector: &Option<Rc<RefCell<LogCollector>>>,
|
2021-08-24 10:05:54 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
if authority_address.is_none() {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Account is immutable");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
2022-03-30 00:17:55 -07:00
|
|
|
if *authority_address
|
2022-06-16 09:46:17 -07:00
|
|
|
!= Some(*transaction_context.get_key_of_account_at_index(
|
|
|
|
instruction_context.get_index_of_instruction_account_in_transaction(2)?,
|
|
|
|
)?)
|
2022-03-30 00:17:55 -07:00
|
|
|
{
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Incorrect authority provided");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
2022-06-16 09:46:17 -07:00
|
|
|
if !instruction_context.is_instruction_account_signer(2)? {
|
2021-11-23 04:23:40 -08:00
|
|
|
ic_logger_msg!(log_collector, "Authority did not sign");
|
2021-08-24 10:05:54 -07:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2022-03-16 03:30:01 -07:00
|
|
|
|
2022-03-30 00:17:55 -07:00
|
|
|
let mut close_account =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
|
|
|
let mut recipient_account =
|
|
|
|
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
|
|
|
|
recipient_account.checked_add_lamports(close_account.get_lamports())?;
|
2022-05-21 08:47:09 -07:00
|
|
|
close_account.set_lamports(0)?;
|
2021-10-28 09:04:03 -07:00
|
|
|
close_account.set_state(&UpgradeableLoaderState::Uninitialized)?;
|
2021-08-24 10:05:54 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-04-25 10:04:11 -07:00
|
|
|
fn process_loader_instruction(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
|
2022-02-17 01:16:28 -08:00
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
2022-04-01 06:48:05 -07:00
|
|
|
let instruction_data = instruction_context.get_instruction_data();
|
2022-06-16 09:46:17 -07:00
|
|
|
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
2022-10-18 01:22:39 -07:00
|
|
|
let mut program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
if program.get_owner() != program_id {
|
2021-01-21 09:57:59 -08:00
|
|
|
ic_msg!(
|
|
|
|
invoke_context,
|
|
|
|
"Executable account not owned by the BPF loader"
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
2022-04-19 02:35:40 -07:00
|
|
|
let is_program_signer = program.is_signer();
|
2020-12-14 15:35:10 -08:00
|
|
|
match limited_deserialize(instruction_data)? {
|
|
|
|
LoaderInstruction::Write { offset, bytes } => {
|
2022-04-19 02:35:40 -07:00
|
|
|
if !is_program_signer {
|
2021-01-21 09:57:59 -08:00
|
|
|
ic_msg!(invoke_context, "Program account did not sign");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2022-10-18 01:22:39 -07:00
|
|
|
drop(program);
|
2023-03-08 07:54:19 -08:00
|
|
|
write_program_data(offset as usize, &bytes, invoke_context)?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
LoaderInstruction::Finalize => {
|
2022-04-19 02:35:40 -07:00
|
|
|
if !is_program_signer {
|
2022-03-16 03:30:01 -07:00
|
|
|
ic_msg!(invoke_context, "key[0] did not sign the transaction");
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
2023-02-11 02:18:25 -08:00
|
|
|
deploy_program!(
|
|
|
|
invoke_context,
|
|
|
|
*program.get_key(),
|
2023-02-17 03:47:45 -08:00
|
|
|
program.get_owner(),
|
|
|
|
program.get_data().len(),
|
2023-05-24 09:15:47 -07:00
|
|
|
invoke_context.programs_loaded_for_tx_batch.slot(),
|
2023-02-11 02:18:25 -08:00
|
|
|
{},
|
|
|
|
program.get_data(),
|
|
|
|
);
|
2022-05-21 08:47:09 -07:00
|
|
|
program.set_executable(true)?;
|
2022-04-19 02:35:40 -07:00
|
|
|
ic_msg!(invoke_context, "Finalized account {:?}", program.get_key());
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2020-12-07 00:49:55 -08:00
|
|
|
}
|
|
|
|
|
2023-03-10 10:05:29 -08:00
|
|
|
fn execute<'a, 'b: 'a>(
|
2023-05-12 09:07:13 -07:00
|
|
|
executable: &'a Executable<RequisiteVerifier, InvokeContext<'static>>,
|
2023-03-10 10:05:29 -08:00
|
|
|
invoke_context: &'a mut InvokeContext<'b>,
|
2023-04-05 06:50:34 -07:00
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
2023-07-05 10:46:21 -07:00
|
|
|
// We dropped the lifetime tracking in the Executor by setting it to 'static,
|
|
|
|
// thus we need to reintroduce the correct lifetime of InvokeContext here again.
|
|
|
|
let executable = unsafe {
|
|
|
|
mem::transmute::<_, &'a Executable<RequisiteVerifier, InvokeContext<'b>>>(executable)
|
|
|
|
};
|
2023-02-17 03:47:45 -08:00
|
|
|
let log_collector = invoke_context.get_log_collector();
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
let (program_id, is_loader_deprecated) = {
|
|
|
|
let program_account =
|
|
|
|
instruction_context.try_borrow_last_program_account(transaction_context)?;
|
|
|
|
(
|
|
|
|
*program_account.get_key(),
|
|
|
|
*program_account.get_owner() == bpf_loader_deprecated::id(),
|
|
|
|
)
|
|
|
|
};
|
2023-02-17 03:47:45 -08:00
|
|
|
#[cfg(any(target_os = "windows", not(target_arch = "x86_64")))]
|
|
|
|
let use_jit = false;
|
|
|
|
#[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
|
2023-05-12 09:07:13 -07:00
|
|
|
let use_jit = executable.get_compiled_program().is_some();
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
let direct_mapping = invoke_context
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
.feature_set
|
|
|
|
.is_active(&bpf_account_data_direct_mapping::id());
|
2023-02-17 03:47:45 -08:00
|
|
|
|
|
|
|
let mut serialize_time = Measure::start("serialize");
|
2023-07-28 04:34:27 -07:00
|
|
|
let (parameter_bytes, regions, accounts_metadata) = serialization::serialize_parameters(
|
2023-02-17 03:47:45 -08:00
|
|
|
invoke_context.transaction_context,
|
|
|
|
instruction_context,
|
|
|
|
invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&cap_bpf_program_instruction_accounts::ID),
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
!direct_mapping,
|
2023-02-17 03:47:45 -08:00
|
|
|
)?;
|
|
|
|
serialize_time.stop();
|
|
|
|
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
// save the account addresses so in case we hit an AccessViolation error we
|
|
|
|
// can map to a more specific error
|
|
|
|
let account_region_addrs = accounts_metadata
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
.iter()
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
.map(|m| {
|
|
|
|
let vm_end = m
|
|
|
|
.vm_data_addr
|
|
|
|
.saturating_add(m.original_data_len as u64)
|
|
|
|
.saturating_add(if !is_loader_deprecated {
|
|
|
|
MAX_PERMITTED_DATA_INCREASE as u64
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
});
|
|
|
|
m.vm_data_addr..vm_end
|
|
|
|
})
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2023-02-17 03:47:45 -08:00
|
|
|
let mut create_vm_time = Measure::start("create_vm");
|
|
|
|
let mut execute_time;
|
|
|
|
let execution_result = {
|
|
|
|
let compute_meter_prev = invoke_context.get_remaining();
|
2023-07-28 04:34:27 -07:00
|
|
|
create_vm!(vm, executable, regions, accounts_metadata, invoke_context,);
|
2023-03-10 10:05:29 -08:00
|
|
|
let mut vm = match vm {
|
2023-02-17 03:47:45 -08:00
|
|
|
Ok(info) => info,
|
|
|
|
Err(e) => {
|
|
|
|
ic_logger_msg!(log_collector, "Failed to create SBF VM: {}", e);
|
2023-04-05 06:50:34 -07:00
|
|
|
return Err(Box::new(InstructionError::ProgramEnvironmentSetupFailure));
|
2021-09-01 02:14:01 -07:00
|
|
|
}
|
2023-02-17 03:47:45 -08:00
|
|
|
};
|
|
|
|
create_vm_time.stop();
|
|
|
|
|
|
|
|
execute_time = Measure::start("execute");
|
2023-07-05 10:46:21 -07:00
|
|
|
let (compute_units_consumed, result) = vm.execute_program(executable, !use_jit);
|
2023-02-17 03:47:45 -08:00
|
|
|
drop(vm);
|
|
|
|
ic_logger_msg!(
|
|
|
|
log_collector,
|
|
|
|
"Program {} consumed {} of {} compute units",
|
|
|
|
&program_id,
|
|
|
|
compute_units_consumed,
|
|
|
|
compute_meter_prev
|
|
|
|
);
|
|
|
|
let (_returned_from_program_id, return_data) =
|
|
|
|
invoke_context.transaction_context.get_return_data();
|
|
|
|
if !return_data.is_empty() {
|
|
|
|
stable_log::program_return(&log_collector, &program_id, return_data);
|
|
|
|
}
|
|
|
|
match result {
|
|
|
|
ProgramResult::Ok(status) if status != SUCCESS => {
|
|
|
|
let error: InstructionError = if (status == MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED
|
|
|
|
&& !invoke_context
|
|
|
|
.feature_set
|
|
|
|
.is_active(&cap_accounts_data_allocations_per_transaction::id()))
|
|
|
|
|| (status == MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
|
2021-12-28 03:14:48 -08:00
|
|
|
&& !invoke_context
|
|
|
|
.feature_set
|
2023-02-17 03:47:45 -08:00
|
|
|
.is_active(&limit_max_instruction_trace_length::id()))
|
|
|
|
{
|
|
|
|
// Until the cap_accounts_data_allocations_per_transaction feature is
|
|
|
|
// enabled, map the `MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED` error to `InvalidError`.
|
|
|
|
// Until the limit_max_instruction_trace_length feature is
|
|
|
|
// enabled, map the `MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED` error to `InvalidError`.
|
|
|
|
InstructionError::InvalidError
|
|
|
|
} else {
|
|
|
|
status.into()
|
|
|
|
};
|
2023-04-05 06:50:34 -07:00
|
|
|
Err(Box::new(error) as Box<dyn std::error::Error>)
|
2020-01-10 13:20:15 -08:00
|
|
|
}
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
ProgramResult::Err(mut error) => {
|
|
|
|
if direct_mapping {
|
|
|
|
if let Some(EbpfError::AccessViolation(
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
_pc,
|
|
|
|
AccessType::Store,
|
|
|
|
address,
|
|
|
|
_size,
|
|
|
|
_section_name,
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
)) = error.downcast_ref()
|
|
|
|
{
|
|
|
|
// If direct_mapping is enabled and a program tries to write to a readonly
|
|
|
|
// region we'll get a memory access violation. Map it to a more specific
|
|
|
|
// error so it's easier for developers to see what happened.
|
|
|
|
if let Some((instruction_account_index, _)) = account_region_addrs
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.find(|(_, vm_region)| vm_region.contains(address))
|
|
|
|
{
|
|
|
|
let transaction_context = &invoke_context.transaction_context;
|
|
|
|
let instruction_context =
|
|
|
|
transaction_context.get_current_instruction_context()?;
|
|
|
|
|
|
|
|
let account = instruction_context.try_borrow_instruction_account(
|
|
|
|
transaction_context,
|
|
|
|
instruction_account_index as IndexOfAccount,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
error = Box::new(if account.is_executable() {
|
|
|
|
InstructionError::ExecutableDataModified
|
|
|
|
} else if account.is_writable() {
|
|
|
|
InstructionError::ExternalAccountDataModified
|
|
|
|
} else {
|
|
|
|
InstructionError::ReadonlyDataModified
|
|
|
|
})
|
|
|
|
}
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
}
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
}
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
Err(error)
|
|
|
|
}
|
2023-02-17 03:47:45 -08:00
|
|
|
_ => Ok(()),
|
2021-12-22 12:07:07 -08:00
|
|
|
}
|
2023-02-17 03:47:45 -08:00
|
|
|
};
|
|
|
|
execute_time.stop();
|
|
|
|
|
2023-04-05 06:50:34 -07:00
|
|
|
fn deserialize_parameters(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
parameter_bytes: &[u8],
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
copy_account_data: bool,
|
2023-04-05 06:50:34 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
serialization::deserialize_parameters(
|
2023-02-17 03:47:45 -08:00
|
|
|
invoke_context.transaction_context,
|
|
|
|
invoke_context
|
|
|
|
.transaction_context
|
|
|
|
.get_current_instruction_context()?,
|
Account data direct mapping (#28053)
* AccountSharedData: make data_mut() private
This ensures that the inner Vec is never handed out. This is in
preparation of enforcing that the capacity of the inner vec never
shrinks, which is required for direct mapping.
* Adds the feature bpf_account_data_direct_mapping.
* Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified.
* WIP: Memory regions for each instruction account in create_vm().
* Fix serialization benches, run both copy and !copy variants
* rbpf-cli: fix build
* BorrowedAccount: ensure that account capacity is never reduced
Accounts can be directly mapped in address space. Their capacity can't
be reduced mid transaction as that would create holes in vm address
space that point to invalid host memory.
* bpf_load: run serialization tests for both copy and !copy account data
* bpf_loader: add Serializer::write_account
* fix lints
* BorrowedAccount: make_data_mut is host only
* Fix unused import warning
* Fix lints
* cpi: add explicit direct_mapping arg to update_(callee|caller)_account
* cpi: rename account_data_or_only_realloc_padding to serialized_data
* cpi: add CallerAccount::original_data_len comment
* cpi: add update_callee_account direct_mapping test
* cpi: add test_update_caller_account_data_direct_mapping and fix bug
We used to have a bug in zeroing data when shrinking account, where we zeroed
the spare account capacity but not the realloc padding.
* cpi: add tests for mutated readonly accounts
* cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on
* cpi: update_caller_account: ensure that account capacity is always enough
Introduce a better way to ensure that account capacity never goes below what
might be mapped in memory regions.
* cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut()
Before we were using BorrowedAccount::get_data_mut() to get the base pointer to
the account data, then we were slicing the spare capacity from it. Calling
get_data_mut() doesn't work if an account has been closed tho, since the
current program doesn't own the account anymore and therefore get_data_mut()
errors out.
* bpf_loader: fix same lint for the umpteenth time
* bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations
* programs/sbf: realloc: add test for large write after realloc
Add a test that after a realloc does a large write that spans the
original account length and the realloc area. This ensures that memory
mapping works correctly across the boundary.
* programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off
By default test banks test with all features on. This ensures we keep
testing the existing code until the new feature is enabled.
* bpf_loader: tweak memcmp syscall
Split the actual memcmp code in a separate function. Remove check
indexing the slices since the slices are guaranteed to have the correct
length by construction.
* bpf_loader: tweak the memset syscall
Use slice::fill, which is effectively memset.
* bpf_loader: syscalls: update mem syscalls to work with non contiguous memory
With direct mapping enabled, accounts can now span multiple memory
regions.
* fix lint, rebase mem_ops
* Implement CoW for writable accounts
* Fix CI
* Move CoW to the MemoryMapping level
* Update after rbpf API change
* Fix merge screwup
* Add create_vm macro. Fix benches.
* cpi: simplify update_caller_account
Simplify the logic to update a caller's memory region when a callee
causes an account data pointer to change (eg during CoW)
* benches/bpf_loader: move serialization out of create_vm bench
* benches/bpf_loader: don't copy accounts when direct mapping is on
* Fix review nits
* bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new
When starting at u64::MAX, the chunk iterator would always return the
empty sequence (None on the first next()) call, instead of returning a
memory access violation.
Use checked instead of saturating arithmetic to detect the condition and
error out.
This commit also adds more tests around boundary conditions.
* Fix loader-v3 tests: data_mut => data_as_mut_slice
* Fix CI
* bpf_loader: fix tuner bench: account must be writable
With direct mapping on, invalid writes are caught early meaning the
tuner would fail on the first store and not consume the whole budget
like the benchmark expects.
---------
Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
|
|
|
copy_account_data,
|
2023-04-05 06:50:34 -07:00
|
|
|
parameter_bytes,
|
2023-07-28 04:34:27 -07:00
|
|
|
&invoke_context.get_syscall_context()?.accounts_metadata,
|
2023-02-17 03:47:45 -08:00
|
|
|
)
|
2023-04-05 06:50:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut deserialize_time = Measure::start("deserialize");
|
|
|
|
let execute_or_deserialize_result = execution_result.and_then(|_| {
|
direct mapping: misc fixes (#32649)
* transaction_context: update make_data_mut comment
* bpf_loader: cpi: pass SerializeAccountMetadata to CallerAccount::from*
We now have a way to provide CallerAccount with trusted values coming
from our internal serialization code and not from untrusted vm space
* bpf_loader: direct_mapping: enforce account info pointers to be immutable
When direct mapping is enabled, we might need to update account data
memory regions across CPI calls. Since the only way we have to retrieve
the regions is based on their vm addresses, we enforce vm addresses to
be stable. Accounts can still be mutated and resized of course, but it
must be done in place.
This also locks all other AccountInfo pointers, since there's no legitimate
reason to make them point to anything else.
* bpf_loader: cpi: access ref_to_len_in_vm through VmValue
Direct mapping needs to translate vm values at each access since
permissions of the underlying memory might have changed.
* direct mapping: improve memory permission tracking across CPI calls
Ensure that the data and realloc regions of an account always track the
account's permissions. In order to do this, we also need to split
realloc regions in their own self contained regions, where before we
had:
[account fields][account data][account realloc + more account fields + next account fields][next account data][...]
we now have:
[account fields][account data][account realloc][more account fields + next account fields][next account data][...]
Tested in TEST_[FORBID|ALLOW]_WRITE_AFTER_OWNERSHIP_CHANGE*
Additionally when direct mapping is on, we must update all perms at once before
doing account data updates. Otherwise, updating an account might write into
another account whose perms we haven't updated yet. Tested in
TEST_FORBID_LEN_UPDATE_AFTER_OWNERSHIP_CHANGE.
* bpf_loader: serialization: address review comment don't return vm_addr from push_account_region
* bpf_loader: rename push_account_region to push_account_data_region
* cpi: fix slow edge case zeroing extra account capacity after shrinking an account
When returning from CPI we need to zero all the account memory up to the
original length only if we know we're potentially dealing with uninitialized
memory.
When we know that the spare capacity has deterministic content, we only need to
zero new_len..prev_len.
This fixes a slow edge case that was triggerable by the following scenario:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - would memset 10..10MB
- shrink to 9 bytes - would memset 9..10MB
- shrink to 8 bytes - would memset 8..10MB
- ...
Now instead in the scenario above the following will happen:
- load a large account (say 10MB) into the vm
- shrink to 10 bytes - memsets 10..10MB
- shrink to 9 bytes - memsets 9..10
- shrink to 8 bytes - memset 8..9
- ...
* bpf_loader: add account_data_region_memory_state helper
Shared between serialization and CPI to figure out the MemoryState of an
account.
* cpi: direct_mapping: error out if ref_to_len_in_vm points to account memory
If ref_to_len_in_vm is allowed to be in account memory, calles could mutate it,
essentially letting callees directly mutate callers memory.
* bpf_loader: direct_mapping: map AccessViolation -> InstructionError
Return the proper ReadonlyDataModified / ExecutableDataModified /
ExternalAccountDataModified depending on where the violation occurs
* bpf_loader: cpi: remove unnecessary infallible slice::get call
2023-08-30 02:57:24 -07:00
|
|
|
deserialize_parameters(invoke_context, parameter_bytes.as_slice(), !direct_mapping)
|
|
|
|
.map_err(|error| Box::new(error) as Box<dyn std::error::Error>)
|
2023-02-17 03:47:45 -08:00
|
|
|
});
|
|
|
|
deserialize_time.stop();
|
|
|
|
|
|
|
|
// Update the timings
|
|
|
|
let timings = &mut invoke_context.timings;
|
|
|
|
timings.serialize_us = timings.serialize_us.saturating_add(serialize_time.as_us());
|
|
|
|
timings.create_vm_us = timings.create_vm_us.saturating_add(create_vm_time.as_us());
|
|
|
|
timings.execute_us = timings.execute_us.saturating_add(execute_time.as_us());
|
|
|
|
timings.deserialize_us = timings
|
|
|
|
.deserialize_us
|
|
|
|
.saturating_add(deserialize_time.as_us());
|
|
|
|
|
|
|
|
execute_or_deserialize_result
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
|
2023-05-02 14:39:27 -07:00
|
|
|
pub mod test_utils {
|
2023-05-09 15:44:38 -07:00
|
|
|
use {
|
|
|
|
super::*, solana_program_runtime::loaded_programs::DELAY_VISIBILITY_SLOT_OFFSET,
|
|
|
|
solana_sdk::account::ReadableAccount,
|
|
|
|
};
|
2023-05-02 14:39:27 -07:00
|
|
|
|
|
|
|
pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
|
2023-05-20 11:28:00 -07:00
|
|
|
let mut load_program_metrics = LoadProgramMetrics::default();
|
2023-08-11 11:58:22 -07:00
|
|
|
let program_runtime_environment = create_program_runtime_environment_v1(
|
2023-05-20 11:28:00 -07:00
|
|
|
&invoke_context.feature_set,
|
|
|
|
invoke_context.get_compute_budget(),
|
|
|
|
false, /* deployment */
|
|
|
|
false, /* debugging_features */
|
|
|
|
);
|
|
|
|
let program_runtime_environment = Arc::new(program_runtime_environment.unwrap());
|
2023-05-02 14:39:27 -07:00
|
|
|
let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
|
|
|
|
for index in 0..num_accounts {
|
|
|
|
let account = invoke_context
|
|
|
|
.transaction_context
|
|
|
|
.get_account_at_index(index)
|
|
|
|
.expect("Failed to get the account")
|
|
|
|
.borrow();
|
|
|
|
|
|
|
|
let owner = account.owner();
|
|
|
|
if check_loader_id(owner) {
|
|
|
|
let pubkey = invoke_context
|
|
|
|
.transaction_context
|
|
|
|
.get_key_of_account_at_index(index)
|
|
|
|
.expect("Failed to get account key");
|
|
|
|
|
|
|
|
if let Ok(loaded_program) = load_program_from_bytes(
|
|
|
|
&FeatureSet::all_enabled(),
|
|
|
|
None,
|
|
|
|
&mut load_program_metrics,
|
|
|
|
account.data(),
|
|
|
|
owner,
|
|
|
|
account.data().len(),
|
|
|
|
0,
|
2023-05-20 11:28:00 -07:00
|
|
|
program_runtime_environment.clone(),
|
2023-05-02 14:39:27 -07:00
|
|
|
) {
|
2023-05-17 06:10:50 -07:00
|
|
|
invoke_context
|
|
|
|
.programs_modified_by_tx
|
|
|
|
.set_slot_for_tests(DELAY_VISIBILITY_SLOT_OFFSET);
|
|
|
|
invoke_context
|
|
|
|
.programs_modified_by_tx
|
|
|
|
.replenish(*pubkey, Arc::new(loaded_program));
|
2023-05-02 14:39:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 10:59:56 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
super::*,
|
2023-08-30 10:48:27 -07:00
|
|
|
assert_matches::assert_matches,
|
2021-12-03 09:00:31 -08:00
|
|
|
rand::Rng,
|
2023-04-11 09:43:42 -07:00
|
|
|
solana_program_runtime::{
|
|
|
|
invoke_context::mock_process_instruction, with_mock_invoke_context,
|
|
|
|
},
|
2022-11-15 06:21:11 -08:00
|
|
|
solana_rbpf::{
|
2023-07-05 10:46:21 -07:00
|
|
|
elf::SBPFVersion,
|
2023-05-12 09:07:13 -07:00
|
|
|
verifier::Verifier,
|
2023-04-14 06:20:08 -07:00
|
|
|
vm::{Config, ContextObject, FunctionRegistry},
|
2022-11-15 06:21:11 -08:00
|
|
|
},
|
2021-12-03 09:00:31 -08:00
|
|
|
solana_sdk::{
|
|
|
|
account::{
|
|
|
|
create_account_shared_data_for_test as create_account_for_test, AccountSharedData,
|
2022-04-19 02:35:40 -07:00
|
|
|
ReadableAccount, WritableAccount,
|
2021-12-03 09:00:31 -08:00
|
|
|
},
|
|
|
|
account_utils::StateMut,
|
|
|
|
clock::Clock,
|
2022-10-19 13:28:57 -07:00
|
|
|
instruction::{AccountMeta, InstructionError},
|
2021-12-03 09:00:31 -08:00
|
|
|
pubkey::Pubkey,
|
|
|
|
rent::Rent,
|
2022-12-13 13:54:24 -08:00
|
|
|
system_program, sysvar,
|
2021-03-24 23:23:20 -07:00
|
|
|
},
|
2023-04-11 09:43:42 -07:00
|
|
|
std::{fs::File, io::Read, ops::Range, sync::atomic::AtomicU64},
|
2020-04-28 14:33:56 -07:00
|
|
|
};
|
|
|
|
|
2022-11-15 06:21:11 -08:00
|
|
|
struct TestContextObject {
|
2020-08-21 15:31:19 -07:00
|
|
|
remaining: u64,
|
2020-06-13 13:20:08 -07:00
|
|
|
}
|
2022-11-15 06:21:11 -08:00
|
|
|
impl ContextObject for TestContextObject {
|
|
|
|
fn trace(&mut self, _state: [u64; 12]) {}
|
2020-08-21 15:31:19 -07:00
|
|
|
fn consume(&mut self, amount: u64) {
|
|
|
|
self.remaining = self.remaining.saturating_sub(amount);
|
2020-06-06 10:18:28 -07:00
|
|
|
}
|
2020-08-21 15:31:19 -07:00
|
|
|
fn get_remaining(&self) -> u64 {
|
|
|
|
self.remaining
|
2020-06-06 10:18:28 -07:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
2018-11-06 14:28:46 -08:00
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
fn process_instruction(
|
2021-11-04 13:47:32 -07:00
|
|
|
loader_id: &Pubkey,
|
2022-09-06 02:31:40 -07:00
|
|
|
program_indices: &[IndexOfAccount],
|
2021-10-08 02:41:07 -07:00
|
|
|
instruction_data: &[u8],
|
2021-12-17 05:01:12 -08:00
|
|
|
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
|
|
|
|
instruction_accounts: Vec<AccountMeta>,
|
|
|
|
expected_result: Result<(), InstructionError>,
|
|
|
|
) -> Vec<AccountSharedData> {
|
2021-11-04 13:47:32 -07:00
|
|
|
mock_process_instruction(
|
|
|
|
loader_id,
|
|
|
|
program_indices.to_vec(),
|
2021-10-08 02:41:07 -07:00
|
|
|
instruction_data,
|
2021-12-17 05:01:12 -08:00
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
|
|
|
expected_result,
|
2021-11-04 13:47:32 -07:00
|
|
|
super::process_instruction,
|
2023-05-02 14:39:27 -07:00
|
|
|
|invoke_context| {
|
|
|
|
test_utils::load_all_invoked_programs(invoke_context);
|
|
|
|
},
|
2023-04-07 03:53:19 -07:00
|
|
|
|_invoke_context| {},
|
2021-10-08 02:41:07 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-12-17 05:01:12 -08:00
|
|
|
fn load_program_account_from_elf(loader_id: &Pubkey, path: &str) -> AccountSharedData {
|
2021-10-08 02:41:07 -07:00
|
|
|
let mut file = File::open(path).expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
let rent = Rent::default();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account =
|
|
|
|
AccountSharedData::new(rent.minimum_balance(elf.len()), 0, loader_id);
|
|
|
|
program_account.set_data(elf);
|
|
|
|
program_account.set_executable(true);
|
2021-10-08 02:41:07 -07:00
|
|
|
program_account
|
|
|
|
}
|
|
|
|
|
2020-11-12 13:13:42 -08:00
|
|
|
#[test]
|
2021-05-28 14:24:02 -07:00
|
|
|
#[should_panic(expected = "LDDWCannotBeLast")]
|
2020-11-12 13:13:42 -08:00
|
|
|
fn test_bpf_loader_check_load_dw() {
|
|
|
|
let prog = &[
|
|
|
|
0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, // first half of lddw
|
|
|
|
];
|
2023-07-05 10:46:21 -07:00
|
|
|
RequisiteVerifier::verify(
|
|
|
|
prog,
|
|
|
|
&Config::default(),
|
|
|
|
&SBPFVersion::V2,
|
|
|
|
&FunctionRegistry::default(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-11-12 13:13:42 -08:00
|
|
|
}
|
|
|
|
|
2019-11-08 09:19:19 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_write() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader::id();
|
2021-11-04 13:47:32 -07:00
|
|
|
let program_id = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account = AccountSharedData::new(1, 0, &loader_id);
|
2020-01-10 13:20:15 -08:00
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Write {
|
2019-11-08 09:19:19 -08:00
|
|
|
offset: 3,
|
|
|
|
bytes: vec![1, 2, 3],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: No program account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
Vec::new(),
|
|
|
|
Vec::new(),
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write bytes to an offset
|
2021-12-17 05:01:12 -08:00
|
|
|
program_account.set_data(vec![0; 6]);
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: true,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-11-08 09:19:19 -08:00
|
|
|
Ok(()),
|
2020-01-22 09:11:56 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(&vec![0, 0, 0, 1, 2, 3], accounts.first().unwrap().data());
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Overflow
|
2021-12-17 05:01:12 -08:00
|
|
|
program_account.set_data(vec![0; 5]);
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account)],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: true,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_finalize() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader::id();
|
2021-11-04 13:47:32 -07:00
|
|
|
let program_id = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account =
|
2022-05-03 17:10:28 -07:00
|
|
|
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so");
|
2021-12-17 05:01:12 -08:00
|
|
|
program_account.set_executable(false);
|
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: No program account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
Vec::new(),
|
|
|
|
Vec::new(),
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Finalize
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: true,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-11-08 09:19:19 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert!(accounts.first().unwrap().executable());
|
2020-01-22 09:11:56 -08:00
|
|
|
|
2021-12-17 05:01:12 -08:00
|
|
|
// Case: Finalize bad ELF
|
2022-03-10 11:48:33 -08:00
|
|
|
*program_account.data_as_mut_slice().get_mut(0).unwrap() = 0;
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![(program_id, program_account)],
|
|
|
|
vec![AccountMeta {
|
|
|
|
pubkey: program_id,
|
|
|
|
is_signer: true,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
}],
|
2019-12-04 12:03:29 -08:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_invoke_main() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader::id();
|
2021-11-04 13:47:32 -07:00
|
|
|
let program_id = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account =
|
2022-05-03 17:10:28 -07:00
|
|
|
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so");
|
2021-12-17 05:01:12 -08:00
|
|
|
let parameter_id = Pubkey::new_unique();
|
|
|
|
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
|
|
|
|
let parameter_meta = AccountMeta {
|
|
|
|
pubkey: parameter_id,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: No program account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&[],
|
|
|
|
Vec::new(),
|
|
|
|
Vec::new(),
|
2019-11-08 09:19:19 -08:00
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Only a program account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
Vec::new(),
|
2019-11-08 09:19:19 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With program and parameter account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account.clone()),
|
|
|
|
(parameter_id, parameter_account.clone()),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone()],
|
2019-11-08 09:19:19 -08:00
|
|
|
Ok(()),
|
2020-08-26 14:48:51 -07:00
|
|
|
);
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
// Case: With duplicate accounts
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account.clone()),
|
|
|
|
(parameter_id, parameter_account),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone(), parameter_meta],
|
2020-01-22 09:11:56 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2021-04-19 09:48:48 -07:00
|
|
|
|
|
|
|
// Case: limited budget
|
2021-12-17 05:01:12 -08:00
|
|
|
mock_process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
vec![0],
|
|
|
|
&[],
|
|
|
|
vec![(program_id, program_account.clone())],
|
|
|
|
Vec::new(),
|
2021-04-19 09:48:48 -07:00
|
|
|
Err(InstructionError::ProgramFailedToComplete),
|
2023-04-03 08:23:24 -07:00
|
|
|
super::process_instruction,
|
|
|
|
|invoke_context| {
|
2022-11-15 06:21:11 -08:00
|
|
|
invoke_context.mock_set_remaining(0);
|
2023-05-02 14:39:27 -07:00
|
|
|
test_utils::load_all_invoked_programs(invoke_context);
|
2021-12-17 05:01:12 -08:00
|
|
|
},
|
2023-04-07 03:53:19 -07:00
|
|
|
|_invoke_context| {},
|
2021-12-17 05:01:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Account not a program
|
|
|
|
program_account.set_executable(false);
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![(program_id, program_account)],
|
|
|
|
Vec::new(),
|
|
|
|
Err(InstructionError::IncorrectProgramId),
|
2021-04-19 09:48:48 -07:00
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
2020-05-08 12:37:04 -07:00
|
|
|
|
2020-08-11 16:11:52 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_serialize_unaligned() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_deprecated::id();
|
2021-12-17 05:01:12 -08:00
|
|
|
let program_id = Pubkey::new_unique();
|
2021-10-08 02:41:07 -07:00
|
|
|
let program_account =
|
2022-05-03 17:10:28 -07:00
|
|
|
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_unaligned.so");
|
2021-12-17 05:01:12 -08:00
|
|
|
let parameter_id = Pubkey::new_unique();
|
|
|
|
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
|
|
|
|
let parameter_meta = AccountMeta {
|
|
|
|
pubkey: parameter_id,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
2020-08-11 16:11:52 -07:00
|
|
|
|
|
|
|
// Case: With program and parameter account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account.clone()),
|
|
|
|
(parameter_id, parameter_account.clone()),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone()],
|
2020-08-11 16:11:52 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With duplicate accounts
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account),
|
|
|
|
(parameter_id, parameter_account),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone(), parameter_meta],
|
2020-08-11 16:11:52 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_serialize_aligned() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader::id();
|
2021-12-17 05:01:12 -08:00
|
|
|
let program_id = Pubkey::new_unique();
|
2021-10-08 02:41:07 -07:00
|
|
|
let program_account =
|
2022-05-03 17:10:28 -07:00
|
|
|
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so");
|
2021-12-17 05:01:12 -08:00
|
|
|
let parameter_id = Pubkey::new_unique();
|
|
|
|
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
|
|
|
|
let parameter_meta = AccountMeta {
|
|
|
|
pubkey: parameter_id,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
2020-08-11 16:11:52 -07:00
|
|
|
|
|
|
|
// Case: With program and parameter account
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account.clone()),
|
|
|
|
(parameter_id, parameter_account.clone()),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone()],
|
2020-08-11 16:11:52 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With duplicate accounts
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[0],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(program_id, program_account),
|
|
|
|
(parameter_id, parameter_account),
|
|
|
|
],
|
|
|
|
vec![parameter_meta.clone(), parameter_meta],
|
2020-08-11 16:11:52 -07:00
|
|
|
Ok(()),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_initialize_buffer() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
2020-12-14 15:35:10 -08:00
|
|
|
let buffer_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let buffer_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
|
2021-01-29 12:43:42 -08:00
|
|
|
let authority_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let authority_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
|
2021-12-17 05:01:12 -08:00
|
|
|
let instruction_data =
|
|
|
|
bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap();
|
|
|
|
let instruction_accounts = vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
];
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Case: Success
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![
|
|
|
|
(buffer_address, buffer_account),
|
|
|
|
(authority_address, authority_account),
|
|
|
|
],
|
|
|
|
instruction_accounts.clone(),
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
2021-01-29 12:43:42 -08:00
|
|
|
authority_address: Some(authority_address)
|
2021-01-08 09:37:57 -08:00
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Case: Already initialized
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction_data,
|
|
|
|
vec![
|
2022-03-10 11:48:33 -08:00
|
|
|
(buffer_address, accounts.first().unwrap().clone()),
|
|
|
|
(authority_address, accounts.get(1).unwrap().clone()),
|
2021-12-17 05:01:12 -08:00
|
|
|
],
|
|
|
|
instruction_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::AccountAlreadyInitialized),
|
2021-01-08 09:37:57 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_write() {
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
2020-12-14 15:35:10 -08:00
|
|
|
let buffer_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let mut buffer_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
|
2021-12-17 05:01:12 -08:00
|
|
|
let instruction_accounts = vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
];
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Case: Not initialized
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts.clone(),
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write entire buffer
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts.clone(),
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
2022-03-10 11:48:33 -08:00
|
|
|
&accounts
|
|
|
|
.first()
|
|
|
|
.unwrap()
|
|
|
|
.data()
|
2022-05-11 07:22:59 -07:00
|
|
|
.get(UpgradeableLoaderState::size_of_buffer_metadata()..)
|
2022-03-10 11:48:33 -08:00
|
|
|
.unwrap(),
|
2020-12-14 15:35:10 -08:00
|
|
|
&[42; 9]
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write portion of the buffer
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 3,
|
|
|
|
bytes: vec![42; 6],
|
|
|
|
})
|
|
|
|
.unwrap();
|
2022-05-11 07:22:59 -07:00
|
|
|
let mut buffer_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
|
2020-12-14 15:35:10 -08:00
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts.clone(),
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
2022-03-10 11:48:33 -08:00
|
|
|
&accounts
|
|
|
|
.first()
|
|
|
|
.unwrap()
|
|
|
|
.data()
|
2022-05-11 07:22:59 -07:00
|
|
|
.get(UpgradeableLoaderState::size_of_buffer_metadata()..)
|
2022-03-10 11:48:33 -08:00
|
|
|
.unwrap(),
|
2020-12-14 15:35:10 -08:00
|
|
|
&[0, 0, 0, 42, 42, 42, 42, 42, 42]
|
|
|
|
);
|
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: overflow size
|
2020-12-14 15:35:10 -08:00
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
2021-10-08 02:41:07 -07:00
|
|
|
bytes: vec![42; 10],
|
2020-12-14 15:35:10 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts.clone(),
|
2021-10-08 02:41:07 -07:00
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: overflow offset
|
2020-12-14 15:35:10 -08:00
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
2021-10-08 02:41:07 -07:00
|
|
|
offset: 1,
|
|
|
|
bytes: vec![42; 9],
|
2020-12-14 15:35:10 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts.clone(),
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
|
|
|
);
|
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: Not signed
|
2020-12-14 15:35:10 -08:00
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
2021-10-08 02:41:07 -07:00
|
|
|
offset: 0,
|
2020-12-14 15:35:10 -08:00
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
2021-10-08 02:41:07 -07:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2021-01-08 09:37:57 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 1,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(authority_address, buffer_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
2021-01-29 12:43:42 -08:00
|
|
|
|
|
|
|
// Case: None authority
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 1,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![(buffer_address, buffer_account.clone())],
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
2021-03-17 13:09:26 -07:00
|
|
|
fn truncate_data(account: &mut AccountSharedData, len: usize) {
|
2021-03-25 09:04:20 -07:00
|
|
|
let mut data = account.data().to_vec();
|
2021-03-23 13:19:31 -07:00
|
|
|
data.truncate(len);
|
|
|
|
account.set_data(data);
|
2021-03-17 13:09:26 -07:00
|
|
|
}
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_upgrade() {
|
2022-05-03 17:10:28 -07:00
|
|
|
let mut file = File::open("test_elfs/out/noop_aligned.so").expect("file open failed");
|
2020-12-14 15:35:10 -08:00
|
|
|
let mut elf_orig = Vec::new();
|
|
|
|
file.read_to_end(&mut elf_orig).unwrap();
|
2022-05-03 17:10:28 -07:00
|
|
|
let mut file = File::open("test_elfs/out/noop_unaligned.so").expect("file open failed");
|
2020-12-14 15:35:10 -08:00
|
|
|
let mut elf_new = Vec::new();
|
|
|
|
file.read_to_end(&mut elf_new).unwrap();
|
|
|
|
assert_ne!(elf_orig.len(), elf_new.len());
|
2022-01-31 08:53:50 -08:00
|
|
|
const SLOT: u64 = 42;
|
2020-12-14 15:35:10 -08:00
|
|
|
let buffer_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
fn get_accounts(
|
2021-12-17 05:01:12 -08:00
|
|
|
buffer_address: &Pubkey,
|
2021-01-08 09:37:57 -08:00
|
|
|
buffer_authority: &Pubkey,
|
2020-12-14 15:35:10 -08:00
|
|
|
upgrade_authority_address: &Pubkey,
|
|
|
|
elf_orig: &[u8],
|
|
|
|
elf_new: &[u8],
|
2021-12-17 05:01:12 -08:00
|
|
|
) -> (Vec<(Pubkey, AccountSharedData)>, Vec<AccountMeta>) {
|
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
|
|
let spill_address = Pubkey::new_unique();
|
|
|
|
let rent = Rent::default();
|
|
|
|
let min_program_balance =
|
2022-05-11 07:22:59 -07:00
|
|
|
1.max(rent.minimum_balance(UpgradeableLoaderState::size_of_program()));
|
2021-12-17 05:01:12 -08:00
|
|
|
let min_programdata_balance = 1.max(rent.minimum_balance(
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
|
2021-12-17 05:01:12 -08:00
|
|
|
));
|
|
|
|
let (programdata_address, _) =
|
|
|
|
Pubkey::find_program_address(&[program_address.as_ref()], &loader_id);
|
|
|
|
let mut buffer_account = AccountSharedData::new(
|
2020-12-14 15:35:10 -08:00
|
|
|
1,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_buffer(elf_new.len()),
|
2020-12-14 15:35:10 -08:00
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(*buffer_authority),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2022-03-10 11:48:33 -08:00
|
|
|
buffer_account
|
|
|
|
.data_as_mut_slice()
|
2022-05-11 07:22:59 -07:00
|
|
|
.get_mut(UpgradeableLoaderState::size_of_buffer_metadata()..)
|
2022-03-10 11:48:33 -08:00
|
|
|
.unwrap()
|
2021-06-18 06:34:46 -07:00
|
|
|
.copy_from_slice(elf_new);
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut programdata_account = AccountSharedData::new(
|
2020-12-14 15:35:10 -08:00
|
|
|
min_programdata_balance,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
|
2020-12-14 15:35:10 -08:00
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
2022-01-31 08:53:50 -08:00
|
|
|
slot: SLOT,
|
2020-12-14 15:35:10 -08:00
|
|
|
upgrade_authority_address: Some(*upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account = AccountSharedData::new(
|
2020-12-14 15:35:10 -08:00
|
|
|
min_program_balance,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_program(),
|
2020-12-14 15:35:10 -08:00
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
program_account.set_executable(true);
|
2020-12-14 15:35:10 -08:00
|
|
|
program_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
2021-12-17 05:01:12 -08:00
|
|
|
programdata_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let spill_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
|
|
|
|
let rent_account = create_account_for_test(&rent);
|
|
|
|
let clock_account = create_account_for_test(&Clock {
|
2022-12-13 13:54:24 -08:00
|
|
|
slot: SLOT.saturating_add(1),
|
2021-12-17 05:01:12 -08:00
|
|
|
..Clock::default()
|
|
|
|
});
|
|
|
|
let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
|
|
|
let transaction_accounts = vec![
|
|
|
|
(programdata_address, programdata_account),
|
|
|
|
(program_address, program_account),
|
|
|
|
(*buffer_address, buffer_account),
|
|
|
|
(spill_address, spill_account),
|
|
|
|
(sysvar::rent::id(), rent_account),
|
|
|
|
(sysvar::clock::id(), clock_account),
|
|
|
|
(*upgrade_authority_address, upgrade_authority_account),
|
|
|
|
];
|
|
|
|
let instruction_accounts = vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: programdata_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: program_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: *buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: spill_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: sysvar::rent::id(),
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: sysvar::clock::id(),
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: *upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
(transaction_accounts, instruction_accounts)
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2021-12-17 05:01:12 -08:00
|
|
|
fn process_instruction(
|
|
|
|
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
|
|
|
|
instruction_accounts: Vec<AccountMeta>,
|
|
|
|
expected_result: Result<(), InstructionError>,
|
|
|
|
) -> Vec<AccountSharedData> {
|
|
|
|
let instruction_data =
|
|
|
|
bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap();
|
|
|
|
mock_process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
Vec::new(),
|
|
|
|
&instruction_data,
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
|
|
|
expected_result,
|
|
|
|
super::process_instruction,
|
2023-04-03 08:23:24 -07:00
|
|
|
|_invoke_context| {},
|
2023-04-07 03:53:19 -07:00
|
|
|
|_invoke_context| {},
|
2020-12-14 15:35:10 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case: Success
|
2021-12-17 05:01:12 -08:00
|
|
|
let (transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(transaction_accounts, instruction_accounts, Ok(()));
|
|
|
|
let min_programdata_balance = Rent::default().minimum_balance(
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(
|
|
|
|
min_programdata_balance,
|
|
|
|
accounts.first().unwrap().lamports()
|
|
|
|
);
|
|
|
|
assert_eq!(0, accounts.get(2).unwrap().lamports());
|
|
|
|
assert_eq!(1, accounts.get(3).unwrap().lamports());
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
2022-12-13 13:54:24 -08:00
|
|
|
slot: SLOT.saturating_add(1),
|
2020-12-14 15:35:10 -08:00
|
|
|
upgrade_authority_address: Some(upgrade_authority_address)
|
|
|
|
}
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
for (i, byte) in accounts
|
|
|
|
.first()
|
|
|
|
.unwrap()
|
|
|
|
.data()
|
|
|
|
.get(
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata_metadata()
|
|
|
|
..UpgradeableLoaderState::size_of_programdata(elf_new.len()),
|
2022-03-10 11:48:33 -08:00
|
|
|
)
|
2021-12-17 05:01:12 -08:00
|
|
|
.unwrap()
|
2020-12-14 15:35:10 -08:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
{
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(*elf_new.get(i).unwrap(), *byte);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Case: not upgradable
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2020-12-14 15:35:10 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
2022-01-31 08:53:50 -08:00
|
|
|
slot: SLOT,
|
2020-12-14 15:35:10 -08:00
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::Immutable),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2021-04-19 09:48:48 -07:00
|
|
|
let invalid_upgrade_authority_address = Pubkey::new_unique();
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts.get_mut(6).unwrap().0 = invalid_upgrade_authority_address;
|
|
|
|
instruction_accounts.get_mut(6).unwrap().pubkey = invalid_upgrade_authority_address;
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: authority did not sign
|
2021-12-17 05:01:12 -08:00
|
|
|
let (transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
instruction_accounts.get_mut(6).unwrap().is_signer = false;
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
2022-09-16 01:54:20 -07:00
|
|
|
// Case: Buffer account and spill account alias
|
|
|
|
let (transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
|
|
|
*instruction_accounts.get_mut(3).unwrap() = instruction_accounts.get(2).unwrap().clone();
|
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
|
|
|
Err(InstructionError::AccountBorrowFailed),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Programdata account and spill account alias
|
|
|
|
let (transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
|
|
|
*instruction_accounts.get_mut(3).unwrap() = instruction_accounts.get(0).unwrap().clone();
|
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
|
|
|
Err(InstructionError::AccountBorrowFailed),
|
|
|
|
);
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
// Case: Program account not executable
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(1)
|
|
|
|
.unwrap()
|
|
|
|
.1
|
|
|
|
.set_executable(false);
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::AccountNotExecutable),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account now owned by loader
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(1)
|
|
|
|
.unwrap()
|
|
|
|
.1
|
|
|
|
.set_owner(Pubkey::new_unique());
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::IncorrectProgramId),
|
2021-01-19 16:24:44 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account not writable
|
2021-12-17 05:01:12 -08:00
|
|
|
let (transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2021-01-19 16:24:44 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
instruction_accounts.get_mut(1).unwrap().is_writable = false;
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-19 16:24:44 -08:00
|
|
|
Err(InstructionError::InvalidArgument),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account not initialized
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(1)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2020-12-14 15:35:10 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
);
|
|
|
|
|
2021-01-29 12:43:42 -08:00
|
|
|
// Case: Program ProgramData account mismatch
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2021-04-19 09:48:48 -07:00
|
|
|
let invalid_programdata_address = Pubkey::new_unique();
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts.get_mut(0).unwrap().0 = invalid_programdata_address;
|
|
|
|
instruction_accounts.get_mut(0).unwrap().pubkey = invalid_programdata_address;
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Buffer account not initialized
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(2)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2020-12-14 15:35:10 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::InvalidArgument),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
2021-01-29 12:43:42 -08:00
|
|
|
// Case: Buffer account too big
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts.get_mut(2).unwrap().1 = AccountSharedData::new(
|
2021-01-29 12:43:42 -08:00
|
|
|
1,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_buffer(
|
|
|
|
elf_orig.len().max(elf_new.len()).saturating_add(1),
|
|
|
|
),
|
2021-12-17 05:01:12 -08:00
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-29 12:43:42 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(2)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-29 12:43:42 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
2022-09-16 01:54:20 -07:00
|
|
|
// Case: Buffer account too small
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&upgrade_authority_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(2)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-29 12:43:42 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2022-03-10 11:48:33 -08:00
|
|
|
truncate_data(&mut transaction_accounts.get_mut(2).unwrap().1, 5);
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
2021-01-29 12:43:42 -08:00
|
|
|
// Case: Mismatched buffer and program authority
|
2021-12-17 05:01:12 -08:00
|
|
|
let (transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
2022-09-16 01:54:20 -07:00
|
|
|
// Case: No buffer authority
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-29 12:43:42 -08:00
|
|
|
&buffer_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(2)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
2021-01-29 12:43:42 -08:00
|
|
|
authority_address: None,
|
2021-01-08 09:37:57 -08:00
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
2022-09-16 01:54:20 -07:00
|
|
|
// Case: No buffer and program authority
|
2021-12-17 05:01:12 -08:00
|
|
|
let (mut transaction_accounts, instruction_accounts) = get_accounts(
|
|
|
|
&buffer_address,
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&upgrade_authority_address,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-29 12:43:42 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
2022-01-31 08:53:50 -08:00
|
|
|
slot: SLOT,
|
2021-01-29 12:43:42 -08:00
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(2)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
process_instruction(
|
|
|
|
transaction_accounts,
|
|
|
|
instruction_accounts,
|
2021-01-29 12:43:42 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_upgrade_authority() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
2020-12-14 15:35:10 -08:00
|
|
|
let slot = 0;
|
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2020-12-14 15:35:10 -08:00
|
|
|
let new_upgrade_authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let new_upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_address = Pubkey::new_unique();
|
2021-06-18 00:03:58 -07:00
|
|
|
let (programdata_address, _) = Pubkey::find_program_address(
|
|
|
|
&[program_address.as_ref()],
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut programdata_account = AccountSharedData::new(
|
2020-12-14 15:35:10 -08:00
|
|
|
1,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata(0),
|
2020-12-14 15:35:10 -08:00
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let programdata_meta = AccountMeta {
|
|
|
|
pubkey: programdata_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
};
|
|
|
|
let upgrade_authority_meta = AccountMeta {
|
|
|
|
pubkey: upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
let new_upgrade_authority_meta = AccountMeta {
|
|
|
|
pubkey: new_upgrade_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Case: Set to new authority
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
new_upgrade_authority_meta.clone(),
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(new_upgrade_authority_address),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not upgradeable
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Authority did not sign
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: upgrade_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
2021-04-19 09:48:48 -07:00
|
|
|
let invalid_upgrade_authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(
|
|
|
|
invalid_upgrade_authority_address,
|
|
|
|
upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
(new_upgrade_authority_address, new_upgrade_authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: invalid_upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_upgrade_authority_meta,
|
|
|
|
],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: No authority
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::Immutable),
|
2020-12-14 15:35:10 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a ProgramData account
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account),
|
|
|
|
],
|
|
|
|
vec![programdata_meta, upgrade_authority_meta],
|
2021-03-17 21:39:29 -07:00
|
|
|
Err(InstructionError::InvalidArgument),
|
2021-01-08 09:37:57 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-01 01:34:04 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_upgrade_authority_checked() {
|
|
|
|
let instruction =
|
|
|
|
bincode::serialize(&UpgradeableLoaderInstruction::SetAuthorityChecked).unwrap();
|
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
|
|
|
let slot = 0;
|
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
|
|
|
let new_upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let new_upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
|
|
let (programdata_address, _) = Pubkey::find_program_address(
|
|
|
|
&[program_address.as_ref()],
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
let mut programdata_account = AccountSharedData::new(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::size_of_programdata(0),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let programdata_meta = AccountMeta {
|
|
|
|
pubkey: programdata_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
};
|
|
|
|
let upgrade_authority_meta = AccountMeta {
|
|
|
|
pubkey: upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
let new_upgrade_authority_meta = AccountMeta {
|
|
|
|
pubkey: new_upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Case: Set to new authority
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
new_upgrade_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(new_upgrade_authority_address),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: set to same authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: present authority not in instruction
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![programdata_meta.clone(), new_upgrade_authority_meta.clone()],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: new authority not in instruction
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: present authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: upgrade_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_upgrade_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: New authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
(
|
|
|
|
new_upgrade_authority_address,
|
|
|
|
new_upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: new_upgrade_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong present authority
|
|
|
|
let invalid_upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(
|
|
|
|
invalid_upgrade_authority_address,
|
|
|
|
upgrade_authority_account.clone(),
|
|
|
|
),
|
|
|
|
(new_upgrade_authority_address, new_upgrade_authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: invalid_upgrade_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_upgrade_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: programdata is immutable
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta.clone(),
|
|
|
|
upgrade_authority_meta.clone(),
|
|
|
|
new_upgrade_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a ProgramData account
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(upgrade_authority_address, upgrade_authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
programdata_meta,
|
|
|
|
upgrade_authority_meta,
|
|
|
|
new_upgrade_authority_meta,
|
|
|
|
],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-08 09:37:57 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_buffer_authority() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
2021-12-17 05:01:12 -08:00
|
|
|
let invalid_authority_address = Pubkey::new_unique();
|
2021-01-08 09:37:57 -08:00
|
|
|
let authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2021-01-08 09:37:57 -08:00
|
|
|
let new_authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let new_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2021-01-08 09:37:57 -08:00
|
|
|
let buffer_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let mut buffer_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(0), &loader_id);
|
2021-01-08 09:37:57 -08:00
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut transaction_accounts = vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(authority_address, authority_account.clone()),
|
|
|
|
(new_authority_address, new_authority_account.clone()),
|
2021-04-19 09:48:48 -07:00
|
|
|
];
|
2021-12-17 05:01:12 -08:00
|
|
|
let buffer_meta = AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
};
|
|
|
|
let authority_meta = AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
let new_authority_meta = AccountMeta {
|
|
|
|
pubkey: new_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Case: New authority required
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta.clone(), authority_meta.clone()],
|
2021-10-08 02:41:07 -07:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2021-01-08 09:37:57 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
2021-10-08 02:41:07 -07:00
|
|
|
authority_address: Some(authority_address),
|
2021-01-08 09:37:57 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2021-10-08 02:41:07 -07:00
|
|
|
// Case: Set to new authority
|
2021-01-08 09:37:57 -08:00
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
2021-10-08 02:41:07 -07:00
|
|
|
Ok(()),
|
2021-01-08 09:37:57 -08:00
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
2021-10-08 02:41:07 -07:00
|
|
|
authority_address: Some(new_authority_address),
|
2021-01-08 09:37:57 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Authority did not sign
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(invalid_authority_address, authority_account),
|
|
|
|
(new_authority_address, new_authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: invalid_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: No authority
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta.clone(), authority_meta.clone()],
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Set to no authority
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a Buffer account
|
2022-03-10 11:48:33 -08:00
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
2021-12-17 05:01:12 -08:00
|
|
|
.1
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta, authority_meta, new_authority_meta],
|
2021-03-17 21:39:29 -07:00
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-01 01:34:04 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_buffer_authority_checked() {
|
|
|
|
let instruction =
|
|
|
|
bincode::serialize(&UpgradeableLoaderInstruction::SetAuthorityChecked).unwrap();
|
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
|
|
|
let invalid_authority_address = Pubkey::new_unique();
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
|
|
|
let new_authority_address = Pubkey::new_unique();
|
|
|
|
let new_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let mut buffer_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(0), &loader_id);
|
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let mut transaction_accounts = vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(authority_address, authority_account.clone()),
|
|
|
|
(new_authority_address, new_authority_account.clone()),
|
|
|
|
];
|
|
|
|
let buffer_meta = AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
};
|
|
|
|
let authority_meta = AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
let new_authority_meta = AccountMeta {
|
|
|
|
pubkey: new_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Case: Set to new authority
|
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(new_authority_address),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: set to same authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Missing current authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta.clone(), new_authority_meta.clone()],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Missing new authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta.clone(), authority_meta.clone()],
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong present authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(invalid_authority_address, authority_account),
|
|
|
|
(new_authority_address, new_authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: invalid_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: present authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: new authority did not sign
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: new_authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a Buffer account
|
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
|
|
|
.1
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
new_authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Buffer is immutable
|
|
|
|
transaction_accounts
|
|
|
|
.get_mut(0)
|
|
|
|
.unwrap()
|
|
|
|
.1
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts.clone(),
|
|
|
|
vec![buffer_meta, authority_meta, new_authority_meta],
|
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:39:29 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_close() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap();
|
2021-10-08 02:41:07 -07:00
|
|
|
let loader_id = bpf_loader_upgradeable::id();
|
2021-12-17 05:01:12 -08:00
|
|
|
let invalid_authority_address = Pubkey::new_unique();
|
2021-03-17 21:39:29 -07:00
|
|
|
let authority_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2021-03-17 21:39:29 -07:00
|
|
|
let recipient_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let recipient_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
|
2021-03-17 21:39:29 -07:00
|
|
|
let buffer_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let mut buffer_account =
|
2022-12-13 13:54:24 -08:00
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(128), &loader_id);
|
2021-03-17 21:39:29 -07:00
|
|
|
buffer_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
2021-08-24 10:05:54 -07:00
|
|
|
let uninitialized_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut uninitialized_account = AccountSharedData::new(
|
2021-08-24 10:05:54 -07:00
|
|
|
1,
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata(0),
|
2021-12-17 05:01:12 -08:00
|
|
|
&loader_id,
|
2021-08-24 10:05:54 -07:00
|
|
|
);
|
|
|
|
uninitialized_account
|
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
|
|
|
let programdata_address = Pubkey::new_unique();
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut programdata_account = AccountSharedData::new(
|
2021-08-24 10:05:54 -07:00
|
|
|
1,
|
2022-12-13 13:54:24 -08:00
|
|
|
UpgradeableLoaderState::size_of_programdata(128),
|
2021-12-17 05:01:12 -08:00
|
|
|
&loader_id,
|
2021-08-24 10:05:54 -07:00
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: 0,
|
|
|
|
upgrade_authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let program_address = Pubkey::new_unique();
|
2022-05-11 07:22:59 -07:00
|
|
|
let mut program_account =
|
|
|
|
AccountSharedData::new(1, UpgradeableLoaderState::size_of_program(), &loader_id);
|
2021-12-17 05:01:12 -08:00
|
|
|
program_account.set_executable(true);
|
2021-08-24 10:05:54 -07:00
|
|
|
program_account
|
2021-03-17 21:39:29 -07:00
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
2021-08-24 10:05:54 -07:00
|
|
|
programdata_address,
|
2021-03-17 21:39:29 -07:00
|
|
|
})
|
|
|
|
.unwrap();
|
2023-02-01 09:07:34 -08:00
|
|
|
let clock_account = create_account_for_test(&Clock {
|
|
|
|
slot: 1,
|
|
|
|
..Clock::default()
|
|
|
|
});
|
2021-12-17 05:01:12 -08:00
|
|
|
let transaction_accounts = vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(recipient_address, recipient_account.clone()),
|
|
|
|
(authority_address, authority_account.clone()),
|
2021-04-19 09:48:48 -07:00
|
|
|
];
|
2021-12-17 05:01:12 -08:00
|
|
|
let buffer_meta = AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
};
|
|
|
|
let recipient_meta = AccountMeta {
|
|
|
|
pubkey: recipient_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
};
|
|
|
|
let authority_meta = AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Case: close a buffer account
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
transaction_accounts,
|
|
|
|
vec![
|
|
|
|
buffer_meta.clone(),
|
|
|
|
recipient_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
],
|
2021-08-24 10:05:54 -07:00
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(0, accounts.first().unwrap().lamports());
|
|
|
|
assert_eq!(2, accounts.get(1).unwrap().lamports());
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
assert_eq!(state, UpgradeableLoaderState::Uninitialized);
|
2022-12-13 13:54:24 -08:00
|
|
|
assert_eq!(
|
|
|
|
UpgradeableLoaderState::size_of_uninitialized(),
|
|
|
|
accounts.first().unwrap().data().len()
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
|
|
|
|
// Case: close with wrong authority
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(buffer_address, buffer_account.clone()),
|
|
|
|
(recipient_address, recipient_account.clone()),
|
|
|
|
(invalid_authority_address, authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
buffer_meta,
|
|
|
|
recipient_meta.clone(),
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: invalid_authority_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: close an uninitialized account
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(uninitialized_address, uninitialized_account.clone()),
|
|
|
|
(recipient_address, recipient_account.clone()),
|
|
|
|
(invalid_authority_address, authority_account.clone()),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: uninitialized_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
},
|
|
|
|
recipient_meta.clone(),
|
|
|
|
authority_meta.clone(),
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(0, accounts.first().unwrap().lamports());
|
|
|
|
assert_eq!(2, accounts.get(1).unwrap().lamports());
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-12-17 05:01:12 -08:00
|
|
|
assert_eq!(state, UpgradeableLoaderState::Uninitialized);
|
2022-12-13 13:54:24 -08:00
|
|
|
assert_eq!(
|
|
|
|
UpgradeableLoaderState::size_of_uninitialized(),
|
|
|
|
accounts.first().unwrap().data().len()
|
|
|
|
);
|
2021-12-17 05:01:12 -08:00
|
|
|
|
|
|
|
// Case: close a program account
|
|
|
|
let accounts = process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&instruction,
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
2022-12-13 13:54:24 -08:00
|
|
|
(recipient_address, recipient_account.clone()),
|
|
|
|
(authority_address, authority_account.clone()),
|
2021-12-17 05:01:12 -08:00
|
|
|
(program_address, program_account.clone()),
|
2023-02-01 09:07:34 -08:00
|
|
|
(sysvar::clock::id(), clock_account.clone()),
|
2021-12-17 05:01:12 -08:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: programdata_address,
|
|
|
|
is_signer: false,
|
2022-06-29 10:29:12 -07:00
|
|
|
is_writable: true,
|
2021-12-17 05:01:12 -08:00
|
|
|
},
|
|
|
|
recipient_meta,
|
|
|
|
authority_meta,
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: program_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
Ok(()),
|
|
|
|
);
|
2022-03-10 11:48:33 -08:00
|
|
|
assert_eq!(0, accounts.first().unwrap().lamports());
|
|
|
|
assert_eq!(2, accounts.get(1).unwrap().lamports());
|
|
|
|
let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
|
2021-08-24 10:05:54 -07:00
|
|
|
assert_eq!(state, UpgradeableLoaderState::Uninitialized);
|
2022-12-13 13:54:24 -08:00
|
|
|
assert_eq!(
|
|
|
|
UpgradeableLoaderState::size_of_uninitialized(),
|
|
|
|
accounts.first().unwrap().data().len()
|
|
|
|
);
|
2021-08-24 10:05:54 -07:00
|
|
|
|
|
|
|
// Try to invoke closed account
|
2022-12-13 13:54:24 -08:00
|
|
|
programdata_account = accounts.first().unwrap().clone();
|
|
|
|
program_account = accounts.get(3).unwrap().clone();
|
2021-12-17 05:01:12 -08:00
|
|
|
process_instruction(
|
2023-05-18 13:18:28 -07:00
|
|
|
&loader_id,
|
2021-12-17 05:01:12 -08:00
|
|
|
&[0, 1],
|
|
|
|
&[],
|
|
|
|
vec![
|
|
|
|
(programdata_address, programdata_account.clone()),
|
|
|
|
(program_address, program_account.clone()),
|
|
|
|
],
|
|
|
|
Vec::new(),
|
2021-08-24 10:05:54 -07:00
|
|
|
Err(InstructionError::InvalidAccountData),
|
2020-08-11 16:11:52 -07:00
|
|
|
);
|
2022-12-13 13:54:24 -08:00
|
|
|
|
|
|
|
// Case: Reopen should fail
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
|
|
|
|
max_data_len: 0,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
vec![
|
|
|
|
(recipient_address, recipient_account),
|
|
|
|
(programdata_address, programdata_account),
|
|
|
|
(program_address, program_account),
|
|
|
|
(buffer_address, buffer_account),
|
|
|
|
(
|
|
|
|
sysvar::rent::id(),
|
|
|
|
create_account_for_test(&Rent::default()),
|
|
|
|
),
|
2023-02-01 09:07:34 -08:00
|
|
|
(sysvar::clock::id(), clock_account),
|
2022-12-13 13:54:24 -08:00
|
|
|
(
|
|
|
|
system_program::id(),
|
|
|
|
AccountSharedData::new(0, 0, &system_program::id()),
|
|
|
|
),
|
|
|
|
(authority_address, authority_account),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: recipient_address,
|
|
|
|
is_signer: true,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: programdata_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: program_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: true,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: buffer_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: sysvar::rent::id(),
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: sysvar::clock::id(),
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: system_program::id(),
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
AccountMeta {
|
|
|
|
pubkey: authority_address,
|
|
|
|
is_signer: false,
|
|
|
|
is_writable: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
Err(InstructionError::AccountAlreadyInitialized),
|
|
|
|
);
|
2020-08-11 16:11:52 -07:00
|
|
|
}
|
|
|
|
|
2020-05-08 12:37:04 -07:00
|
|
|
/// fuzzing utility function
|
|
|
|
fn fuzz<F>(
|
|
|
|
bytes: &[u8],
|
|
|
|
outer_iters: usize,
|
|
|
|
inner_iters: usize,
|
|
|
|
offset: Range<usize>,
|
|
|
|
value: Range<u8>,
|
|
|
|
work: F,
|
|
|
|
) where
|
|
|
|
F: Fn(&mut [u8]),
|
|
|
|
{
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
for _ in 0..outer_iters {
|
|
|
|
let mut mangled_bytes = bytes.to_vec();
|
|
|
|
for _ in 0..inner_iters {
|
2023-08-21 10:11:21 -07:00
|
|
|
let offset = rng.gen_range(offset.start..offset.end);
|
|
|
|
let value = rng.gen_range(value.start..value.end);
|
2022-03-10 11:48:33 -08:00
|
|
|
*mangled_bytes.get_mut(offset).unwrap() = value;
|
2020-05-08 12:37:04 -07:00
|
|
|
work(&mut mangled_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_fuzz() {
|
2021-11-04 13:47:32 -07:00
|
|
|
let loader_id = bpf_loader::id();
|
|
|
|
let program_id = Pubkey::new_unique();
|
2020-05-08 12:37:04 -07:00
|
|
|
|
|
|
|
// Create program account
|
2022-05-03 17:10:28 -07:00
|
|
|
let mut file = File::open("test_elfs/out/noop_aligned.so").expect("file open failed");
|
2020-05-08 12:37:04 -07:00
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
// Mangle the whole file
|
2020-05-08 12:37:04 -07:00
|
|
|
fuzz(
|
|
|
|
&elf,
|
|
|
|
1_000_000_000,
|
|
|
|
100,
|
|
|
|
0..elf.len(),
|
|
|
|
0..255,
|
|
|
|
|bytes: &mut [u8]| {
|
2021-12-17 05:01:12 -08:00
|
|
|
let mut program_account = AccountSharedData::new(1, 0, &loader_id);
|
|
|
|
program_account.set_data(bytes.to_vec());
|
|
|
|
program_account.set_executable(true);
|
|
|
|
process_instruction(
|
|
|
|
&loader_id,
|
|
|
|
&[],
|
|
|
|
&[],
|
|
|
|
vec![(program_id, program_account)],
|
|
|
|
Vec::new(),
|
|
|
|
Ok(()),
|
|
|
|
);
|
2020-05-08 12:37:04 -07:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2023-03-15 10:03:04 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_calculate_heap_cost() {
|
|
|
|
let heap_cost = 8_u64;
|
|
|
|
|
|
|
|
// heap allocations are in 32K block, `heap_cost` of CU is consumed per additional 32k
|
|
|
|
|
|
|
|
// when `enable_heap_size_round_up` not enabled:
|
|
|
|
{
|
|
|
|
// assert less than 32K heap should cost zero unit
|
|
|
|
assert_eq!(0, calculate_heap_cost(31_u64 * 1024, heap_cost, false));
|
|
|
|
|
|
|
|
// assert exact 32K heap should be cost zero unit
|
|
|
|
assert_eq!(0, calculate_heap_cost(32_u64 * 1024, heap_cost, false));
|
|
|
|
|
|
|
|
// assert slightly more than 32K heap is mistakenly cost zero unit
|
|
|
|
assert_eq!(0, calculate_heap_cost(33_u64 * 1024, heap_cost, false));
|
|
|
|
|
|
|
|
// assert exact 64K heap should cost 1 * heap_cost
|
|
|
|
assert_eq!(
|
|
|
|
heap_cost,
|
|
|
|
calculate_heap_cost(64_u64 * 1024, heap_cost, false)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// when `enable_heap_size_round_up` is enabled:
|
|
|
|
{
|
|
|
|
// assert less than 32K heap should cost zero unit
|
|
|
|
assert_eq!(0, calculate_heap_cost(31_u64 * 1024, heap_cost, true));
|
|
|
|
|
|
|
|
// assert exact 32K heap should be cost zero unit
|
|
|
|
assert_eq!(0, calculate_heap_cost(32_u64 * 1024, heap_cost, true));
|
|
|
|
|
|
|
|
// assert slightly more than 32K heap should cost 1 * heap_cost
|
|
|
|
assert_eq!(
|
|
|
|
heap_cost,
|
|
|
|
calculate_heap_cost(33_u64 * 1024, heap_cost, true)
|
|
|
|
);
|
|
|
|
|
|
|
|
// assert exact 64K heap should cost 1 * heap_cost
|
|
|
|
assert_eq!(
|
|
|
|
heap_cost,
|
|
|
|
calculate_heap_cost(64_u64 * 1024, heap_cost, true)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-04-11 09:43:42 -07:00
|
|
|
|
|
|
|
fn deploy_test_program(
|
|
|
|
invoke_context: &mut InvokeContext,
|
|
|
|
program_id: Pubkey,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut file = File::open("test_elfs/out/noop_unaligned.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
deploy_program!(
|
|
|
|
invoke_context,
|
|
|
|
program_id,
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
elf.len(),
|
|
|
|
2,
|
|
|
|
{},
|
|
|
|
&elf
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_program_usage_count_on_upgrade() {
|
|
|
|
let transaction_accounts = vec![];
|
|
|
|
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
|
|
|
let program_id = Pubkey::new_unique();
|
2023-06-03 00:38:11 -07:00
|
|
|
let env = Arc::new(BuiltinProgram::new_loader(Config::default()));
|
2023-04-11 09:43:42 -07:00
|
|
|
let program = LoadedProgram {
|
2023-06-03 00:38:11 -07:00
|
|
|
program: LoadedProgramType::Unloaded(env),
|
2023-04-11 09:43:42 -07:00
|
|
|
account_size: 0,
|
|
|
|
deployment_slot: 0,
|
|
|
|
effective_slot: 0,
|
|
|
|
maybe_expiration_slot: None,
|
2023-06-02 11:26:56 -07:00
|
|
|
tx_usage_counter: AtomicU64::new(100),
|
|
|
|
ix_usage_counter: AtomicU64::new(100),
|
2023-04-11 09:43:42 -07:00
|
|
|
};
|
2023-05-09 15:44:38 -07:00
|
|
|
invoke_context
|
|
|
|
.programs_modified_by_tx
|
|
|
|
.replenish(program_id, Arc::new(program));
|
2023-04-11 09:43:42 -07:00
|
|
|
|
2023-08-30 10:48:27 -07:00
|
|
|
assert_matches!(
|
2023-04-11 09:43:42 -07:00
|
|
|
deploy_test_program(&mut invoke_context, program_id,),
|
|
|
|
Ok(())
|
2023-08-30 10:48:27 -07:00
|
|
|
);
|
2023-04-11 09:43:42 -07:00
|
|
|
|
|
|
|
let updated_program = invoke_context
|
2023-05-09 15:44:38 -07:00
|
|
|
.programs_modified_by_tx
|
|
|
|
.find(&program_id)
|
2023-04-11 09:43:42 -07:00
|
|
|
.expect("Didn't find upgraded program in the cache");
|
|
|
|
|
|
|
|
assert_eq!(updated_program.deployment_slot, 2);
|
2023-06-02 11:26:56 -07:00
|
|
|
assert_eq!(
|
|
|
|
updated_program.tx_usage_counter.load(Ordering::Relaxed),
|
|
|
|
100
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
updated_program.ix_usage_counter.load(Ordering::Relaxed),
|
|
|
|
100
|
|
|
|
);
|
2023-04-11 09:43:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_program_usage_count_on_non_upgrade() {
|
|
|
|
let transaction_accounts = vec![];
|
|
|
|
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
|
|
|
let program_id = Pubkey::new_unique();
|
2023-06-03 00:38:11 -07:00
|
|
|
let env = Arc::new(BuiltinProgram::new_loader(Config::default()));
|
2023-04-11 09:43:42 -07:00
|
|
|
let program = LoadedProgram {
|
2023-06-03 00:38:11 -07:00
|
|
|
program: LoadedProgramType::Unloaded(env),
|
2023-04-11 09:43:42 -07:00
|
|
|
account_size: 0,
|
|
|
|
deployment_slot: 0,
|
|
|
|
effective_slot: 0,
|
|
|
|
maybe_expiration_slot: None,
|
2023-06-02 11:26:56 -07:00
|
|
|
tx_usage_counter: AtomicU64::new(100),
|
|
|
|
ix_usage_counter: AtomicU64::new(100),
|
2023-04-11 09:43:42 -07:00
|
|
|
};
|
2023-05-09 15:44:38 -07:00
|
|
|
invoke_context
|
|
|
|
.programs_modified_by_tx
|
|
|
|
.replenish(program_id, Arc::new(program));
|
2023-04-11 09:43:42 -07:00
|
|
|
|
|
|
|
let program_id2 = Pubkey::new_unique();
|
2023-08-30 10:48:27 -07:00
|
|
|
assert_matches!(
|
2023-04-11 09:43:42 -07:00
|
|
|
deploy_test_program(&mut invoke_context, program_id2),
|
|
|
|
Ok(())
|
2023-08-30 10:48:27 -07:00
|
|
|
);
|
2023-04-11 09:43:42 -07:00
|
|
|
|
|
|
|
let program2 = invoke_context
|
2023-05-09 15:44:38 -07:00
|
|
|
.programs_modified_by_tx
|
|
|
|
.find(&program_id2)
|
2023-04-11 09:43:42 -07:00
|
|
|
.expect("Didn't find upgraded program in the cache");
|
|
|
|
|
|
|
|
assert_eq!(program2.deployment_slot, 2);
|
2023-06-02 11:26:56 -07:00
|
|
|
assert_eq!(program2.tx_usage_counter.load(Ordering::Relaxed), 0);
|
|
|
|
assert_eq!(program2.ix_usage_counter.load(Ordering::Relaxed), 0);
|
2023-04-11 09:43:42 -07:00
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
}
|