Skip to main content

solana_bpf_loader_program/
lib.rs

1#![cfg(feature = "agave-unstable-api")]
2#![deny(clippy::arithmetic_side_effects)]
3#![deny(clippy::indexing_slicing)]
4
5#[cfg(feature = "svm-internal")]
6use qualifier_attr::qualifiers;
7use {
8    solana_bincode::limited_deserialize,
9    solana_instruction::{AccountMeta, error::InstructionError},
10    solana_loader_v3_interface::{
11        instruction::{MINIMUM_EXTEND_PROGRAM_BYTES, UpgradeableLoaderInstruction},
12        state::UpgradeableLoaderState,
13    },
14    solana_program_runtime::{
15        deploy_program,
16        invoke_context::InvokeContext,
17        program_cache_entry::{ProgramCacheEntry, ProgramCacheEntryOwner, ProgramCacheEntryType},
18        sysvar_cache::get_sysvar_with_account_check,
19        vm::execute,
20    },
21    solana_pubkey::Pubkey,
22    solana_sbpf::{declare_builtin_function, elf::get_sbpf_version, program::SBPFVersion},
23    solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, native_loader},
24    solana_svm_log_collector::{LogCollector, ic_logger_msg, ic_msg},
25    solana_svm_measure::measure::Measure,
26    solana_svm_type_overrides::sync::Arc,
27    solana_system_interface::{MAX_PERMITTED_DATA_LENGTH, instruction as system_instruction},
28    solana_transaction_context::{IndexOfAccount, instruction::InstructionContext},
29    std::{cell::RefCell, rc::Rc},
30};
31
32#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
33const DEFAULT_LOADER_COMPUTE_UNITS: u64 = 570;
34#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
35const DEPRECATED_LOADER_COMPUTE_UNITS: u64 = 1_140;
36#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
37const UPGRADEABLE_LOADER_COMPUTE_UNITS: u64 = 2_370;
38
39fn write_program_data(
40    program_data_offset: usize,
41    bytes: &[u8],
42    invoke_context: &mut InvokeContext,
43) -> Result<(), InstructionError> {
44    let transaction_context = &invoke_context.transaction_context;
45    let instruction_context = transaction_context.get_current_instruction_context()?;
46    let mut program = instruction_context.try_borrow_instruction_account(0)?;
47    let data = program.get_data_mut()?;
48    let write_offset = program_data_offset.saturating_add(bytes.len());
49    if data.len() < write_offset {
50        ic_msg!(
51            invoke_context,
52            "Write overflow: {} < {}",
53            data.len(),
54            write_offset,
55        );
56        return Err(InstructionError::AccountDataTooSmall);
57    }
58    data.get_mut(program_data_offset..write_offset)
59        .ok_or(InstructionError::AccountDataTooSmall)?
60        .copy_from_slice(bytes);
61    Ok(())
62}
63
64declare_builtin_function!(
65    Entrypoint,
66    fn rust(
67        invoke_context: &mut InvokeContext<'static, 'static>,
68        _arg0: u64,
69        _arg1: u64,
70        _arg2: u64,
71        _arg3: u64,
72        _arg4: u64,
73    ) -> Result<u64, Box<dyn std::error::Error>> {
74        process_instruction_inner(invoke_context)
75    }
76);
77
78mod migration_authority {
79    solana_pubkey::declare_id!("3Scf35jMNk2xXBD6areNjgMtXgp5ZspDhms8vdcbzC42");
80}
81
82#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
83pub(crate) fn process_instruction_inner<'a>(
84    invoke_context: &mut InvokeContext<'a, 'a>,
85) -> Result<u64, Box<dyn std::error::Error>> {
86    let log_collector = invoke_context.get_log_collector();
87    let transaction_context = &invoke_context.transaction_context;
88    let instruction_context = transaction_context.get_current_instruction_context()?;
89    let program_id = instruction_context.get_program_key()?;
90    let owner_id = instruction_context.get_program_owner()?;
91
92    // Program Management Instruction
93    if native_loader::check_id(&owner_id) {
94        let program_id = instruction_context.get_program_key()?;
95        return if bpf_loader_upgradeable::check_id(program_id) {
96            invoke_context
97                .compute_meter
98                .consume_checked(UPGRADEABLE_LOADER_COMPUTE_UNITS)?;
99            process_loader_upgradeable_instruction(invoke_context)
100        } else if bpf_loader::check_id(program_id) {
101            invoke_context
102                .compute_meter
103                .consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?;
104            ic_logger_msg!(
105                log_collector,
106                "BPF loader management instructions are no longer supported",
107            );
108            Err(InstructionError::UnsupportedProgramId)
109        } else if bpf_loader_deprecated::check_id(program_id) {
110            invoke_context
111                .compute_meter
112                .consume_checked(DEPRECATED_LOADER_COMPUTE_UNITS)?;
113            ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
114            Err(InstructionError::UnsupportedProgramId)
115        } else {
116            ic_logger_msg!(log_collector, "Invalid BPF loader id");
117            Err(InstructionError::UnsupportedProgramId)
118        }
119        .map(|_| 0)
120        .map_err(|error| Box::new(error) as Box<dyn std::error::Error>);
121    }
122
123    // Program Invocation
124    let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
125    let executor = invoke_context
126        .program_cache_for_tx_batch
127        .find(program_id)
128        .ok_or_else(|| {
129            ic_logger_msg!(log_collector, "Program is not cached");
130            InstructionError::UnsupportedProgramId
131        })?;
132    get_or_create_executor_time.stop();
133    invoke_context.timings.get_or_create_executor_us += get_or_create_executor_time.as_us();
134
135    match &executor.program {
136        ProgramCacheEntryType::FailedVerification(_)
137        | ProgramCacheEntryType::Closed
138        | ProgramCacheEntryType::DelayVisibility => {
139            ic_logger_msg!(log_collector, "Program is not deployed");
140            Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>)
141        }
142        ProgramCacheEntryType::Loaded(executable) => execute(executable, invoke_context, &executor),
143        _ => Err(Box::new(InstructionError::UnsupportedProgramId) as Box<dyn std::error::Error>),
144    }
145    .map(|_| 0)
146}
147
148fn process_loader_upgradeable_instruction(
149    invoke_context: &mut InvokeContext,
150) -> Result<(), InstructionError> {
151    let log_collector = invoke_context.get_log_collector();
152    let transaction_context = &invoke_context.transaction_context;
153    let instruction_context = transaction_context.get_current_instruction_context()?;
154    let instruction_data = instruction_context.get_instruction_data();
155    let program_id = instruction_context.get_program_key()?;
156
157    match limited_deserialize(instruction_data, solana_packet::PACKET_DATA_SIZE as u64)? {
158        UpgradeableLoaderInstruction::InitializeBuffer => {
159            instruction_context.check_number_of_instruction_accounts(2)?;
160            let mut buffer = instruction_context.try_borrow_instruction_account(0)?;
161
162            if UpgradeableLoaderState::Uninitialized != buffer.get_state()? {
163                ic_logger_msg!(log_collector, "Buffer account already initialized");
164                return Err(InstructionError::AccountAlreadyInitialized);
165            }
166
167            let authority_key = Some(*instruction_context.get_key_of_instruction_account(1)?);
168
169            buffer.set_state(&UpgradeableLoaderState::Buffer {
170                authority_address: authority_key,
171            })?;
172        }
173        UpgradeableLoaderInstruction::Write { offset, bytes } => {
174            instruction_context.check_number_of_instruction_accounts(2)?;
175            let buffer = instruction_context.try_borrow_instruction_account(0)?;
176
177            if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
178                if authority_address.is_none() {
179                    ic_logger_msg!(log_collector, "Buffer is immutable");
180                    return Err(InstructionError::Immutable); // TODO better error code
181                }
182                let authority_key = Some(*instruction_context.get_key_of_instruction_account(1)?);
183                if authority_address != authority_key {
184                    ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
185                    return Err(InstructionError::IncorrectAuthority);
186                }
187                if !instruction_context.is_instruction_account_signer(1)? {
188                    ic_logger_msg!(log_collector, "Buffer authority did not sign");
189                    return Err(InstructionError::MissingRequiredSignature);
190                }
191            } else {
192                ic_logger_msg!(log_collector, "Invalid Buffer account");
193                return Err(InstructionError::InvalidAccountData);
194            }
195            drop(buffer);
196            write_program_data(
197                UpgradeableLoaderState::size_of_buffer_metadata().saturating_add(offset as usize),
198                &bytes,
199                invoke_context,
200            )?;
201        }
202        UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
203            instruction_context.check_number_of_instruction_accounts(4)?;
204            let payer_key = *instruction_context.get_key_of_instruction_account(0)?;
205            let programdata_key = *instruction_context.get_key_of_instruction_account(1)?;
206            let rent =
207                get_sysvar_with_account_check::rent(invoke_context, &instruction_context, 4)?;
208            let clock =
209                get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 5)?;
210            instruction_context.check_number_of_instruction_accounts(8)?;
211            let authority_key = Some(*instruction_context.get_key_of_instruction_account(7)?);
212
213            // Verify Program account
214
215            let program = instruction_context.try_borrow_instruction_account(2)?;
216            if UpgradeableLoaderState::Uninitialized != program.get_state()? {
217                ic_logger_msg!(log_collector, "Program account already initialized");
218                return Err(InstructionError::AccountAlreadyInitialized);
219            }
220            if program.get_data().len() < UpgradeableLoaderState::size_of_program() {
221                ic_logger_msg!(log_collector, "Program account too small");
222                return Err(InstructionError::AccountDataTooSmall);
223            }
224            if program.get_lamports() < rent.minimum_balance(program.get_data().len()) {
225                ic_logger_msg!(log_collector, "Program account not rent-exempt");
226                return Err(InstructionError::ExecutableAccountNotRentExempt);
227            }
228            let new_program_id = *program.get_key();
229            drop(program);
230
231            // Verify Buffer account
232
233            let buffer = instruction_context.try_borrow_instruction_account(3)?;
234            if !buffer.is_writable() {
235                ic_logger_msg!(log_collector, "Buffer account not writeable");
236                return Err(InstructionError::InvalidArgument);
237            }
238            if buffer.get_owner() != program_id {
239                ic_logger_msg!(log_collector, "Buffer account not owned by loader");
240                return Err(InstructionError::IncorrectProgramId);
241            }
242            if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
243                if authority_address != authority_key {
244                    ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
245                    return Err(InstructionError::IncorrectAuthority);
246                }
247                if !instruction_context.is_instruction_account_signer(7)? {
248                    ic_logger_msg!(log_collector, "Upgrade authority did not sign");
249                    return Err(InstructionError::MissingRequiredSignature);
250                }
251            } else {
252                ic_logger_msg!(log_collector, "Invalid Buffer account");
253                return Err(InstructionError::InvalidArgument);
254            }
255            let buffer_key = *buffer.get_key();
256            let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
257            let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
258            let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
259            let programdata_len = UpgradeableLoaderState::size_of_programdata(max_data_len);
260            if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
261                || buffer_data_len == 0
262            {
263                ic_logger_msg!(log_collector, "Buffer account too small");
264                return Err(InstructionError::InvalidAccountData);
265            }
266            drop(buffer);
267            if max_data_len < buffer_data_len {
268                ic_logger_msg!(
269                    log_collector,
270                    "Max data length is too small to hold Buffer data"
271                );
272                return Err(InstructionError::AccountDataTooSmall);
273            }
274            if programdata_len > MAX_PERMITTED_DATA_LENGTH as usize {
275                ic_logger_msg!(log_collector, "Max data length is too large");
276                return Err(InstructionError::InvalidArgument);
277            }
278
279            // Create ProgramData account
280            let (derived_address, bump_seed) =
281                Pubkey::find_program_address(&[new_program_id.as_ref()], program_id);
282            if derived_address != programdata_key {
283                ic_logger_msg!(log_collector, "ProgramData address is not derived");
284                return Err(InstructionError::InvalidArgument);
285            }
286
287            // Drain the Buffer account to payer before paying for programdata account
288            {
289                let mut buffer = instruction_context.try_borrow_instruction_account(3)?;
290                let mut payer = instruction_context.try_borrow_instruction_account(0)?;
291                payer.checked_add_lamports(buffer.get_lamports())?;
292                buffer.set_lamports(0)?;
293            }
294
295            let owner_id = *program_id;
296            let mut instruction = system_instruction::create_account(
297                &payer_key,
298                &programdata_key,
299                1.max(rent.minimum_balance(programdata_len)),
300                programdata_len as u64,
301                program_id,
302            );
303
304            // pass an extra account to avoid the overly strict UnbalancedInstruction error
305            instruction
306                .accounts
307                .push(AccountMeta::new(buffer_key, false));
308
309            invoke_context
310                .native_invoke_signed(instruction, &[&[new_program_id.as_ref(), &[bump_seed]]])?;
311
312            // Load and verify the program bits
313            let transaction_context = &invoke_context.transaction_context;
314            let instruction_context = transaction_context.get_current_instruction_context()?;
315            let buffer = instruction_context.try_borrow_instruction_account(3)?;
316            deploy_program!(
317                invoke_context,
318                &new_program_id,
319                &owner_id,
320                UpgradeableLoaderState::size_of_program().saturating_add(programdata_len),
321                buffer
322                    .get_data()
323                    .get(buffer_data_offset..)
324                    .ok_or(InstructionError::AccountDataTooSmall)?,
325                clock.slot,
326                invoke_context
327                    .get_feature_set()
328                    .disable_sbpf_v0_v1_v2_deployment,
329            );
330            drop(buffer);
331
332            let transaction_context = &invoke_context.transaction_context;
333            let instruction_context = transaction_context.get_current_instruction_context()?;
334
335            // Update the ProgramData account and record the program bits
336            {
337                let mut programdata = instruction_context.try_borrow_instruction_account(1)?;
338                programdata.set_state(&UpgradeableLoaderState::ProgramData {
339                    slot: clock.slot,
340                    upgrade_authority_address: authority_key,
341                })?;
342                let dst_slice = programdata
343                    .get_data_mut()?
344                    .get_mut(
345                        programdata_data_offset
346                            ..programdata_data_offset.saturating_add(buffer_data_len),
347                    )
348                    .ok_or(InstructionError::AccountDataTooSmall)?;
349                let mut buffer = instruction_context.try_borrow_instruction_account(3)?;
350                let src_slice = buffer
351                    .get_data()
352                    .get(buffer_data_offset..)
353                    .ok_or(InstructionError::AccountDataTooSmall)?;
354                dst_slice.copy_from_slice(src_slice);
355                buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
356            }
357
358            // Update the Program account
359            let mut program = instruction_context.try_borrow_instruction_account(2)?;
360            program.set_state(&UpgradeableLoaderState::Program {
361                programdata_address: programdata_key,
362            })?;
363            program.set_executable(true)?;
364            drop(program);
365
366            ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id);
367        }
368        UpgradeableLoaderInstruction::Upgrade => {
369            instruction_context.check_number_of_instruction_accounts(3)?;
370            let programdata_key = *instruction_context.get_key_of_instruction_account(0)?;
371            let rent =
372                get_sysvar_with_account_check::rent(invoke_context, &instruction_context, 4)?;
373            let clock =
374                get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 5)?;
375            instruction_context.check_number_of_instruction_accounts(7)?;
376            let authority_key = Some(*instruction_context.get_key_of_instruction_account(6)?);
377
378            // Verify Program account
379
380            let program = instruction_context.try_borrow_instruction_account(1)?;
381            if !program.is_writable() {
382                ic_logger_msg!(log_collector, "Program account not writeable");
383                return Err(InstructionError::InvalidArgument);
384            }
385            if program.get_owner() != program_id {
386                ic_logger_msg!(log_collector, "Program account not owned by loader");
387                return Err(InstructionError::IncorrectProgramId);
388            }
389            if let UpgradeableLoaderState::Program {
390                programdata_address,
391            } = program.get_state()?
392            {
393                if programdata_address != programdata_key {
394                    ic_logger_msg!(log_collector, "Program and ProgramData account mismatch");
395                    return Err(InstructionError::InvalidArgument);
396                }
397            } else {
398                ic_logger_msg!(log_collector, "Invalid Program account");
399                return Err(InstructionError::InvalidAccountData);
400            }
401            let new_program_id = *program.get_key();
402            drop(program);
403
404            // Verify Buffer account
405
406            let buffer = instruction_context.try_borrow_instruction_account(2)?;
407            if !buffer.is_writable() {
408                ic_logger_msg!(log_collector, "Buffer account not writeable");
409                return Err(InstructionError::InvalidArgument);
410            }
411            if buffer.get_owner() != program_id {
412                ic_logger_msg!(log_collector, "Buffer account not owned by loader");
413                return Err(InstructionError::IncorrectProgramId);
414            }
415            if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
416                if authority_address != authority_key {
417                    ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
418                    return Err(InstructionError::IncorrectAuthority);
419                }
420                if !instruction_context.is_instruction_account_signer(6)? {
421                    ic_logger_msg!(log_collector, "Upgrade authority did not sign");
422                    return Err(InstructionError::MissingRequiredSignature);
423                }
424            } else {
425                ic_logger_msg!(log_collector, "Invalid Buffer account");
426                return Err(InstructionError::InvalidArgument);
427            }
428            let buffer_lamports = buffer.get_lamports();
429            let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata();
430            let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
431            if buffer.get_data().len() < UpgradeableLoaderState::size_of_buffer_metadata()
432                || buffer_data_len == 0
433            {
434                ic_logger_msg!(log_collector, "Buffer account too small");
435                return Err(InstructionError::InvalidAccountData);
436            }
437            drop(buffer);
438
439            // Verify ProgramData account
440
441            let programdata = instruction_context.try_borrow_instruction_account(0)?;
442            let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
443            let programdata_balance_required =
444                1.max(rent.minimum_balance(programdata.get_data().len()));
445            if programdata.get_data().len()
446                < UpgradeableLoaderState::size_of_programdata(buffer_data_len)
447            {
448                ic_logger_msg!(log_collector, "ProgramData account not large enough");
449                return Err(InstructionError::AccountDataTooSmall);
450            }
451            if programdata.get_lamports().saturating_add(buffer_lamports)
452                < programdata_balance_required
453            {
454                ic_logger_msg!(
455                    log_collector,
456                    "Buffer account balance too low to fund upgrade"
457                );
458                return Err(InstructionError::InsufficientFunds);
459            }
460            if let UpgradeableLoaderState::ProgramData {
461                slot,
462                upgrade_authority_address,
463            } = programdata.get_state()?
464            {
465                if clock.slot == slot {
466                    ic_logger_msg!(log_collector, "Program was deployed in this block already");
467                    return Err(InstructionError::InvalidArgument);
468                }
469                if upgrade_authority_address.is_none() {
470                    ic_logger_msg!(log_collector, "Program not upgradeable");
471                    return Err(InstructionError::Immutable);
472                }
473                if upgrade_authority_address != authority_key {
474                    ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
475                    return Err(InstructionError::IncorrectAuthority);
476                }
477                if !instruction_context.is_instruction_account_signer(6)? {
478                    ic_logger_msg!(log_collector, "Upgrade authority did not sign");
479                    return Err(InstructionError::MissingRequiredSignature);
480                }
481            } else {
482                ic_logger_msg!(log_collector, "Invalid ProgramData account");
483                return Err(InstructionError::InvalidAccountData);
484            };
485            let programdata_len = programdata.get_data().len();
486            drop(programdata);
487
488            // Load and verify the program bits
489            let buffer = instruction_context.try_borrow_instruction_account(2)?;
490            deploy_program!(
491                invoke_context,
492                &new_program_id,
493                program_id,
494                UpgradeableLoaderState::size_of_program().saturating_add(programdata_len),
495                buffer
496                    .get_data()
497                    .get(buffer_data_offset..)
498                    .ok_or(InstructionError::AccountDataTooSmall)?,
499                clock.slot,
500                invoke_context
501                    .get_feature_set()
502                    .disable_sbpf_v0_v1_v2_deployment,
503            );
504            drop(buffer);
505
506            let transaction_context = &invoke_context.transaction_context;
507            let instruction_context = transaction_context.get_current_instruction_context()?;
508
509            // Update the ProgramData account, record the upgraded data, and zero
510            // the rest
511            let mut programdata = instruction_context.try_borrow_instruction_account(0)?;
512            {
513                programdata.set_state(&UpgradeableLoaderState::ProgramData {
514                    slot: clock.slot,
515                    upgrade_authority_address: authority_key,
516                })?;
517                let dst_slice = programdata
518                    .get_data_mut()?
519                    .get_mut(
520                        programdata_data_offset
521                            ..programdata_data_offset.saturating_add(buffer_data_len),
522                    )
523                    .ok_or(InstructionError::AccountDataTooSmall)?;
524                let buffer = instruction_context.try_borrow_instruction_account(2)?;
525                let src_slice = buffer
526                    .get_data()
527                    .get(buffer_data_offset..)
528                    .ok_or(InstructionError::AccountDataTooSmall)?;
529                dst_slice.copy_from_slice(src_slice);
530            }
531            programdata
532                .get_data_mut()?
533                .get_mut(programdata_data_offset.saturating_add(buffer_data_len)..)
534                .ok_or(InstructionError::AccountDataTooSmall)?
535                .fill(0);
536
537            // Fund ProgramData to rent-exemption, spill the rest
538            let mut buffer = instruction_context.try_borrow_instruction_account(2)?;
539            let mut spill = instruction_context.try_borrow_instruction_account(3)?;
540            spill.checked_add_lamports(
541                programdata
542                    .get_lamports()
543                    .saturating_add(buffer_lamports)
544                    .saturating_sub(programdata_balance_required),
545            )?;
546            buffer.set_lamports(0)?;
547            programdata.set_lamports(programdata_balance_required)?;
548            buffer.set_data_length(UpgradeableLoaderState::size_of_buffer(0))?;
549
550            ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id);
551        }
552        UpgradeableLoaderInstruction::SetAuthority => {
553            instruction_context.check_number_of_instruction_accounts(2)?;
554            let mut account = instruction_context.try_borrow_instruction_account(0)?;
555            let present_authority_key = instruction_context.get_key_of_instruction_account(1)?;
556            let new_authority = instruction_context.get_key_of_instruction_account(2).ok();
557
558            match account.get_state()? {
559                UpgradeableLoaderState::Buffer { authority_address } => {
560                    if new_authority.is_none() {
561                        ic_logger_msg!(log_collector, "Buffer authority is not optional");
562                        return Err(InstructionError::IncorrectAuthority);
563                    }
564                    if authority_address.is_none() {
565                        ic_logger_msg!(log_collector, "Buffer is immutable");
566                        return Err(InstructionError::Immutable);
567                    }
568                    if authority_address != Some(*present_authority_key) {
569                        ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
570                        return Err(InstructionError::IncorrectAuthority);
571                    }
572                    if !instruction_context.is_instruction_account_signer(1)? {
573                        ic_logger_msg!(log_collector, "Buffer authority did not sign");
574                        return Err(InstructionError::MissingRequiredSignature);
575                    }
576                    account.set_state(&UpgradeableLoaderState::Buffer {
577                        authority_address: new_authority.cloned(),
578                    })?;
579                }
580                UpgradeableLoaderState::ProgramData {
581                    slot,
582                    upgrade_authority_address,
583                } => {
584                    if upgrade_authority_address.is_none() {
585                        ic_logger_msg!(log_collector, "Program not upgradeable");
586                        return Err(InstructionError::Immutable);
587                    }
588                    if upgrade_authority_address != Some(*present_authority_key) {
589                        ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
590                        return Err(InstructionError::IncorrectAuthority);
591                    }
592                    if !instruction_context.is_instruction_account_signer(1)? {
593                        ic_logger_msg!(log_collector, "Upgrade authority did not sign");
594                        return Err(InstructionError::MissingRequiredSignature);
595                    }
596                    if invoke_context
597                        .get_feature_set()
598                        .disable_sbpf_v0_v1_v2_deployment
599                        && new_authority.is_none()
600                        && let Some(program) = account
601                            .get_data()
602                            .get(UpgradeableLoaderState::size_of_programdata_metadata()..)
603                        && let Ok(sbpf_version) = get_sbpf_version(program)
604                        && sbpf_version < SBPFVersion::V3
605                    {
606                        return Err(InstructionError::InvalidAccountData);
607                    }
608                    account.set_state(&UpgradeableLoaderState::ProgramData {
609                        slot,
610                        upgrade_authority_address: new_authority.cloned(),
611                    })?;
612                }
613                _ => {
614                    ic_logger_msg!(log_collector, "Account does not support authorities");
615                    return Err(InstructionError::InvalidArgument);
616                }
617            }
618
619            ic_logger_msg!(log_collector, "New authority {:?}", new_authority);
620        }
621        UpgradeableLoaderInstruction::SetAuthorityChecked => {
622            if !invoke_context
623                .get_feature_set()
624                .enable_bpf_loader_set_authority_checked_ix
625            {
626                return Err(InstructionError::InvalidInstructionData);
627            }
628
629            instruction_context.check_number_of_instruction_accounts(3)?;
630            let mut account = instruction_context.try_borrow_instruction_account(0)?;
631            let present_authority_key = instruction_context.get_key_of_instruction_account(1)?;
632            let new_authority_key = instruction_context.get_key_of_instruction_account(2)?;
633
634            match account.get_state()? {
635                UpgradeableLoaderState::Buffer { authority_address } => {
636                    if authority_address.is_none() {
637                        ic_logger_msg!(log_collector, "Buffer is immutable");
638                        return Err(InstructionError::Immutable);
639                    }
640                    if authority_address != Some(*present_authority_key) {
641                        ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
642                        return Err(InstructionError::IncorrectAuthority);
643                    }
644                    if !instruction_context.is_instruction_account_signer(1)? {
645                        ic_logger_msg!(log_collector, "Buffer authority did not sign");
646                        return Err(InstructionError::MissingRequiredSignature);
647                    }
648                    if !instruction_context.is_instruction_account_signer(2)? {
649                        ic_logger_msg!(log_collector, "New authority did not sign");
650                        return Err(InstructionError::MissingRequiredSignature);
651                    }
652                    account.set_state(&UpgradeableLoaderState::Buffer {
653                        authority_address: Some(*new_authority_key),
654                    })?;
655                }
656                UpgradeableLoaderState::ProgramData {
657                    slot,
658                    upgrade_authority_address,
659                } => {
660                    if upgrade_authority_address.is_none() {
661                        ic_logger_msg!(log_collector, "Program not upgradeable");
662                        return Err(InstructionError::Immutable);
663                    }
664                    if upgrade_authority_address != Some(*present_authority_key) {
665                        ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
666                        return Err(InstructionError::IncorrectAuthority);
667                    }
668                    if !instruction_context.is_instruction_account_signer(1)? {
669                        ic_logger_msg!(log_collector, "Upgrade authority did not sign");
670                        return Err(InstructionError::MissingRequiredSignature);
671                    }
672                    if !instruction_context.is_instruction_account_signer(2)? {
673                        ic_logger_msg!(log_collector, "New authority did not sign");
674                        return Err(InstructionError::MissingRequiredSignature);
675                    }
676                    account.set_state(&UpgradeableLoaderState::ProgramData {
677                        slot,
678                        upgrade_authority_address: Some(*new_authority_key),
679                    })?;
680                }
681                _ => {
682                    ic_logger_msg!(log_collector, "Account does not support authorities");
683                    return Err(InstructionError::InvalidArgument);
684                }
685            }
686
687            ic_logger_msg!(log_collector, "New authority {:?}", new_authority_key);
688        }
689        UpgradeableLoaderInstruction::Close => {
690            instruction_context.check_number_of_instruction_accounts(2)?;
691            if instruction_context.get_index_of_instruction_account_in_transaction(0)?
692                == instruction_context.get_index_of_instruction_account_in_transaction(1)?
693            {
694                ic_logger_msg!(
695                    log_collector,
696                    "Recipient is the same as the account being closed"
697                );
698                return Err(InstructionError::InvalidArgument);
699            }
700            let mut close_account = instruction_context.try_borrow_instruction_account(0)?;
701            let close_key = *close_account.get_key();
702            let close_account_state = close_account.get_state()?;
703            close_account.set_data_length(UpgradeableLoaderState::size_of_uninitialized())?;
704            match close_account_state {
705                UpgradeableLoaderState::Uninitialized => {
706                    let mut recipient_account =
707                        instruction_context.try_borrow_instruction_account(1)?;
708                    recipient_account.checked_add_lamports(close_account.get_lamports())?;
709                    close_account.set_lamports(0)?;
710
711                    ic_logger_msg!(log_collector, "Closed Uninitialized {}", close_key);
712                }
713                UpgradeableLoaderState::Buffer { authority_address } => {
714                    instruction_context.check_number_of_instruction_accounts(3)?;
715                    drop(close_account);
716                    common_close_account(&authority_address, &instruction_context, &log_collector)?;
717
718                    ic_logger_msg!(log_collector, "Closed Buffer {}", close_key);
719                }
720                UpgradeableLoaderState::ProgramData {
721                    slot,
722                    upgrade_authority_address: authority_address,
723                } => {
724                    instruction_context.check_number_of_instruction_accounts(4)?;
725                    drop(close_account);
726                    let program_account = instruction_context.try_borrow_instruction_account(3)?;
727                    let program_key = *program_account.get_key();
728
729                    if !program_account.is_writable() {
730                        ic_logger_msg!(log_collector, "Program account is not writable");
731                        return Err(InstructionError::InvalidArgument);
732                    }
733                    if program_account.get_owner() != program_id {
734                        ic_logger_msg!(log_collector, "Program account not owned by loader");
735                        return Err(InstructionError::IncorrectProgramId);
736                    }
737                    let clock = invoke_context
738                        .environment_config
739                        .sysvar_cache()
740                        .get_clock()?;
741                    if clock.slot == slot {
742                        ic_logger_msg!(log_collector, "Program was deployed in this block already");
743                        return Err(InstructionError::InvalidArgument);
744                    }
745
746                    match program_account.get_state()? {
747                        UpgradeableLoaderState::Program {
748                            programdata_address,
749                        } => {
750                            if programdata_address != close_key {
751                                ic_logger_msg!(
752                                    log_collector,
753                                    "ProgramData account does not match ProgramData account"
754                                );
755                                return Err(InstructionError::InvalidArgument);
756                            }
757
758                            drop(program_account);
759                            common_close_account(
760                                &authority_address,
761                                &instruction_context,
762                                &log_collector,
763                            )?;
764                            let clock = invoke_context
765                                .environment_config
766                                .sysvar_cache()
767                                .get_clock()?;
768                            invoke_context
769                                .program_cache_for_tx_batch
770                                .store_modified_entry(
771                                    program_key,
772                                    Arc::new(ProgramCacheEntry::new_tombstone(
773                                        clock.slot,
774                                        ProgramCacheEntryOwner::LoaderV3,
775                                        ProgramCacheEntryType::Closed,
776                                    )),
777                                );
778                        }
779                        _ => {
780                            ic_logger_msg!(log_collector, "Invalid Program account");
781                            return Err(InstructionError::InvalidArgument);
782                        }
783                    }
784
785                    ic_logger_msg!(log_collector, "Closed Program {}", program_key);
786                }
787                _ => {
788                    ic_logger_msg!(log_collector, "Account does not support closing");
789                    return Err(InstructionError::InvalidArgument);
790                }
791            }
792        }
793        UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => {
794            common_extend_program(invoke_context, additional_bytes, false)?;
795        }
796    }
797
798    Ok(())
799}
800
801fn common_extend_program(
802    invoke_context: &mut InvokeContext,
803    additional_bytes: u32,
804    check_authority: bool,
805) -> Result<(), InstructionError> {
806    let log_collector = invoke_context.get_log_collector();
807    let transaction_context = &invoke_context.transaction_context;
808    let instruction_context = transaction_context.get_current_instruction_context()?;
809    let program_id = instruction_context.get_program_key()?;
810
811    const PROGRAM_DATA_ACCOUNT_INDEX: IndexOfAccount = 0;
812    const PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 1;
813    const AUTHORITY_ACCOUNT_INDEX: IndexOfAccount = 2;
814    // The unused `system_program_account_index` is 3 if `check_authority` and 2 otherwise.
815    let optional_payer_account_index = if check_authority { 4 } else { 3 };
816
817    if additional_bytes == 0 {
818        ic_logger_msg!(log_collector, "Additional bytes must be greater than 0");
819        return Err(InstructionError::InvalidInstructionData);
820    }
821
822    let programdata_account =
823        instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
824    let programdata_key = *programdata_account.get_key();
825
826    if program_id != programdata_account.get_owner() {
827        ic_logger_msg!(log_collector, "ProgramData owner is invalid");
828        return Err(InstructionError::InvalidAccountOwner);
829    }
830    if !programdata_account.is_writable() {
831        ic_logger_msg!(log_collector, "ProgramData is not writable");
832        return Err(InstructionError::InvalidArgument);
833    }
834
835    let program_account =
836        instruction_context.try_borrow_instruction_account(PROGRAM_ACCOUNT_INDEX)?;
837    if !program_account.is_writable() {
838        ic_logger_msg!(log_collector, "Program account is not writable");
839        return Err(InstructionError::InvalidArgument);
840    }
841    if program_account.get_owner() != program_id {
842        ic_logger_msg!(log_collector, "Program account not owned by loader");
843        return Err(InstructionError::InvalidAccountOwner);
844    }
845    let program_key = *program_account.get_key();
846    match program_account.get_state()? {
847        UpgradeableLoaderState::Program {
848            programdata_address,
849        } => {
850            if programdata_address != programdata_key {
851                ic_logger_msg!(
852                    log_collector,
853                    "Program account does not match ProgramData account"
854                );
855                return Err(InstructionError::InvalidArgument);
856            }
857        }
858        _ => {
859            ic_logger_msg!(log_collector, "Invalid Program account");
860            return Err(InstructionError::InvalidAccountData);
861        }
862    }
863    drop(program_account);
864
865    let old_len = programdata_account.get_data().len();
866    let new_len = old_len.saturating_add(additional_bytes as usize);
867    if new_len > MAX_PERMITTED_DATA_LENGTH as usize {
868        ic_logger_msg!(
869            log_collector,
870            "Extended ProgramData length of {} bytes exceeds max account data length of {} bytes",
871            new_len,
872            MAX_PERMITTED_DATA_LENGTH
873        );
874        return Err(InstructionError::InvalidRealloc);
875    }
876
877    if invoke_context
878        .get_feature_set()
879        .loader_v3_minimum_extend_program_size
880    {
881        // SIMD-0431: Minimum Extend Program Size
882        //
883        // All extensions must be >= 10 KiB in additional_bytes, unless
884        // MAX_PERMITTED_DATA_LENGTH - current_len < 10 KiB. In that case,
885        // additional_bytes must be equal to the remaining free space.
886        let headroom = (MAX_PERMITTED_DATA_LENGTH as usize).saturating_sub(old_len);
887        if additional_bytes < MINIMUM_EXTEND_PROGRAM_BYTES
888            && (additional_bytes as usize) != headroom
889        {
890            ic_logger_msg!(
891                log_collector,
892                "ExtendProgram requires a minimum of {} additional bytes or to extend to maximum \
893                 size, but only {} were requested",
894                MINIMUM_EXTEND_PROGRAM_BYTES,
895                additional_bytes,
896            );
897            return Err(InstructionError::InvalidArgument);
898        }
899    }
900
901    let clock_slot = invoke_context
902        .environment_config
903        .sysvar_cache()
904        .get_clock()
905        .map(|clock| clock.slot)?;
906
907    let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData {
908        slot,
909        upgrade_authority_address,
910    } = programdata_account.get_state()?
911    {
912        if clock_slot == slot {
913            ic_logger_msg!(log_collector, "Program was extended in this block already");
914            return Err(InstructionError::InvalidArgument);
915        }
916
917        if upgrade_authority_address.is_none() {
918            ic_logger_msg!(
919                log_collector,
920                "Cannot extend ProgramData accounts that are not upgradeable"
921            );
922            return Err(InstructionError::Immutable);
923        }
924
925        if check_authority {
926            let authority_key =
927                Some(*instruction_context.get_key_of_instruction_account(AUTHORITY_ACCOUNT_INDEX)?);
928            if upgrade_authority_address != authority_key {
929                ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
930                return Err(InstructionError::IncorrectAuthority);
931            }
932            if !instruction_context.is_instruction_account_signer(AUTHORITY_ACCOUNT_INDEX)? {
933                ic_logger_msg!(log_collector, "Upgrade authority did not sign");
934                return Err(InstructionError::MissingRequiredSignature);
935            }
936        }
937
938        upgrade_authority_address
939    } else {
940        ic_logger_msg!(log_collector, "ProgramData state is invalid");
941        return Err(InstructionError::InvalidAccountData);
942    };
943
944    let required_payment = {
945        let balance = programdata_account.get_lamports();
946        let rent = invoke_context
947            .environment_config
948            .sysvar_cache()
949            .get_rent()?;
950        let min_balance = rent.minimum_balance(new_len).max(1);
951        min_balance.saturating_sub(balance)
952    };
953
954    // Borrowed accounts need to be dropped before native_invoke_signed
955    drop(programdata_account);
956
957    // Dereference the program ID to prevent overlapping mutable/immutable borrow of invoke context
958    let program_id = *program_id;
959    if required_payment > 0 {
960        let payer_key =
961            *instruction_context.get_key_of_instruction_account(optional_payer_account_index)?;
962
963        invoke_context.native_invoke_signed(
964            system_instruction::transfer(&payer_key, &programdata_key, required_payment),
965            &[],
966        )?;
967    }
968
969    let transaction_context = &invoke_context.transaction_context;
970    let instruction_context = transaction_context.get_current_instruction_context()?;
971    let mut programdata_account =
972        instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
973    programdata_account.set_data_length(new_len)?;
974
975    let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
976
977    deploy_program!(
978        invoke_context,
979        &program_key,
980        &program_id,
981        UpgradeableLoaderState::size_of_program().saturating_add(new_len),
982        programdata_account
983            .get_data()
984            .get(programdata_data_offset..)
985            .ok_or(InstructionError::AccountDataTooSmall)?,
986        clock_slot,
987        false, // disable_sbpf_v0_v1_v2_deployment // explicitly continue to allow them for extend program
988    );
989    drop(programdata_account);
990
991    let mut programdata_account =
992        instruction_context.try_borrow_instruction_account(PROGRAM_DATA_ACCOUNT_INDEX)?;
993    programdata_account.set_state(&UpgradeableLoaderState::ProgramData {
994        slot: clock_slot,
995        upgrade_authority_address,
996    })?;
997
998    ic_logger_msg!(
999        log_collector,
1000        "Extended ProgramData account by {} bytes",
1001        additional_bytes
1002    );
1003
1004    Ok(())
1005}
1006
1007fn common_close_account(
1008    authority_address: &Option<Pubkey>,
1009    instruction_context: &InstructionContext,
1010    log_collector: &Option<Rc<RefCell<LogCollector>>>,
1011) -> Result<(), InstructionError> {
1012    if authority_address.is_none() {
1013        ic_logger_msg!(log_collector, "Account is immutable");
1014        return Err(InstructionError::Immutable);
1015    }
1016    if *authority_address != Some(*instruction_context.get_key_of_instruction_account(2)?) {
1017        ic_logger_msg!(log_collector, "Incorrect authority provided");
1018        return Err(InstructionError::IncorrectAuthority);
1019    }
1020    if !instruction_context.is_instruction_account_signer(2)? {
1021        ic_logger_msg!(log_collector, "Authority did not sign");
1022        return Err(InstructionError::MissingRequiredSignature);
1023    }
1024
1025    let mut close_account = instruction_context.try_borrow_instruction_account(0)?;
1026    let mut recipient_account = instruction_context.try_borrow_instruction_account(1)?;
1027
1028    recipient_account.checked_add_lamports(close_account.get_lamports())?;
1029    close_account.set_lamports(0)?;
1030    close_account.set_state(&UpgradeableLoaderState::Uninitialized)?;
1031    Ok(())
1032}
1033
1034#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
1035mod test_utils {
1036    #[cfg(all(feature = "svm-internal", feature = "metrics"))]
1037    use solana_program_runtime::program_metrics::LoadProgramMetrics;
1038    #[cfg(feature = "svm-internal")]
1039    use {
1040        super::*, solana_account::ReadableAccount,
1041        solana_program_runtime::loaded_programs::ProgramRuntimeEnvironment,
1042        solana_program_runtime::program_cache_entry::DELAY_VISIBILITY_SLOT_OFFSET,
1043        solana_syscalls::create_program_runtime_environment,
1044    };
1045
1046    #[cfg(feature = "svm-internal")]
1047    fn check_loader_id(id: &Pubkey) -> bool {
1048        bpf_loader::check_id(id)
1049            || bpf_loader_deprecated::check_id(id)
1050            || bpf_loader_upgradeable::check_id(id)
1051    }
1052
1053    #[cfg(feature = "svm-internal")]
1054    #[cfg_attr(feature = "svm-internal", qualifiers(pub))]
1055    fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
1056        let program_runtime_environment = create_program_runtime_environment(
1057            invoke_context.get_feature_set(),
1058            invoke_context.get_compute_budget(),
1059            false, /* deployment */
1060            false, /* debugging_features */
1061        )
1062        .unwrap();
1063        let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
1064        for index in 0..num_accounts {
1065            let account = invoke_context
1066                .transaction_context
1067                .accounts()
1068                .try_borrow(index)
1069                .expect("Failed to get the account");
1070
1071            let owner = account.owner();
1072            if check_loader_id(owner) {
1073                let programdata_data_offset = 0;
1074                let pubkey = invoke_context
1075                    .transaction_context
1076                    .get_key_of_account_at_index(index)
1077                    .expect("Failed to get account key");
1078
1079                let programdata = account
1080                    .data()
1081                    .get(programdata_data_offset.min(account.data().len())..)
1082                    .unwrap();
1083                let effective_slot = DELAY_VISIBILITY_SLOT_OFFSET;
1084                let loaded_program = ProgramCacheEntry::new(
1085                    owner,
1086                    ProgramRuntimeEnvironment::clone(&program_runtime_environment),
1087                    0,
1088                    effective_slot,
1089                    programdata,
1090                    account.data().len(),
1091                    #[cfg(feature = "metrics")]
1092                    &mut LoadProgramMetrics::default(),
1093                )
1094                .map_err(|_| InstructionError::InvalidAccountData);
1095                if let Ok(loaded_program) = loaded_program {
1096                    invoke_context
1097                        .program_cache_for_tx_batch
1098                        .store_modified_entry(*pubkey, Arc::new(loaded_program));
1099                }
1100            }
1101        }
1102    }
1103}
1104
1105#[cfg(test)]
1106mod tests {
1107    use {
1108        super::*,
1109        assert_matches::assert_matches,
1110        rand::Rng,
1111        solana_account::{
1112            AccountSharedData, ReadableAccount, WritableAccount,
1113            create_account_shared_data_for_test as create_account_for_test, state_traits::StateMut,
1114        },
1115        solana_clock::Clock,
1116        solana_epoch_schedule::EpochSchedule,
1117        solana_instruction::{AccountMeta, error::InstructionError},
1118        solana_program_runtime::{
1119            invoke_context::mock_process_instruction, loaded_programs::ProgramRuntimeEnvironment,
1120            program_metrics::ProgramStatistics, vm::calculate_heap_cost, with_mock_invoke_context,
1121        },
1122        solana_pubkey::Pubkey,
1123        solana_rent::Rent,
1124        solana_sbpf::program::{BuiltinFunctionDefinition, BuiltinProgram},
1125        solana_sdk_ids::{system_program, sysvar},
1126        solana_svm_type_overrides::sync::atomic::{AtomicU64, Ordering},
1127        std::{fs::File, io::Read, ops::Range},
1128    };
1129
1130    // 10 iterations is intentionally low: `mock_process_instruction` runs on a
1131    // single thread, so additional `shuttle::check_random` iterations validate
1132    // only the harness wiring, not concurrent interleavings. Bump this if a
1133    // future refactor introduces `shuttle::thread::spawn` inside
1134    // `mock_process_instruction`.
1135    #[cfg(feature = "shuttle-test")]
1136    const MOCK_PROCESS_RANDOM_ITERATIONS: usize = 10;
1137
1138    /// Wrapper around `mock_process_instruction` that runs under
1139    /// `shuttle::check_random` when the `shuttle-test` feature is enabled,
1140    /// providing the Shuttle scheduler context required by
1141    /// `solana-svm-type-overrides`'s shuttle-aware atomic types. With default
1142    /// features, this is a thin pass-through to `mock_process_instruction`
1143    /// with `Entrypoint::register` and an empty post-adjustment closure.
1144    ///
1145    /// `mock_process_instruction` itself is single-threaded: the only
1146    /// Shuttle-backed atomic in the access path is
1147    /// `ProgramCacheEntry::latest_access_slot` (routed to
1148    /// `shuttle::sync::atomic::AtomicU64` by `solana_svm_type_overrides`), and
1149    /// it is touched from one Shuttle thread. Iteration-to-iteration variance
1150    /// under `shuttle::check_random` is solely scheduler bookkeeping noise, so
1151    /// any iteration's captured result is equivalent. If
1152    /// `mock_process_instruction` ever spawns Shuttle threads internally,
1153    /// this last-write-wins capture must be re-evaluated.
1154    ///
1155    /// `setup` is typed as `fn(&mut InvokeContext)` (function pointer, not
1156    /// `impl Fn`) so it satisfies Shuttle's `Fn + Send + Sync + 'static` bound
1157    /// when captured by value into the inner closure. Callers must pass
1158    /// non-capturing closures or `fn` items; capturing closures will produce a
1159    /// fn-pointer coercion error at the call site.
1160    fn process_instruction_with_setup(
1161        program_id: &Pubkey,
1162        instruction_data: &[u8],
1163        transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
1164        instruction_accounts: Vec<AccountMeta>,
1165        expected_result: Result<(), InstructionError>,
1166        setup: fn(&mut InvokeContext),
1167    ) -> Vec<AccountSharedData> {
1168        #[cfg(feature = "shuttle-test")]
1169        {
1170            let program_id = *program_id;
1171            let instruction_data = instruction_data.to_vec();
1172            let result = shuttle::sync::Arc::new(shuttle::sync::Mutex::new(None));
1173            let result_for_test = shuttle::sync::Arc::clone(&result);
1174            shuttle::check_random(
1175                move || {
1176                    let accounts = mock_process_instruction(
1177                        &program_id,
1178                        &instruction_data,
1179                        transaction_accounts.clone(),
1180                        instruction_accounts.clone(),
1181                        expected_result.clone(),
1182                        Entrypoint::register,
1183                        setup,
1184                        |_invoke_context| {},
1185                    );
1186                    *result_for_test.lock().unwrap() = Some(accounts);
1187                },
1188                MOCK_PROCESS_RANDOM_ITERATIONS,
1189            );
1190
1191            // Consume the harness cell after Shuttle exits so extraction does
1192            // not call `shuttle::sync::Mutex::lock` outside the scheduler.
1193            let mut result = match shuttle::sync::Arc::try_unwrap(result) {
1194                Ok(result) => result,
1195                Err(_) => panic!("shuttle test result still has outstanding references"),
1196            };
1197            result
1198                .get_mut()
1199                .unwrap()
1200                .take()
1201                .expect("shuttle test did not produce a result")
1202        }
1203
1204        #[cfg(not(feature = "shuttle-test"))]
1205        mock_process_instruction(
1206            program_id,
1207            instruction_data,
1208            transaction_accounts,
1209            instruction_accounts,
1210            expected_result,
1211            Entrypoint::register,
1212            setup,
1213            |_invoke_context| {},
1214        )
1215    }
1216
1217    fn process_instruction(
1218        program_id: &Pubkey,
1219        instruction_data: &[u8],
1220        transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
1221        instruction_accounts: Vec<AccountMeta>,
1222        expected_result: Result<(), InstructionError>,
1223    ) -> Vec<AccountSharedData> {
1224        process_instruction_with_setup(
1225            program_id,
1226            instruction_data,
1227            transaction_accounts,
1228            instruction_accounts,
1229            expected_result,
1230            |invoke_context| {
1231                test_utils::load_all_invoked_programs(invoke_context);
1232            },
1233        )
1234    }
1235
1236    fn load_program_account_from_elf(loader_id: &Pubkey, path: &str) -> AccountSharedData {
1237        let mut file = File::open(path).expect("file open failed");
1238        let mut elf = Vec::new();
1239        file.read_to_end(&mut elf).unwrap();
1240        let rent = Rent::default();
1241        let mut program_account =
1242            AccountSharedData::new(rent.minimum_balance(elf.len()), 0, loader_id);
1243        program_account.set_data(elf);
1244        program_account.set_executable(true);
1245        program_account
1246    }
1247
1248    #[test]
1249    fn test_bpf_loader_invoke_main() {
1250        let loader_id = bpf_loader::id();
1251        let program_id = Pubkey::new_unique();
1252        let program_account =
1253            load_program_account_from_elf(&loader_id, "test_elfs/out/sbpfv3_return_ok.so");
1254        let parameter_id = Pubkey::new_unique();
1255        let parameter_account = AccountSharedData::new(1, 0, &loader_id);
1256        let parameter_meta = AccountMeta {
1257            pubkey: parameter_id,
1258            is_signer: false,
1259            is_writable: false,
1260        };
1261
1262        // Case: No program account
1263        process_instruction(
1264            &loader_id,
1265            &[],
1266            Vec::new(),
1267            Vec::new(),
1268            Err(InstructionError::UnsupportedProgramId),
1269        );
1270
1271        // Case: Only a program account
1272        process_instruction(
1273            &program_id,
1274            &[],
1275            vec![(program_id, program_account.clone())],
1276            Vec::new(),
1277            Ok(()),
1278        );
1279
1280        // Case: With program and parameter account
1281        process_instruction(
1282            &program_id,
1283            &[],
1284            vec![
1285                (program_id, program_account.clone()),
1286                (parameter_id, parameter_account.clone()),
1287            ],
1288            vec![parameter_meta.clone()],
1289            Ok(()),
1290        );
1291
1292        // Case: With duplicate accounts
1293        process_instruction(
1294            &program_id,
1295            &[],
1296            vec![
1297                (program_id, program_account.clone()),
1298                (parameter_id, parameter_account.clone()),
1299            ],
1300            vec![parameter_meta.clone(), parameter_meta],
1301            Ok(()),
1302        );
1303
1304        // Case: limited budget
1305        process_instruction_with_setup(
1306            &program_id,
1307            &[],
1308            vec![(program_id, program_account)],
1309            Vec::new(),
1310            Err(InstructionError::ProgramFailedToComplete),
1311            |invoke_context| {
1312                invoke_context.compute_meter.mock_set_remaining(0);
1313                test_utils::load_all_invoked_programs(invoke_context);
1314            },
1315        );
1316
1317        // Case: Account not a program
1318        process_instruction_with_setup(
1319            &program_id,
1320            &[],
1321            vec![(program_id, parameter_account.clone())],
1322            Vec::new(),
1323            Err(InstructionError::UnsupportedProgramId),
1324            |invoke_context| {
1325                test_utils::load_all_invoked_programs(invoke_context);
1326            },
1327        );
1328        process_instruction(
1329            &program_id,
1330            &[],
1331            vec![(program_id, parameter_account)],
1332            Vec::new(),
1333            Err(InstructionError::UnsupportedProgramId),
1334        );
1335    }
1336
1337    #[test]
1338    fn test_bpf_loader_serialize_unaligned() {
1339        let loader_id = bpf_loader_deprecated::id();
1340        let program_id = Pubkey::new_unique();
1341        let program_account =
1342            load_program_account_from_elf(&loader_id, "test_elfs/out/noop_unaligned.so");
1343        let parameter_id = Pubkey::new_unique();
1344        let parameter_account = AccountSharedData::new(1, 0, &loader_id);
1345        let parameter_meta = AccountMeta {
1346            pubkey: parameter_id,
1347            is_signer: false,
1348            is_writable: false,
1349        };
1350
1351        // Case: With program and parameter account
1352        process_instruction(
1353            &program_id,
1354            &[],
1355            vec![
1356                (program_id, program_account.clone()),
1357                (parameter_id, parameter_account.clone()),
1358            ],
1359            vec![parameter_meta.clone()],
1360            Ok(()),
1361        );
1362
1363        // Case: With duplicate accounts
1364        process_instruction(
1365            &program_id,
1366            &[],
1367            vec![
1368                (program_id, program_account),
1369                (parameter_id, parameter_account),
1370            ],
1371            vec![parameter_meta.clone(), parameter_meta],
1372            Ok(()),
1373        );
1374    }
1375
1376    #[test]
1377    fn test_bpf_loader_serialize_aligned() {
1378        let loader_id = bpf_loader::id();
1379        let program_id = Pubkey::new_unique();
1380        let program_account =
1381            load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so");
1382        let parameter_id = Pubkey::new_unique();
1383        let parameter_account = AccountSharedData::new(1, 0, &loader_id);
1384        let parameter_meta = AccountMeta {
1385            pubkey: parameter_id,
1386            is_signer: false,
1387            is_writable: false,
1388        };
1389
1390        // Case: With program and parameter account
1391        process_instruction(
1392            &program_id,
1393            &[],
1394            vec![
1395                (program_id, program_account.clone()),
1396                (parameter_id, parameter_account.clone()),
1397            ],
1398            vec![parameter_meta.clone()],
1399            Ok(()),
1400        );
1401
1402        // Case: With duplicate accounts
1403        process_instruction(
1404            &program_id,
1405            &[],
1406            vec![
1407                (program_id, program_account),
1408                (parameter_id, parameter_account),
1409            ],
1410            vec![parameter_meta.clone(), parameter_meta],
1411            Ok(()),
1412        );
1413    }
1414
1415    #[test]
1416    fn test_bpf_loader_upgradeable_initialize_buffer() {
1417        let loader_id = bpf_loader_upgradeable::id();
1418        let buffer_address = Pubkey::new_unique();
1419        let buffer_account =
1420            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
1421        let authority_address = Pubkey::new_unique();
1422        let authority_account =
1423            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
1424        let instruction_data =
1425            bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap();
1426        let instruction_accounts = vec![
1427            AccountMeta {
1428                pubkey: buffer_address,
1429                is_signer: false,
1430                is_writable: true,
1431            },
1432            AccountMeta {
1433                pubkey: authority_address,
1434                is_signer: false,
1435                is_writable: false,
1436            },
1437        ];
1438
1439        // Case: Success
1440        let accounts = process_instruction(
1441            &loader_id,
1442            &instruction_data,
1443            vec![
1444                (buffer_address, buffer_account),
1445                (authority_address, authority_account),
1446            ],
1447            instruction_accounts.clone(),
1448            Ok(()),
1449        );
1450        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
1451        assert_eq!(
1452            state,
1453            UpgradeableLoaderState::Buffer {
1454                authority_address: Some(authority_address)
1455            }
1456        );
1457
1458        // Case: Already initialized
1459        let accounts = process_instruction(
1460            &loader_id,
1461            &instruction_data,
1462            vec![
1463                (buffer_address, accounts.first().unwrap().clone()),
1464                (authority_address, accounts.get(1).unwrap().clone()),
1465            ],
1466            instruction_accounts,
1467            Err(InstructionError::AccountAlreadyInitialized),
1468        );
1469        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
1470        assert_eq!(
1471            state,
1472            UpgradeableLoaderState::Buffer {
1473                authority_address: Some(authority_address)
1474            }
1475        );
1476    }
1477
1478    #[test]
1479    fn test_bpf_loader_upgradeable_write() {
1480        let loader_id = bpf_loader_upgradeable::id();
1481        let buffer_address = Pubkey::new_unique();
1482        let mut buffer_account =
1483            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
1484        let instruction_accounts = vec![
1485            AccountMeta {
1486                pubkey: buffer_address,
1487                is_signer: false,
1488                is_writable: true,
1489            },
1490            AccountMeta {
1491                pubkey: buffer_address,
1492                is_signer: true,
1493                is_writable: false,
1494            },
1495        ];
1496
1497        // Case: Not initialized
1498        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1499            offset: 0,
1500            bytes: vec![42; 9],
1501        })
1502        .unwrap();
1503        process_instruction(
1504            &loader_id,
1505            &instruction,
1506            vec![(buffer_address, buffer_account.clone())],
1507            instruction_accounts.clone(),
1508            Err(InstructionError::InvalidAccountData),
1509        );
1510
1511        // Case: Write entire buffer
1512        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1513            offset: 0,
1514            bytes: vec![42; 9],
1515        })
1516        .unwrap();
1517        buffer_account
1518            .set_state(&UpgradeableLoaderState::Buffer {
1519                authority_address: Some(buffer_address),
1520            })
1521            .unwrap();
1522        let accounts = process_instruction(
1523            &loader_id,
1524            &instruction,
1525            vec![(buffer_address, buffer_account.clone())],
1526            instruction_accounts.clone(),
1527            Ok(()),
1528        );
1529        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
1530        assert_eq!(
1531            state,
1532            UpgradeableLoaderState::Buffer {
1533                authority_address: Some(buffer_address)
1534            }
1535        );
1536        assert_eq!(
1537            &accounts
1538                .first()
1539                .unwrap()
1540                .data()
1541                .get(UpgradeableLoaderState::size_of_buffer_metadata()..)
1542                .unwrap(),
1543            &[42; 9]
1544        );
1545
1546        // Case: Write portion of the buffer
1547        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1548            offset: 3,
1549            bytes: vec![42; 6],
1550        })
1551        .unwrap();
1552        let mut buffer_account =
1553            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(9), &loader_id);
1554        buffer_account
1555            .set_state(&UpgradeableLoaderState::Buffer {
1556                authority_address: Some(buffer_address),
1557            })
1558            .unwrap();
1559        let accounts = process_instruction(
1560            &loader_id,
1561            &instruction,
1562            vec![(buffer_address, buffer_account.clone())],
1563            instruction_accounts.clone(),
1564            Ok(()),
1565        );
1566        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
1567        assert_eq!(
1568            state,
1569            UpgradeableLoaderState::Buffer {
1570                authority_address: Some(buffer_address)
1571            }
1572        );
1573        assert_eq!(
1574            &accounts
1575                .first()
1576                .unwrap()
1577                .data()
1578                .get(UpgradeableLoaderState::size_of_buffer_metadata()..)
1579                .unwrap(),
1580            &[0, 0, 0, 42, 42, 42, 42, 42, 42]
1581        );
1582
1583        // Case: overflow size
1584        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1585            offset: 0,
1586            bytes: vec![42; 10],
1587        })
1588        .unwrap();
1589        buffer_account
1590            .set_state(&UpgradeableLoaderState::Buffer {
1591                authority_address: Some(buffer_address),
1592            })
1593            .unwrap();
1594        process_instruction(
1595            &loader_id,
1596            &instruction,
1597            vec![(buffer_address, buffer_account.clone())],
1598            instruction_accounts.clone(),
1599            Err(InstructionError::AccountDataTooSmall),
1600        );
1601
1602        // Case: overflow offset
1603        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1604            offset: 1,
1605            bytes: vec![42; 9],
1606        })
1607        .unwrap();
1608        buffer_account
1609            .set_state(&UpgradeableLoaderState::Buffer {
1610                authority_address: Some(buffer_address),
1611            })
1612            .unwrap();
1613        process_instruction(
1614            &loader_id,
1615            &instruction,
1616            vec![(buffer_address, buffer_account.clone())],
1617            instruction_accounts.clone(),
1618            Err(InstructionError::AccountDataTooSmall),
1619        );
1620
1621        // Case: Not signed
1622        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1623            offset: 0,
1624            bytes: vec![42; 9],
1625        })
1626        .unwrap();
1627        buffer_account
1628            .set_state(&UpgradeableLoaderState::Buffer {
1629                authority_address: Some(buffer_address),
1630            })
1631            .unwrap();
1632        process_instruction(
1633            &loader_id,
1634            &instruction,
1635            vec![(buffer_address, buffer_account.clone())],
1636            vec![
1637                AccountMeta {
1638                    pubkey: buffer_address,
1639                    is_signer: false,
1640                    is_writable: false,
1641                },
1642                AccountMeta {
1643                    pubkey: buffer_address,
1644                    is_signer: false,
1645                    is_writable: false,
1646                },
1647            ],
1648            Err(InstructionError::MissingRequiredSignature),
1649        );
1650
1651        // Case: wrong authority
1652        let authority_address = Pubkey::new_unique();
1653        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1654            offset: 1,
1655            bytes: vec![42; 9],
1656        })
1657        .unwrap();
1658        buffer_account
1659            .set_state(&UpgradeableLoaderState::Buffer {
1660                authority_address: Some(buffer_address),
1661            })
1662            .unwrap();
1663        process_instruction(
1664            &loader_id,
1665            &instruction,
1666            vec![
1667                (buffer_address, buffer_account.clone()),
1668                (authority_address, buffer_account.clone()),
1669            ],
1670            vec![
1671                AccountMeta {
1672                    pubkey: buffer_address,
1673                    is_signer: false,
1674                    is_writable: false,
1675                },
1676                AccountMeta {
1677                    pubkey: authority_address,
1678                    is_signer: false,
1679                    is_writable: false,
1680                },
1681            ],
1682            Err(InstructionError::IncorrectAuthority),
1683        );
1684
1685        // Case: None authority
1686        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
1687            offset: 1,
1688            bytes: vec![42; 9],
1689        })
1690        .unwrap();
1691        buffer_account
1692            .set_state(&UpgradeableLoaderState::Buffer {
1693                authority_address: None,
1694            })
1695            .unwrap();
1696        process_instruction(
1697            &loader_id,
1698            &instruction,
1699            vec![(buffer_address, buffer_account.clone())],
1700            instruction_accounts,
1701            Err(InstructionError::Immutable),
1702        );
1703    }
1704
1705    fn truncate_data(account: &mut AccountSharedData, len: usize) {
1706        let mut data = account.data().to_vec();
1707        data.truncate(len);
1708        account.set_data(data);
1709    }
1710
1711    #[test]
1712    fn test_bpf_loader_upgradeable_upgrade() {
1713        let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
1714        let mut elf_orig = Vec::new();
1715        file.read_to_end(&mut elf_orig).unwrap();
1716        let mut file = File::open("test_elfs/out/sbpfv3_return_err.so").expect("file open failed");
1717        let mut elf_new = Vec::new();
1718        file.read_to_end(&mut elf_new).unwrap();
1719        assert_ne!(elf_orig.len(), elf_new.len());
1720        const SLOT: u64 = 42;
1721        let buffer_address = Pubkey::new_unique();
1722        let upgrade_authority_address = Pubkey::new_unique();
1723
1724        fn get_accounts(
1725            buffer_address: &Pubkey,
1726            buffer_authority: &Pubkey,
1727            upgrade_authority_address: &Pubkey,
1728            elf_orig: &[u8],
1729            elf_new: &[u8],
1730        ) -> (Vec<(Pubkey, AccountSharedData)>, Vec<AccountMeta>) {
1731            let loader_id = bpf_loader_upgradeable::id();
1732            let program_address = Pubkey::new_unique();
1733            let spill_address = Pubkey::new_unique();
1734            let rent = Rent::default();
1735            let min_program_balance =
1736                1.max(rent.minimum_balance(UpgradeableLoaderState::size_of_program()));
1737            let min_programdata_balance = 1.max(rent.minimum_balance(
1738                UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
1739            ));
1740            let (programdata_address, _) =
1741                Pubkey::find_program_address(&[program_address.as_ref()], &loader_id);
1742            let mut buffer_account = AccountSharedData::new(
1743                1,
1744                UpgradeableLoaderState::size_of_buffer(elf_new.len()),
1745                &bpf_loader_upgradeable::id(),
1746            );
1747            buffer_account
1748                .set_state(&UpgradeableLoaderState::Buffer {
1749                    authority_address: Some(*buffer_authority),
1750                })
1751                .unwrap();
1752            buffer_account
1753                .data_as_mut_slice()
1754                .get_mut(UpgradeableLoaderState::size_of_buffer_metadata()..)
1755                .unwrap()
1756                .copy_from_slice(elf_new);
1757            let mut programdata_account = AccountSharedData::new(
1758                min_programdata_balance,
1759                UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
1760                &bpf_loader_upgradeable::id(),
1761            );
1762            programdata_account
1763                .set_state(&UpgradeableLoaderState::ProgramData {
1764                    slot: SLOT,
1765                    upgrade_authority_address: Some(*upgrade_authority_address),
1766                })
1767                .unwrap();
1768            let mut program_account = AccountSharedData::new(
1769                min_program_balance,
1770                UpgradeableLoaderState::size_of_program(),
1771                &bpf_loader_upgradeable::id(),
1772            );
1773            program_account.set_executable(true);
1774            program_account
1775                .set_state(&UpgradeableLoaderState::Program {
1776                    programdata_address,
1777                })
1778                .unwrap();
1779            let spill_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
1780            let rent_account = create_account_for_test(&rent);
1781            let clock_account = create_account_for_test(&Clock {
1782                slot: SLOT.saturating_add(1),
1783                ..Clock::default()
1784            });
1785            let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
1786            let transaction_accounts = vec![
1787                (programdata_address, programdata_account),
1788                (program_address, program_account),
1789                (*buffer_address, buffer_account),
1790                (spill_address, spill_account),
1791                (sysvar::rent::id(), rent_account),
1792                (sysvar::clock::id(), clock_account),
1793                (*upgrade_authority_address, upgrade_authority_account),
1794            ];
1795            let instruction_accounts = vec![
1796                AccountMeta {
1797                    pubkey: programdata_address,
1798                    is_signer: false,
1799                    is_writable: true,
1800                },
1801                AccountMeta {
1802                    pubkey: program_address,
1803                    is_signer: false,
1804                    is_writable: true,
1805                },
1806                AccountMeta {
1807                    pubkey: *buffer_address,
1808                    is_signer: false,
1809                    is_writable: true,
1810                },
1811                AccountMeta {
1812                    pubkey: spill_address,
1813                    is_signer: false,
1814                    is_writable: true,
1815                },
1816                AccountMeta {
1817                    pubkey: sysvar::rent::id(),
1818                    is_signer: false,
1819                    is_writable: false,
1820                },
1821                AccountMeta {
1822                    pubkey: sysvar::clock::id(),
1823                    is_signer: false,
1824                    is_writable: false,
1825                },
1826                AccountMeta {
1827                    pubkey: *upgrade_authority_address,
1828                    is_signer: true,
1829                    is_writable: false,
1830                },
1831            ];
1832            (transaction_accounts, instruction_accounts)
1833        }
1834
1835        fn process_instruction(
1836            transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
1837            instruction_accounts: Vec<AccountMeta>,
1838            expected_result: Result<(), InstructionError>,
1839        ) -> Vec<AccountSharedData> {
1840            let instruction_data =
1841                bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap();
1842            process_instruction_with_setup(
1843                &bpf_loader_upgradeable::id(),
1844                &instruction_data,
1845                transaction_accounts,
1846                instruction_accounts,
1847                expected_result,
1848                |_invoke_context| {},
1849            )
1850        }
1851
1852        // Case: Success
1853        let (transaction_accounts, instruction_accounts) = get_accounts(
1854            &buffer_address,
1855            &upgrade_authority_address,
1856            &upgrade_authority_address,
1857            &elf_orig,
1858            &elf_new,
1859        );
1860        let accounts = process_instruction(transaction_accounts, instruction_accounts, Ok(()));
1861        let min_programdata_balance = Rent::default().minimum_balance(
1862            UpgradeableLoaderState::size_of_programdata(elf_orig.len().max(elf_new.len())),
1863        );
1864        assert_eq!(
1865            min_programdata_balance,
1866            accounts.first().unwrap().lamports()
1867        );
1868        assert_eq!(0, accounts.get(2).unwrap().lamports());
1869        assert_eq!(1, accounts.get(3).unwrap().lamports());
1870        assert_eq!(
1871            UpgradeableLoaderState::size_of_buffer(0),
1872            accounts.get(2).unwrap().data().len()
1873        );
1874        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
1875        assert_eq!(
1876            state,
1877            UpgradeableLoaderState::ProgramData {
1878                slot: SLOT.saturating_add(1),
1879                upgrade_authority_address: Some(upgrade_authority_address)
1880            }
1881        );
1882        for (i, byte) in accounts
1883            .first()
1884            .unwrap()
1885            .data()
1886            .get(
1887                UpgradeableLoaderState::size_of_programdata_metadata()
1888                    ..UpgradeableLoaderState::size_of_programdata(elf_new.len()),
1889            )
1890            .unwrap()
1891            .iter()
1892            .enumerate()
1893        {
1894            assert_eq!(*elf_new.get(i).unwrap(), *byte);
1895        }
1896
1897        // Case: not upgradable
1898        let (mut transaction_accounts, instruction_accounts) = get_accounts(
1899            &buffer_address,
1900            &upgrade_authority_address,
1901            &upgrade_authority_address,
1902            &elf_orig,
1903            &elf_new,
1904        );
1905        transaction_accounts
1906            .get_mut(0)
1907            .unwrap()
1908            .1
1909            .set_state(&UpgradeableLoaderState::ProgramData {
1910                slot: SLOT,
1911                upgrade_authority_address: None,
1912            })
1913            .unwrap();
1914        process_instruction(
1915            transaction_accounts,
1916            instruction_accounts,
1917            Err(InstructionError::Immutable),
1918        );
1919
1920        // Case: wrong authority
1921        let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
1922            &buffer_address,
1923            &upgrade_authority_address,
1924            &upgrade_authority_address,
1925            &elf_orig,
1926            &elf_new,
1927        );
1928        let invalid_upgrade_authority_address = Pubkey::new_unique();
1929        transaction_accounts.get_mut(6).unwrap().0 = invalid_upgrade_authority_address;
1930        instruction_accounts.get_mut(6).unwrap().pubkey = invalid_upgrade_authority_address;
1931        process_instruction(
1932            transaction_accounts,
1933            instruction_accounts,
1934            Err(InstructionError::IncorrectAuthority),
1935        );
1936
1937        // Case: authority did not sign
1938        let (transaction_accounts, mut instruction_accounts) = get_accounts(
1939            &buffer_address,
1940            &upgrade_authority_address,
1941            &upgrade_authority_address,
1942            &elf_orig,
1943            &elf_new,
1944        );
1945        instruction_accounts.get_mut(6).unwrap().is_signer = false;
1946        process_instruction(
1947            transaction_accounts,
1948            instruction_accounts,
1949            Err(InstructionError::MissingRequiredSignature),
1950        );
1951
1952        // Case: Buffer account and spill account alias
1953        let (transaction_accounts, mut instruction_accounts) = get_accounts(
1954            &buffer_address,
1955            &upgrade_authority_address,
1956            &upgrade_authority_address,
1957            &elf_orig,
1958            &elf_new,
1959        );
1960        *instruction_accounts.get_mut(3).unwrap() = instruction_accounts.get(2).unwrap().clone();
1961        process_instruction(
1962            transaction_accounts,
1963            instruction_accounts,
1964            Err(InstructionError::AccountBorrowFailed),
1965        );
1966
1967        // Case: Programdata account and spill account alias
1968        let (transaction_accounts, mut instruction_accounts) = get_accounts(
1969            &buffer_address,
1970            &upgrade_authority_address,
1971            &upgrade_authority_address,
1972            &elf_orig,
1973            &elf_new,
1974        );
1975        *instruction_accounts.get_mut(3).unwrap() = instruction_accounts.first().unwrap().clone();
1976        process_instruction(
1977            transaction_accounts,
1978            instruction_accounts,
1979            Err(InstructionError::AccountBorrowFailed),
1980        );
1981
1982        // Case: Program account not a program
1983        let (transaction_accounts, mut instruction_accounts) = get_accounts(
1984            &buffer_address,
1985            &upgrade_authority_address,
1986            &upgrade_authority_address,
1987            &elf_orig,
1988            &elf_new,
1989        );
1990        *instruction_accounts.get_mut(1).unwrap() = instruction_accounts.get(2).unwrap().clone();
1991        let instruction_data = bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap();
1992
1993        process_instruction_with_setup(
1994            &bpf_loader_upgradeable::id(),
1995            &instruction_data,
1996            transaction_accounts.clone(),
1997            instruction_accounts.clone(),
1998            Err(InstructionError::InvalidAccountData),
1999            |invoke_context| {
2000                test_utils::load_all_invoked_programs(invoke_context);
2001            },
2002        );
2003        process_instruction(
2004            transaction_accounts.clone(),
2005            instruction_accounts.clone(),
2006            Err(InstructionError::InvalidAccountData),
2007        );
2008
2009        // Case: Program account now owned by loader
2010        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2011            &buffer_address,
2012            &upgrade_authority_address,
2013            &upgrade_authority_address,
2014            &elf_orig,
2015            &elf_new,
2016        );
2017        transaction_accounts
2018            .get_mut(1)
2019            .unwrap()
2020            .1
2021            .set_owner(Pubkey::new_unique());
2022        process_instruction(
2023            transaction_accounts,
2024            instruction_accounts,
2025            Err(InstructionError::IncorrectProgramId),
2026        );
2027
2028        // Case: Program account not writable
2029        let (transaction_accounts, mut instruction_accounts) = get_accounts(
2030            &buffer_address,
2031            &upgrade_authority_address,
2032            &upgrade_authority_address,
2033            &elf_orig,
2034            &elf_new,
2035        );
2036        instruction_accounts.get_mut(1).unwrap().is_writable = false;
2037        process_instruction(
2038            transaction_accounts,
2039            instruction_accounts,
2040            Err(InstructionError::InvalidArgument),
2041        );
2042
2043        // Case: Program account not initialized
2044        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2045            &buffer_address,
2046            &upgrade_authority_address,
2047            &upgrade_authority_address,
2048            &elf_orig,
2049            &elf_new,
2050        );
2051        transaction_accounts
2052            .get_mut(1)
2053            .unwrap()
2054            .1
2055            .set_state(&UpgradeableLoaderState::Uninitialized)
2056            .unwrap();
2057        process_instruction(
2058            transaction_accounts,
2059            instruction_accounts,
2060            Err(InstructionError::InvalidAccountData),
2061        );
2062
2063        // Case: Program ProgramData account mismatch
2064        let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
2065            &buffer_address,
2066            &upgrade_authority_address,
2067            &upgrade_authority_address,
2068            &elf_orig,
2069            &elf_new,
2070        );
2071        let invalid_programdata_address = Pubkey::new_unique();
2072        transaction_accounts.get_mut(0).unwrap().0 = invalid_programdata_address;
2073        instruction_accounts.get_mut(0).unwrap().pubkey = invalid_programdata_address;
2074        process_instruction(
2075            transaction_accounts,
2076            instruction_accounts,
2077            Err(InstructionError::InvalidArgument),
2078        );
2079
2080        // Case: Buffer account not initialized
2081        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2082            &buffer_address,
2083            &upgrade_authority_address,
2084            &upgrade_authority_address,
2085            &elf_orig,
2086            &elf_new,
2087        );
2088        transaction_accounts
2089            .get_mut(2)
2090            .unwrap()
2091            .1
2092            .set_state(&UpgradeableLoaderState::Uninitialized)
2093            .unwrap();
2094        process_instruction(
2095            transaction_accounts,
2096            instruction_accounts,
2097            Err(InstructionError::InvalidArgument),
2098        );
2099
2100        // Case: Buffer account not writable
2101        for buffer_balance in [0, 1_000_000, 15 * 1_000_000_000] {
2102            let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
2103                &buffer_address,
2104                &upgrade_authority_address,
2105                &upgrade_authority_address,
2106                &elf_orig,
2107                &elf_new,
2108            );
2109            transaction_accounts
2110                .get_mut(2)
2111                .unwrap()
2112                .1
2113                .set_lamports(buffer_balance);
2114            instruction_accounts.get_mut(2).unwrap().is_writable = false;
2115            process_instruction(
2116                transaction_accounts,
2117                instruction_accounts,
2118                Err(InstructionError::InvalidArgument),
2119            );
2120        }
2121
2122        // Case: Buffer account not owned by loader: lamports scenario
2123        //
2124        // In `Upgrade`, the buffer's lamports are used to fund the additional
2125        // programdata rent directly, with the rest spilled to the spill
2126        // account. Then, the buffer's data is set to `size_of_buffer(0)`.
2127        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2128            &buffer_address,
2129            &upgrade_authority_address,
2130            &upgrade_authority_address,
2131            &elf_orig,
2132            &elf_new,
2133        );
2134        {
2135            // Let's make sure the programdata requires a top-up.
2136            let required_rent = |elf_len| {
2137                Rent::default()
2138                    .minimum_balance(UpgradeableLoaderState::size_of_programdata(elf_len))
2139            };
2140            let rent_orig = required_rent(elf_orig.len());
2141            let rent_new = required_rent(elf_new.len());
2142            let programdata = &mut transaction_accounts.first_mut().unwrap().1;
2143            programdata.set_lamports(rent_orig);
2144            let buffer = &mut transaction_accounts.get_mut(2).unwrap().1;
2145            buffer.set_owner(Pubkey::new_unique());
2146            buffer.set_lamports(rent_new);
2147        }
2148        process_instruction(
2149            transaction_accounts,
2150            instruction_accounts,
2151            Err(InstructionError::IncorrectProgramId),
2152        );
2153
2154        // Case: Buffer account not owned by loader: shrink scenario
2155        //
2156        // Same as the above case, but give the buffer a lamports balance of
2157        // `0`, rendering its balance "unchanged" by the spill operation.
2158        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2159            &buffer_address,
2160            &upgrade_authority_address,
2161            &upgrade_authority_address,
2162            &elf_orig,
2163            &elf_new,
2164        );
2165        {
2166            // Set the buffer's lamports to zero.
2167            let buffer = &mut transaction_accounts.get_mut(2).unwrap().1;
2168            buffer.set_owner(Pubkey::new_unique());
2169            buffer.set_lamports(0);
2170        }
2171        process_instruction(
2172            transaction_accounts,
2173            instruction_accounts,
2174            Err(InstructionError::IncorrectProgramId),
2175        );
2176
2177        // Case: Buffer account not owned by loader: no-op scenario
2178        //
2179        // Same as the above case, but also truncate the buffer's data to
2180        // `size_of_buffer(0)` - just the buffer metadata, no ELF - rendering
2181        // the closing resize "unchanged" as well.
2182        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2183            &buffer_address,
2184            &upgrade_authority_address,
2185            &upgrade_authority_address,
2186            &elf_orig,
2187            &elf_new,
2188        );
2189        {
2190            // Empty the buffer (metadata only) and zero its lamports.
2191            let buffer = &mut transaction_accounts.get_mut(2).unwrap().1;
2192            buffer.set_owner(Pubkey::new_unique());
2193            buffer.set_lamports(0);
2194            truncate_data(buffer, UpgradeableLoaderState::size_of_buffer(0));
2195        }
2196        process_instruction(
2197            transaction_accounts,
2198            instruction_accounts,
2199            Err(InstructionError::IncorrectProgramId),
2200        );
2201
2202        // Case: Buffer account too big
2203        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2204            &buffer_address,
2205            &upgrade_authority_address,
2206            &upgrade_authority_address,
2207            &elf_orig,
2208            &elf_new,
2209        );
2210        transaction_accounts.get_mut(2).unwrap().1 = AccountSharedData::new(
2211            1,
2212            UpgradeableLoaderState::size_of_buffer(
2213                elf_orig.len().max(elf_new.len()).saturating_add(1),
2214            ),
2215            &bpf_loader_upgradeable::id(),
2216        );
2217        transaction_accounts
2218            .get_mut(2)
2219            .unwrap()
2220            .1
2221            .set_state(&UpgradeableLoaderState::Buffer {
2222                authority_address: Some(upgrade_authority_address),
2223            })
2224            .unwrap();
2225        process_instruction(
2226            transaction_accounts,
2227            instruction_accounts,
2228            Err(InstructionError::AccountDataTooSmall),
2229        );
2230
2231        // Case: Buffer account too small
2232        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2233            &buffer_address,
2234            &upgrade_authority_address,
2235            &upgrade_authority_address,
2236            &elf_orig,
2237            &elf_new,
2238        );
2239        transaction_accounts
2240            .get_mut(2)
2241            .unwrap()
2242            .1
2243            .set_state(&UpgradeableLoaderState::Buffer {
2244                authority_address: Some(upgrade_authority_address),
2245            })
2246            .unwrap();
2247        truncate_data(&mut transaction_accounts.get_mut(2).unwrap().1, 5);
2248        process_instruction(
2249            transaction_accounts,
2250            instruction_accounts,
2251            Err(InstructionError::InvalidAccountData),
2252        );
2253
2254        // Case: Mismatched buffer and program authority
2255        let (transaction_accounts, instruction_accounts) = get_accounts(
2256            &buffer_address,
2257            &buffer_address,
2258            &upgrade_authority_address,
2259            &elf_orig,
2260            &elf_new,
2261        );
2262        process_instruction(
2263            transaction_accounts,
2264            instruction_accounts,
2265            Err(InstructionError::IncorrectAuthority),
2266        );
2267
2268        // Case: No buffer authority
2269        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2270            &buffer_address,
2271            &buffer_address,
2272            &upgrade_authority_address,
2273            &elf_orig,
2274            &elf_new,
2275        );
2276        transaction_accounts
2277            .get_mut(2)
2278            .unwrap()
2279            .1
2280            .set_state(&UpgradeableLoaderState::Buffer {
2281                authority_address: None,
2282            })
2283            .unwrap();
2284        process_instruction(
2285            transaction_accounts,
2286            instruction_accounts,
2287            Err(InstructionError::IncorrectAuthority),
2288        );
2289
2290        // Case: No buffer and program authority
2291        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2292            &buffer_address,
2293            &buffer_address,
2294            &upgrade_authority_address,
2295            &elf_orig,
2296            &elf_new,
2297        );
2298        transaction_accounts
2299            .get_mut(0)
2300            .unwrap()
2301            .1
2302            .set_state(&UpgradeableLoaderState::ProgramData {
2303                slot: SLOT,
2304                upgrade_authority_address: None,
2305            })
2306            .unwrap();
2307        transaction_accounts
2308            .get_mut(2)
2309            .unwrap()
2310            .1
2311            .set_state(&UpgradeableLoaderState::Buffer {
2312                authority_address: None,
2313            })
2314            .unwrap();
2315        process_instruction(
2316            transaction_accounts,
2317            instruction_accounts,
2318            Err(InstructionError::IncorrectAuthority),
2319        );
2320
2321        // Case: Upgrade to SBPFv0
2322        let mut file =
2323            File::open("test_elfs/out/sbpfv0_verifier_err.so").expect("file open failed");
2324        let mut elf_new = Vec::new();
2325        file.read_to_end(&mut elf_new).unwrap();
2326        let (transaction_accounts, instruction_accounts) = get_accounts(
2327            &buffer_address,
2328            &upgrade_authority_address,
2329            &upgrade_authority_address,
2330            &elf_orig,
2331            &elf_new,
2332        );
2333        process_instruction(
2334            transaction_accounts,
2335            instruction_accounts,
2336            Err(InstructionError::InvalidAccountData),
2337        );
2338    }
2339
2340    #[test]
2341    fn test_bpf_loader_upgradeable_deploy_with_max_data_len() {
2342        let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
2343        let mut elf = Vec::new();
2344        file.read_to_end(&mut elf).unwrap();
2345        const SLOT: u64 = 42;
2346        let payer_address = Pubkey::new_unique();
2347        let buffer_address = Pubkey::new_unique();
2348        let upgrade_authority_address = Pubkey::new_unique();
2349
2350        fn get_accounts(
2351            payer_address: &Pubkey,
2352            buffer_address: &Pubkey,
2353            buffer_authority: &Pubkey,
2354            upgrade_authority_address: &Pubkey,
2355            elf: &[u8],
2356        ) -> (Vec<(Pubkey, AccountSharedData)>, Vec<AccountMeta>) {
2357            let loader_id = bpf_loader_upgradeable::id();
2358            let program_address = Pubkey::new_unique();
2359            let rent = Rent::default();
2360            let min_program_balance =
2361                1.max(rent.minimum_balance(UpgradeableLoaderState::size_of_program()));
2362            let min_programdata_balance =
2363                1.max(rent.minimum_balance(UpgradeableLoaderState::size_of_programdata(elf.len())));
2364            let (programdata_address, _) =
2365                Pubkey::find_program_address(&[program_address.as_ref()], &loader_id);
2366            let mut buffer_account = AccountSharedData::new(
2367                1,
2368                UpgradeableLoaderState::size_of_buffer(elf.len()),
2369                &bpf_loader_upgradeable::id(),
2370            );
2371            buffer_account
2372                .set_state(&UpgradeableLoaderState::Buffer {
2373                    authority_address: Some(*buffer_authority),
2374                })
2375                .unwrap();
2376            buffer_account
2377                .data_as_mut_slice()
2378                .get_mut(UpgradeableLoaderState::size_of_buffer_metadata()..)
2379                .unwrap()
2380                .copy_from_slice(elf);
2381            let programdata_account = AccountSharedData::new(0, 0, &system_program::id());
2382            let mut program_account = AccountSharedData::new(
2383                min_program_balance,
2384                UpgradeableLoaderState::size_of_program(),
2385                &bpf_loader_upgradeable::id(),
2386            );
2387            program_account
2388                .set_state(&UpgradeableLoaderState::Uninitialized)
2389                .unwrap();
2390            let payer_account = AccountSharedData::new(
2391                min_programdata_balance.saturating_add(1),
2392                0,
2393                &system_program::id(),
2394            );
2395            let rent_account = create_account_for_test(&rent);
2396            let clock_account = create_account_for_test(&Clock {
2397                slot: SLOT,
2398                ..Clock::default()
2399            });
2400            let system_program_account = AccountSharedData::new(0, 0, &native_loader::id());
2401            let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
2402            let transaction_accounts = vec![
2403                (*payer_address, payer_account),
2404                (programdata_address, programdata_account),
2405                (program_address, program_account),
2406                (*buffer_address, buffer_account),
2407                (sysvar::rent::id(), rent_account),
2408                (sysvar::clock::id(), clock_account),
2409                (system_program::id(), system_program_account),
2410                (*upgrade_authority_address, upgrade_authority_account),
2411            ];
2412            let instruction_accounts = vec![
2413                AccountMeta {
2414                    pubkey: *payer_address,
2415                    is_signer: true,
2416                    is_writable: true,
2417                },
2418                AccountMeta {
2419                    pubkey: programdata_address,
2420                    is_signer: false,
2421                    is_writable: true,
2422                },
2423                AccountMeta {
2424                    pubkey: program_address,
2425                    is_signer: false,
2426                    is_writable: true,
2427                },
2428                AccountMeta {
2429                    pubkey: *buffer_address,
2430                    is_signer: false,
2431                    is_writable: true,
2432                },
2433                AccountMeta {
2434                    pubkey: sysvar::rent::id(),
2435                    is_signer: false,
2436                    is_writable: false,
2437                },
2438                AccountMeta {
2439                    pubkey: sysvar::clock::id(),
2440                    is_signer: false,
2441                    is_writable: false,
2442                },
2443                AccountMeta {
2444                    pubkey: system_program::id(),
2445                    is_signer: false,
2446                    is_writable: false,
2447                },
2448                AccountMeta {
2449                    pubkey: *upgrade_authority_address,
2450                    is_signer: true,
2451                    is_writable: false,
2452                },
2453            ];
2454            (transaction_accounts, instruction_accounts)
2455        }
2456
2457        fn process_instruction(
2458            max_data_len: usize,
2459            transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
2460            instruction_accounts: Vec<AccountMeta>,
2461            expected_result: Result<(), InstructionError>,
2462        ) -> Vec<AccountSharedData> {
2463            let instruction_data =
2464                bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
2465                    max_data_len,
2466                })
2467                .unwrap();
2468            process_instruction_with_setup(
2469                &bpf_loader_upgradeable::id(),
2470                &instruction_data,
2471                transaction_accounts,
2472                instruction_accounts,
2473                expected_result,
2474                |invoke_context| {
2475                    // Register the system program for CPI support.
2476                    invoke_context.program_cache_for_tx_batch.replenish(
2477                        system_program::id(),
2478                        Arc::new(ProgramCacheEntry::new_builtin(
2479                            0,
2480                            0,
2481                            solana_system_program::system_processor::Entrypoint::register,
2482                        )),
2483                    );
2484                },
2485            )
2486        }
2487
2488        // Case: Success
2489        let (transaction_accounts, instruction_accounts) = get_accounts(
2490            &payer_address,
2491            &buffer_address,
2492            &upgrade_authority_address,
2493            &upgrade_authority_address,
2494            &elf,
2495        );
2496        let programdata_address = instruction_accounts.get(1).unwrap().pubkey;
2497        let accounts = process_instruction(
2498            elf.len(),
2499            transaction_accounts,
2500            instruction_accounts,
2501            Ok(()),
2502        );
2503        let min_programdata_balance =
2504            Rent::default().minimum_balance(UpgradeableLoaderState::size_of_programdata(elf.len()));
2505        assert_eq!(min_programdata_balance, accounts.get(1).unwrap().lamports());
2506        assert_eq!(2, accounts.first().unwrap().lamports());
2507        assert_eq!(0, accounts.get(3).unwrap().lamports());
2508        assert_eq!(
2509            UpgradeableLoaderState::size_of_buffer(0),
2510            accounts.get(3).unwrap().data().len()
2511        );
2512        let state: UpgradeableLoaderState = accounts.get(1).unwrap().state().unwrap();
2513        assert_eq!(
2514            state,
2515            UpgradeableLoaderState::ProgramData {
2516                slot: SLOT,
2517                upgrade_authority_address: Some(upgrade_authority_address),
2518            }
2519        );
2520        for (i, byte) in accounts
2521            .get(1)
2522            .unwrap()
2523            .data()
2524            .get(
2525                UpgradeableLoaderState::size_of_programdata_metadata()
2526                    ..UpgradeableLoaderState::size_of_programdata(elf.len()),
2527            )
2528            .unwrap()
2529            .iter()
2530            .enumerate()
2531        {
2532            assert_eq!(*elf.get(i).unwrap(), *byte);
2533        }
2534        let state: UpgradeableLoaderState = accounts.get(2).unwrap().state().unwrap();
2535        assert_eq!(
2536            state,
2537            UpgradeableLoaderState::Program {
2538                programdata_address,
2539            }
2540        );
2541        assert!(accounts.get(2).unwrap().executable());
2542
2543        // Case: wrong authority
2544        let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
2545            &payer_address,
2546            &buffer_address,
2547            &upgrade_authority_address,
2548            &upgrade_authority_address,
2549            &elf,
2550        );
2551        let invalid_upgrade_authority_address = Pubkey::new_unique();
2552        transaction_accounts.get_mut(7).unwrap().0 = invalid_upgrade_authority_address;
2553        instruction_accounts.get_mut(7).unwrap().pubkey = invalid_upgrade_authority_address;
2554        process_instruction(
2555            elf.len(),
2556            transaction_accounts,
2557            instruction_accounts,
2558            Err(InstructionError::IncorrectAuthority),
2559        );
2560
2561        // Case: authority did not sign
2562        let (transaction_accounts, mut instruction_accounts) = get_accounts(
2563            &payer_address,
2564            &buffer_address,
2565            &upgrade_authority_address,
2566            &upgrade_authority_address,
2567            &elf,
2568        );
2569        instruction_accounts.get_mut(7).unwrap().is_signer = false;
2570        process_instruction(
2571            elf.len(),
2572            transaction_accounts,
2573            instruction_accounts,
2574            Err(InstructionError::MissingRequiredSignature),
2575        );
2576
2577        // Case: Buffer account and payer account alias
2578        let (transaction_accounts, mut instruction_accounts) = get_accounts(
2579            &payer_address,
2580            &buffer_address,
2581            &upgrade_authority_address,
2582            &upgrade_authority_address,
2583            &elf,
2584        );
2585        *instruction_accounts.get_mut(0).unwrap() = instruction_accounts.get(3).unwrap().clone();
2586        process_instruction(
2587            elf.len(),
2588            transaction_accounts,
2589            instruction_accounts,
2590            Err(InstructionError::AccountBorrowFailed),
2591        );
2592
2593        // Case: Program account not owned by loader
2594        //
2595        // Unlike `Upgrade`, `DeployWithMaxDataLen` has no explicit owner
2596        // check on the program account. Validation passes, and the failure
2597        // only surfaces at the end when the handler tries to mutate the
2598        // program's state — `set_state` requires the account to be owned by
2599        // the currently-executing program, so it trips
2600        // `ExternalAccountDataModified`.
2601        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2602            &payer_address,
2603            &buffer_address,
2604            &upgrade_authority_address,
2605            &upgrade_authority_address,
2606            &elf,
2607        );
2608        transaction_accounts
2609            .get_mut(2)
2610            .unwrap()
2611            .1
2612            .set_owner(Pubkey::new_unique());
2613        process_instruction(
2614            elf.len(),
2615            transaction_accounts,
2616            instruction_accounts,
2617            Err(InstructionError::ExternalAccountDataModified),
2618        );
2619
2620        // Case: Program account not writable
2621        //
2622        // `DeployWithMaxDataLen` also lacks an explicit writability check on
2623        // the program account, so the failure again surfaces at
2624        // `set_state`, this time via the writability guard: a non-writable
2625        // account yields `ReadonlyDataModified`.
2626        let (transaction_accounts, mut instruction_accounts) = get_accounts(
2627            &payer_address,
2628            &buffer_address,
2629            &upgrade_authority_address,
2630            &upgrade_authority_address,
2631            &elf,
2632        );
2633        instruction_accounts.get_mut(2).unwrap().is_writable = false;
2634        process_instruction(
2635            elf.len(),
2636            transaction_accounts,
2637            instruction_accounts,
2638            Err(InstructionError::ReadonlyDataModified),
2639        );
2640
2641        // Case: Program account already initialized
2642        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2643            &payer_address,
2644            &buffer_address,
2645            &upgrade_authority_address,
2646            &upgrade_authority_address,
2647            &elf,
2648        );
2649        transaction_accounts
2650            .get_mut(2)
2651            .unwrap()
2652            .1
2653            .set_state(&UpgradeableLoaderState::Program {
2654                programdata_address: Pubkey::new_unique(),
2655            })
2656            .unwrap();
2657        process_instruction(
2658            elf.len(),
2659            transaction_accounts,
2660            instruction_accounts,
2661            Err(InstructionError::AccountAlreadyInitialized),
2662        );
2663
2664        // Case: Program account too small
2665        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2666            &payer_address,
2667            &buffer_address,
2668            &upgrade_authority_address,
2669            &upgrade_authority_address,
2670            &elf,
2671        );
2672        truncate_data(&mut transaction_accounts.get_mut(2).unwrap().1, 5);
2673        process_instruction(
2674            elf.len(),
2675            transaction_accounts,
2676            instruction_accounts,
2677            Err(InstructionError::AccountDataTooSmall),
2678        );
2679
2680        // Case: Program account not rent-exempt
2681        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2682            &payer_address,
2683            &buffer_address,
2684            &upgrade_authority_address,
2685            &upgrade_authority_address,
2686            &elf,
2687        );
2688        transaction_accounts.get_mut(2).unwrap().1.set_lamports(1);
2689        process_instruction(
2690            elf.len(),
2691            transaction_accounts,
2692            instruction_accounts,
2693            Err(InstructionError::ExecutableAccountNotRentExempt),
2694        );
2695
2696        // Case: ProgramData address not derived
2697        let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
2698            &payer_address,
2699            &buffer_address,
2700            &upgrade_authority_address,
2701            &upgrade_authority_address,
2702            &elf,
2703        );
2704        let invalid_programdata_address = Pubkey::new_unique();
2705        transaction_accounts.get_mut(1).unwrap().0 = invalid_programdata_address;
2706        instruction_accounts.get_mut(1).unwrap().pubkey = invalid_programdata_address;
2707        process_instruction(
2708            elf.len(),
2709            transaction_accounts,
2710            instruction_accounts,
2711            Err(InstructionError::InvalidArgument),
2712        );
2713
2714        // Case: Buffer account not initialized
2715        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2716            &payer_address,
2717            &buffer_address,
2718            &upgrade_authority_address,
2719            &upgrade_authority_address,
2720            &elf,
2721        );
2722        transaction_accounts
2723            .get_mut(3)
2724            .unwrap()
2725            .1
2726            .set_state(&UpgradeableLoaderState::Uninitialized)
2727            .unwrap();
2728        process_instruction(
2729            elf.len(),
2730            transaction_accounts,
2731            instruction_accounts,
2732            Err(InstructionError::InvalidArgument),
2733        );
2734
2735        // Case: Buffer account not writable
2736        for buffer_balance in [0, 1_000_000, 15 * 1_000_000_000] {
2737            let (mut transaction_accounts, mut instruction_accounts) = get_accounts(
2738                &payer_address,
2739                &buffer_address,
2740                &upgrade_authority_address,
2741                &upgrade_authority_address,
2742                &elf,
2743            );
2744            transaction_accounts
2745                .get_mut(3)
2746                .unwrap()
2747                .1
2748                .set_lamports(buffer_balance);
2749            instruction_accounts.get_mut(3).unwrap().is_writable = false;
2750            process_instruction(
2751                elf.len(),
2752                transaction_accounts,
2753                instruction_accounts,
2754                Err(InstructionError::InvalidArgument),
2755            );
2756        }
2757
2758        // Case: Buffer account not owned by loader: lamports scenario
2759        //
2760        // In `DeployWithMaxDataLen`, the buffer's lamports are drained to the
2761        // payer before the payer is debited for the programdata's rent. Then,
2762        // the buffer's data is set to `size_of_buffer(0)`.
2763        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2764            &payer_address,
2765            &buffer_address,
2766            &upgrade_authority_address,
2767            &upgrade_authority_address,
2768            &elf,
2769        );
2770        {
2771            // Let's make sure the programdata requires a top-up.
2772            let required_rent = Rent::default()
2773                .minimum_balance(UpgradeableLoaderState::size_of_programdata(elf.len()));
2774            let programdata = &transaction_accounts.get(1).unwrap().1;
2775            assert!(programdata.lamports() < required_rent);
2776            let buffer = &mut transaction_accounts.get_mut(3).unwrap().1;
2777            buffer.set_owner(Pubkey::new_unique());
2778            buffer.set_lamports(required_rent);
2779        }
2780        process_instruction(
2781            elf.len(),
2782            transaction_accounts,
2783            instruction_accounts,
2784            Err(InstructionError::IncorrectProgramId),
2785        );
2786
2787        // Case: Buffer account not owned by loader: shrink scenario
2788        //
2789        // Same as the above case, but give the buffer a lamports balance of
2790        // `0`, rendering its balance "unchanged" by the drain operation.
2791        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2792            &payer_address,
2793            &buffer_address,
2794            &upgrade_authority_address,
2795            &upgrade_authority_address,
2796            &elf,
2797        );
2798        {
2799            // Set the buffer's lamports to zero.
2800            let buffer = &mut transaction_accounts.get_mut(3).unwrap().1;
2801            buffer.set_owner(Pubkey::new_unique());
2802            buffer.set_lamports(0);
2803        }
2804        process_instruction(
2805            elf.len(),
2806            transaction_accounts,
2807            instruction_accounts,
2808            Err(InstructionError::IncorrectProgramId),
2809        );
2810
2811        // Case: Buffer account not owned by loader: no-op scenario
2812        //
2813        // Same as the above case, but also truncate the buffer's data to
2814        // `size_of_buffer(0)` - just the buffer metadata, no ELF - rendering
2815        // the closing resize "unchanged" as well.
2816        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2817            &payer_address,
2818            &buffer_address,
2819            &upgrade_authority_address,
2820            &upgrade_authority_address,
2821            &elf,
2822        );
2823        {
2824            // Empty the buffer (metadata only) and zero its lamports.
2825            let buffer = &mut transaction_accounts.get_mut(3).unwrap().1;
2826            buffer.set_owner(Pubkey::new_unique());
2827            buffer.set_lamports(0);
2828            truncate_data(buffer, UpgradeableLoaderState::size_of_buffer(0));
2829        }
2830        process_instruction(
2831            elf.len(),
2832            transaction_accounts,
2833            instruction_accounts,
2834            Err(InstructionError::IncorrectProgramId),
2835        );
2836
2837        // Case: Max data length too small for Buffer data
2838        let (transaction_accounts, instruction_accounts) = get_accounts(
2839            &payer_address,
2840            &buffer_address,
2841            &upgrade_authority_address,
2842            &upgrade_authority_address,
2843            &elf,
2844        );
2845        process_instruction(
2846            elf.len().saturating_sub(1),
2847            transaction_accounts,
2848            instruction_accounts,
2849            Err(InstructionError::AccountDataTooSmall),
2850        );
2851
2852        // Case: Max data length too large
2853        let (transaction_accounts, instruction_accounts) = get_accounts(
2854            &payer_address,
2855            &buffer_address,
2856            &upgrade_authority_address,
2857            &upgrade_authority_address,
2858            &elf,
2859        );
2860        process_instruction(
2861            MAX_PERMITTED_DATA_LENGTH as usize,
2862            transaction_accounts,
2863            instruction_accounts,
2864            Err(InstructionError::InvalidArgument),
2865        );
2866
2867        // Case: Mismatched buffer authority
2868        let (transaction_accounts, instruction_accounts) = get_accounts(
2869            &payer_address,
2870            &buffer_address,
2871            &buffer_address,
2872            &upgrade_authority_address,
2873            &elf,
2874        );
2875        process_instruction(
2876            elf.len(),
2877            transaction_accounts,
2878            instruction_accounts,
2879            Err(InstructionError::IncorrectAuthority),
2880        );
2881
2882        // Case: No buffer authority
2883        let (mut transaction_accounts, instruction_accounts) = get_accounts(
2884            &payer_address,
2885            &buffer_address,
2886            &buffer_address,
2887            &upgrade_authority_address,
2888            &elf,
2889        );
2890        transaction_accounts
2891            .get_mut(3)
2892            .unwrap()
2893            .1
2894            .set_state(&UpgradeableLoaderState::Buffer {
2895                authority_address: None,
2896            })
2897            .unwrap();
2898        process_instruction(
2899            elf.len(),
2900            transaction_accounts,
2901            instruction_accounts,
2902            Err(InstructionError::IncorrectAuthority),
2903        );
2904
2905        // Case: Deploy SBPFv0
2906        let mut file =
2907            File::open("test_elfs/out/sbpfv0_verifier_err.so").expect("file open failed");
2908        let mut elf = Vec::new();
2909        file.read_to_end(&mut elf).unwrap();
2910        let (transaction_accounts, instruction_accounts) = get_accounts(
2911            &payer_address,
2912            &buffer_address,
2913            &upgrade_authority_address,
2914            &upgrade_authority_address,
2915            &elf,
2916        );
2917        process_instruction(
2918            elf.len(),
2919            transaction_accounts,
2920            instruction_accounts,
2921            Err(InstructionError::InvalidAccountData),
2922        );
2923    }
2924
2925    #[test]
2926    fn test_bpf_loader_upgradeable_set_upgrade_authority() {
2927        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
2928        let loader_id = bpf_loader_upgradeable::id();
2929        let slot = 0;
2930        let upgrade_authority_address = Pubkey::new_unique();
2931        let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
2932        let new_upgrade_authority_address = Pubkey::new_unique();
2933        let new_upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
2934        let program_address = Pubkey::new_unique();
2935        let (programdata_address, _) = Pubkey::find_program_address(
2936            &[program_address.as_ref()],
2937            &bpf_loader_upgradeable::id(),
2938        );
2939        let mut programdata_account = AccountSharedData::new(
2940            1,
2941            UpgradeableLoaderState::size_of_programdata(0),
2942            &bpf_loader_upgradeable::id(),
2943        );
2944        programdata_account
2945            .set_state(&UpgradeableLoaderState::ProgramData {
2946                slot,
2947                upgrade_authority_address: Some(upgrade_authority_address),
2948            })
2949            .unwrap();
2950        let programdata_meta = AccountMeta {
2951            pubkey: programdata_address,
2952            is_signer: false,
2953            is_writable: true,
2954        };
2955        let upgrade_authority_meta = AccountMeta {
2956            pubkey: upgrade_authority_address,
2957            is_signer: true,
2958            is_writable: false,
2959        };
2960        let new_upgrade_authority_meta = AccountMeta {
2961            pubkey: new_upgrade_authority_address,
2962            is_signer: false,
2963            is_writable: false,
2964        };
2965
2966        // Case: Set to new authority
2967        let accounts = process_instruction(
2968            &loader_id,
2969            &instruction,
2970            vec![
2971                (programdata_address, programdata_account.clone()),
2972                (upgrade_authority_address, upgrade_authority_account.clone()),
2973                (
2974                    new_upgrade_authority_address,
2975                    new_upgrade_authority_account.clone(),
2976                ),
2977            ],
2978            vec![
2979                programdata_meta.clone(),
2980                upgrade_authority_meta.clone(),
2981                new_upgrade_authority_meta.clone(),
2982            ],
2983            Ok(()),
2984        );
2985        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
2986        assert_eq!(
2987            state,
2988            UpgradeableLoaderState::ProgramData {
2989                slot,
2990                upgrade_authority_address: Some(new_upgrade_authority_address),
2991            }
2992        );
2993
2994        // Case: Finalize
2995        let accounts = process_instruction(
2996            &loader_id,
2997            &instruction,
2998            vec![
2999                (programdata_address, programdata_account.clone()),
3000                (upgrade_authority_address, upgrade_authority_account.clone()),
3001            ],
3002            vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
3003            Ok(()),
3004        );
3005        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3006        assert_eq!(
3007            state,
3008            UpgradeableLoaderState::ProgramData {
3009                slot,
3010                upgrade_authority_address: None,
3011            }
3012        );
3013
3014        // Case: Finalize a SBPFv0 program
3015        let mut file =
3016            File::open("test_elfs/out/sbpfv0_verifier_err.so").expect("file open failed");
3017        let mut elf = Vec::new();
3018        file.read_to_end(&mut elf).unwrap();
3019        programdata_account.resize(UpgradeableLoaderState::size_of_programdata(elf.len()), 0);
3020        programdata_account
3021            .data_as_mut_slice()
3022            .get_mut(UpgradeableLoaderState::size_of_programdata_metadata()..)
3023            .unwrap()
3024            .copy_from_slice(&elf);
3025        process_instruction(
3026            &loader_id,
3027            &instruction,
3028            vec![
3029                (programdata_address, programdata_account.clone()),
3030                (upgrade_authority_address, upgrade_authority_account.clone()),
3031            ],
3032            vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
3033            Err(InstructionError::InvalidAccountData),
3034        );
3035
3036        // Case: Authority did not sign
3037        process_instruction(
3038            &loader_id,
3039            &instruction,
3040            vec![
3041                (programdata_address, programdata_account.clone()),
3042                (upgrade_authority_address, upgrade_authority_account.clone()),
3043            ],
3044            vec![
3045                programdata_meta.clone(),
3046                AccountMeta {
3047                    pubkey: upgrade_authority_address,
3048                    is_signer: false,
3049                    is_writable: false,
3050                },
3051            ],
3052            Err(InstructionError::MissingRequiredSignature),
3053        );
3054
3055        // Case: wrong authority
3056        let invalid_upgrade_authority_address = Pubkey::new_unique();
3057        process_instruction(
3058            &loader_id,
3059            &instruction,
3060            vec![
3061                (programdata_address, programdata_account.clone()),
3062                (
3063                    invalid_upgrade_authority_address,
3064                    upgrade_authority_account.clone(),
3065                ),
3066                (new_upgrade_authority_address, new_upgrade_authority_account),
3067            ],
3068            vec![
3069                programdata_meta.clone(),
3070                AccountMeta {
3071                    pubkey: invalid_upgrade_authority_address,
3072                    is_signer: true,
3073                    is_writable: false,
3074                },
3075                new_upgrade_authority_meta,
3076            ],
3077            Err(InstructionError::IncorrectAuthority),
3078        );
3079
3080        // Case: No authority
3081        programdata_account
3082            .set_state(&UpgradeableLoaderState::ProgramData {
3083                slot,
3084                upgrade_authority_address: None,
3085            })
3086            .unwrap();
3087        process_instruction(
3088            &loader_id,
3089            &instruction,
3090            vec![
3091                (programdata_address, programdata_account.clone()),
3092                (upgrade_authority_address, upgrade_authority_account.clone()),
3093            ],
3094            vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
3095            Err(InstructionError::Immutable),
3096        );
3097
3098        // Case: Not a ProgramData account
3099        programdata_account
3100            .set_state(&UpgradeableLoaderState::Program {
3101                programdata_address: Pubkey::new_unique(),
3102            })
3103            .unwrap();
3104        process_instruction(
3105            &loader_id,
3106            &instruction,
3107            vec![
3108                (programdata_address, programdata_account.clone()),
3109                (upgrade_authority_address, upgrade_authority_account),
3110            ],
3111            vec![programdata_meta, upgrade_authority_meta],
3112            Err(InstructionError::InvalidArgument),
3113        );
3114    }
3115
3116    #[test]
3117    fn test_bpf_loader_upgradeable_set_upgrade_authority_checked() {
3118        let instruction =
3119            bincode::serialize(&UpgradeableLoaderInstruction::SetAuthorityChecked).unwrap();
3120        let loader_id = bpf_loader_upgradeable::id();
3121        let slot = 0;
3122        let upgrade_authority_address = Pubkey::new_unique();
3123        let upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3124        let new_upgrade_authority_address = Pubkey::new_unique();
3125        let new_upgrade_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3126        let program_address = Pubkey::new_unique();
3127        let (programdata_address, _) = Pubkey::find_program_address(
3128            &[program_address.as_ref()],
3129            &bpf_loader_upgradeable::id(),
3130        );
3131        let mut programdata_account = AccountSharedData::new(
3132            1,
3133            UpgradeableLoaderState::size_of_programdata(0),
3134            &bpf_loader_upgradeable::id(),
3135        );
3136        programdata_account
3137            .set_state(&UpgradeableLoaderState::ProgramData {
3138                slot,
3139                upgrade_authority_address: Some(upgrade_authority_address),
3140            })
3141            .unwrap();
3142        let programdata_meta = AccountMeta {
3143            pubkey: programdata_address,
3144            is_signer: false,
3145            is_writable: true,
3146        };
3147        let upgrade_authority_meta = AccountMeta {
3148            pubkey: upgrade_authority_address,
3149            is_signer: true,
3150            is_writable: false,
3151        };
3152        let new_upgrade_authority_meta = AccountMeta {
3153            pubkey: new_upgrade_authority_address,
3154            is_signer: true,
3155            is_writable: false,
3156        };
3157
3158        // Case: Set to new authority
3159        let accounts = process_instruction(
3160            &loader_id,
3161            &instruction,
3162            vec![
3163                (programdata_address, programdata_account.clone()),
3164                (upgrade_authority_address, upgrade_authority_account.clone()),
3165                (
3166                    new_upgrade_authority_address,
3167                    new_upgrade_authority_account.clone(),
3168                ),
3169            ],
3170            vec![
3171                programdata_meta.clone(),
3172                upgrade_authority_meta.clone(),
3173                new_upgrade_authority_meta.clone(),
3174            ],
3175            Ok(()),
3176        );
3177
3178        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3179        assert_eq!(
3180            state,
3181            UpgradeableLoaderState::ProgramData {
3182                slot,
3183                upgrade_authority_address: Some(new_upgrade_authority_address),
3184            }
3185        );
3186
3187        // Case: set to same authority
3188        process_instruction(
3189            &loader_id,
3190            &instruction,
3191            vec![
3192                (programdata_address, programdata_account.clone()),
3193                (upgrade_authority_address, upgrade_authority_account.clone()),
3194            ],
3195            vec![
3196                programdata_meta.clone(),
3197                upgrade_authority_meta.clone(),
3198                upgrade_authority_meta.clone(),
3199            ],
3200            Ok(()),
3201        );
3202
3203        // Case: present authority not in instruction
3204        process_instruction(
3205            &loader_id,
3206            &instruction,
3207            vec![
3208                (programdata_address, programdata_account.clone()),
3209                (upgrade_authority_address, upgrade_authority_account.clone()),
3210                (
3211                    new_upgrade_authority_address,
3212                    new_upgrade_authority_account.clone(),
3213                ),
3214            ],
3215            vec![programdata_meta.clone(), new_upgrade_authority_meta.clone()],
3216            Err(InstructionError::MissingAccount),
3217        );
3218
3219        // Case: new authority not in instruction
3220        process_instruction(
3221            &loader_id,
3222            &instruction,
3223            vec![
3224                (programdata_address, programdata_account.clone()),
3225                (upgrade_authority_address, upgrade_authority_account.clone()),
3226                (
3227                    new_upgrade_authority_address,
3228                    new_upgrade_authority_account.clone(),
3229                ),
3230            ],
3231            vec![programdata_meta.clone(), upgrade_authority_meta.clone()],
3232            Err(InstructionError::MissingAccount),
3233        );
3234
3235        // Case: present authority did not sign
3236        process_instruction(
3237            &loader_id,
3238            &instruction,
3239            vec![
3240                (programdata_address, programdata_account.clone()),
3241                (upgrade_authority_address, upgrade_authority_account.clone()),
3242                (
3243                    new_upgrade_authority_address,
3244                    new_upgrade_authority_account.clone(),
3245                ),
3246            ],
3247            vec![
3248                programdata_meta.clone(),
3249                AccountMeta {
3250                    pubkey: upgrade_authority_address,
3251                    is_signer: false,
3252                    is_writable: false,
3253                },
3254                new_upgrade_authority_meta.clone(),
3255            ],
3256            Err(InstructionError::MissingRequiredSignature),
3257        );
3258
3259        // Case: New authority did not sign
3260        process_instruction(
3261            &loader_id,
3262            &instruction,
3263            vec![
3264                (programdata_address, programdata_account.clone()),
3265                (upgrade_authority_address, upgrade_authority_account.clone()),
3266                (
3267                    new_upgrade_authority_address,
3268                    new_upgrade_authority_account.clone(),
3269                ),
3270            ],
3271            vec![
3272                programdata_meta.clone(),
3273                upgrade_authority_meta.clone(),
3274                AccountMeta {
3275                    pubkey: new_upgrade_authority_address,
3276                    is_signer: false,
3277                    is_writable: false,
3278                },
3279            ],
3280            Err(InstructionError::MissingRequiredSignature),
3281        );
3282
3283        // Case: wrong present authority
3284        let invalid_upgrade_authority_address = Pubkey::new_unique();
3285        process_instruction(
3286            &loader_id,
3287            &instruction,
3288            vec![
3289                (programdata_address, programdata_account.clone()),
3290                (
3291                    invalid_upgrade_authority_address,
3292                    upgrade_authority_account.clone(),
3293                ),
3294                (new_upgrade_authority_address, new_upgrade_authority_account),
3295            ],
3296            vec![
3297                programdata_meta.clone(),
3298                AccountMeta {
3299                    pubkey: invalid_upgrade_authority_address,
3300                    is_signer: true,
3301                    is_writable: false,
3302                },
3303                new_upgrade_authority_meta.clone(),
3304            ],
3305            Err(InstructionError::IncorrectAuthority),
3306        );
3307
3308        // Case: programdata is immutable
3309        programdata_account
3310            .set_state(&UpgradeableLoaderState::ProgramData {
3311                slot,
3312                upgrade_authority_address: None,
3313            })
3314            .unwrap();
3315        process_instruction(
3316            &loader_id,
3317            &instruction,
3318            vec![
3319                (programdata_address, programdata_account.clone()),
3320                (upgrade_authority_address, upgrade_authority_account.clone()),
3321            ],
3322            vec![
3323                programdata_meta.clone(),
3324                upgrade_authority_meta.clone(),
3325                new_upgrade_authority_meta.clone(),
3326            ],
3327            Err(InstructionError::Immutable),
3328        );
3329
3330        // Case: Not a ProgramData account
3331        programdata_account
3332            .set_state(&UpgradeableLoaderState::Program {
3333                programdata_address: Pubkey::new_unique(),
3334            })
3335            .unwrap();
3336        process_instruction(
3337            &loader_id,
3338            &instruction,
3339            vec![
3340                (programdata_address, programdata_account.clone()),
3341                (upgrade_authority_address, upgrade_authority_account),
3342            ],
3343            vec![
3344                programdata_meta,
3345                upgrade_authority_meta,
3346                new_upgrade_authority_meta,
3347            ],
3348            Err(InstructionError::InvalidArgument),
3349        );
3350    }
3351
3352    #[test]
3353    fn test_bpf_loader_upgradeable_set_buffer_authority() {
3354        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
3355        let loader_id = bpf_loader_upgradeable::id();
3356        let invalid_authority_address = Pubkey::new_unique();
3357        let authority_address = Pubkey::new_unique();
3358        let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3359        let new_authority_address = Pubkey::new_unique();
3360        let new_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3361        let buffer_address = Pubkey::new_unique();
3362        let mut buffer_account =
3363            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(0), &loader_id);
3364        buffer_account
3365            .set_state(&UpgradeableLoaderState::Buffer {
3366                authority_address: Some(authority_address),
3367            })
3368            .unwrap();
3369        let mut transaction_accounts = vec![
3370            (buffer_address, buffer_account.clone()),
3371            (authority_address, authority_account.clone()),
3372            (new_authority_address, new_authority_account.clone()),
3373        ];
3374        let buffer_meta = AccountMeta {
3375            pubkey: buffer_address,
3376            is_signer: false,
3377            is_writable: true,
3378        };
3379        let authority_meta = AccountMeta {
3380            pubkey: authority_address,
3381            is_signer: true,
3382            is_writable: false,
3383        };
3384        let new_authority_meta = AccountMeta {
3385            pubkey: new_authority_address,
3386            is_signer: false,
3387            is_writable: false,
3388        };
3389
3390        // Case: New authority required
3391        let accounts = process_instruction(
3392            &loader_id,
3393            &instruction,
3394            transaction_accounts.clone(),
3395            vec![buffer_meta.clone(), authority_meta.clone()],
3396            Err(InstructionError::IncorrectAuthority),
3397        );
3398        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3399        assert_eq!(
3400            state,
3401            UpgradeableLoaderState::Buffer {
3402                authority_address: Some(authority_address),
3403            }
3404        );
3405
3406        // Case: Set to new authority
3407        buffer_account
3408            .set_state(&UpgradeableLoaderState::Buffer {
3409                authority_address: Some(authority_address),
3410            })
3411            .unwrap();
3412        let accounts = process_instruction(
3413            &loader_id,
3414            &instruction,
3415            transaction_accounts.clone(),
3416            vec![
3417                buffer_meta.clone(),
3418                authority_meta.clone(),
3419                new_authority_meta.clone(),
3420            ],
3421            Ok(()),
3422        );
3423        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3424        assert_eq!(
3425            state,
3426            UpgradeableLoaderState::Buffer {
3427                authority_address: Some(new_authority_address),
3428            }
3429        );
3430
3431        // Case: Authority did not sign
3432        process_instruction(
3433            &loader_id,
3434            &instruction,
3435            transaction_accounts.clone(),
3436            vec![
3437                buffer_meta.clone(),
3438                AccountMeta {
3439                    pubkey: authority_address,
3440                    is_signer: false,
3441                    is_writable: false,
3442                },
3443                new_authority_meta.clone(),
3444            ],
3445            Err(InstructionError::MissingRequiredSignature),
3446        );
3447
3448        // Case: wrong authority
3449        process_instruction(
3450            &loader_id,
3451            &instruction,
3452            vec![
3453                (buffer_address, buffer_account.clone()),
3454                (invalid_authority_address, authority_account),
3455                (new_authority_address, new_authority_account),
3456            ],
3457            vec![
3458                buffer_meta.clone(),
3459                AccountMeta {
3460                    pubkey: invalid_authority_address,
3461                    is_signer: true,
3462                    is_writable: false,
3463                },
3464                new_authority_meta.clone(),
3465            ],
3466            Err(InstructionError::IncorrectAuthority),
3467        );
3468
3469        // Case: No authority
3470        process_instruction(
3471            &loader_id,
3472            &instruction,
3473            transaction_accounts.clone(),
3474            vec![buffer_meta.clone(), authority_meta.clone()],
3475            Err(InstructionError::IncorrectAuthority),
3476        );
3477
3478        // Case: Set to no authority
3479        transaction_accounts
3480            .get_mut(0)
3481            .unwrap()
3482            .1
3483            .set_state(&UpgradeableLoaderState::Buffer {
3484                authority_address: None,
3485            })
3486            .unwrap();
3487        process_instruction(
3488            &loader_id,
3489            &instruction,
3490            transaction_accounts.clone(),
3491            vec![
3492                buffer_meta.clone(),
3493                authority_meta.clone(),
3494                new_authority_meta.clone(),
3495            ],
3496            Err(InstructionError::Immutable),
3497        );
3498
3499        // Case: Not a Buffer account
3500        transaction_accounts
3501            .get_mut(0)
3502            .unwrap()
3503            .1
3504            .set_state(&UpgradeableLoaderState::Program {
3505                programdata_address: Pubkey::new_unique(),
3506            })
3507            .unwrap();
3508        process_instruction(
3509            &loader_id,
3510            &instruction,
3511            transaction_accounts.clone(),
3512            vec![buffer_meta, authority_meta, new_authority_meta],
3513            Err(InstructionError::InvalidArgument),
3514        );
3515    }
3516
3517    #[test]
3518    fn test_bpf_loader_upgradeable_set_buffer_authority_checked() {
3519        let instruction =
3520            bincode::serialize(&UpgradeableLoaderInstruction::SetAuthorityChecked).unwrap();
3521        let loader_id = bpf_loader_upgradeable::id();
3522        let invalid_authority_address = Pubkey::new_unique();
3523        let authority_address = Pubkey::new_unique();
3524        let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3525        let new_authority_address = Pubkey::new_unique();
3526        let new_authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3527        let buffer_address = Pubkey::new_unique();
3528        let mut buffer_account =
3529            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(0), &loader_id);
3530        buffer_account
3531            .set_state(&UpgradeableLoaderState::Buffer {
3532                authority_address: Some(authority_address),
3533            })
3534            .unwrap();
3535        let mut transaction_accounts = vec![
3536            (buffer_address, buffer_account.clone()),
3537            (authority_address, authority_account.clone()),
3538            (new_authority_address, new_authority_account.clone()),
3539        ];
3540        let buffer_meta = AccountMeta {
3541            pubkey: buffer_address,
3542            is_signer: false,
3543            is_writable: true,
3544        };
3545        let authority_meta = AccountMeta {
3546            pubkey: authority_address,
3547            is_signer: true,
3548            is_writable: false,
3549        };
3550        let new_authority_meta = AccountMeta {
3551            pubkey: new_authority_address,
3552            is_signer: true,
3553            is_writable: false,
3554        };
3555
3556        // Case: Set to new authority
3557        buffer_account
3558            .set_state(&UpgradeableLoaderState::Buffer {
3559                authority_address: Some(authority_address),
3560            })
3561            .unwrap();
3562        let accounts = process_instruction(
3563            &loader_id,
3564            &instruction,
3565            transaction_accounts.clone(),
3566            vec![
3567                buffer_meta.clone(),
3568                authority_meta.clone(),
3569                new_authority_meta.clone(),
3570            ],
3571            Ok(()),
3572        );
3573        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3574        assert_eq!(
3575            state,
3576            UpgradeableLoaderState::Buffer {
3577                authority_address: Some(new_authority_address),
3578            }
3579        );
3580
3581        // Case: set to same authority
3582        process_instruction(
3583            &loader_id,
3584            &instruction,
3585            transaction_accounts.clone(),
3586            vec![
3587                buffer_meta.clone(),
3588                authority_meta.clone(),
3589                authority_meta.clone(),
3590            ],
3591            Ok(()),
3592        );
3593
3594        // Case: Missing current authority
3595        process_instruction(
3596            &loader_id,
3597            &instruction,
3598            transaction_accounts.clone(),
3599            vec![buffer_meta.clone(), new_authority_meta.clone()],
3600            Err(InstructionError::MissingAccount),
3601        );
3602
3603        // Case: Missing new authority
3604        process_instruction(
3605            &loader_id,
3606            &instruction,
3607            transaction_accounts.clone(),
3608            vec![buffer_meta.clone(), authority_meta.clone()],
3609            Err(InstructionError::MissingAccount),
3610        );
3611
3612        // Case: wrong present authority
3613        process_instruction(
3614            &loader_id,
3615            &instruction,
3616            vec![
3617                (buffer_address, buffer_account.clone()),
3618                (invalid_authority_address, authority_account),
3619                (new_authority_address, new_authority_account),
3620            ],
3621            vec![
3622                buffer_meta.clone(),
3623                AccountMeta {
3624                    pubkey: invalid_authority_address,
3625                    is_signer: true,
3626                    is_writable: false,
3627                },
3628                new_authority_meta.clone(),
3629            ],
3630            Err(InstructionError::IncorrectAuthority),
3631        );
3632
3633        // Case: present authority did not sign
3634        process_instruction(
3635            &loader_id,
3636            &instruction,
3637            transaction_accounts.clone(),
3638            vec![
3639                buffer_meta.clone(),
3640                AccountMeta {
3641                    pubkey: authority_address,
3642                    is_signer: false,
3643                    is_writable: false,
3644                },
3645                new_authority_meta.clone(),
3646            ],
3647            Err(InstructionError::MissingRequiredSignature),
3648        );
3649
3650        // Case: new authority did not sign
3651        process_instruction(
3652            &loader_id,
3653            &instruction,
3654            transaction_accounts.clone(),
3655            vec![
3656                buffer_meta.clone(),
3657                authority_meta.clone(),
3658                AccountMeta {
3659                    pubkey: new_authority_address,
3660                    is_signer: false,
3661                    is_writable: false,
3662                },
3663            ],
3664            Err(InstructionError::MissingRequiredSignature),
3665        );
3666
3667        // Case: Not a Buffer account
3668        transaction_accounts
3669            .get_mut(0)
3670            .unwrap()
3671            .1
3672            .set_state(&UpgradeableLoaderState::Program {
3673                programdata_address: Pubkey::new_unique(),
3674            })
3675            .unwrap();
3676        process_instruction(
3677            &loader_id,
3678            &instruction,
3679            transaction_accounts.clone(),
3680            vec![
3681                buffer_meta.clone(),
3682                authority_meta.clone(),
3683                new_authority_meta.clone(),
3684            ],
3685            Err(InstructionError::InvalidArgument),
3686        );
3687
3688        // Case: Buffer is immutable
3689        transaction_accounts
3690            .get_mut(0)
3691            .unwrap()
3692            .1
3693            .set_state(&UpgradeableLoaderState::Buffer {
3694                authority_address: None,
3695            })
3696            .unwrap();
3697        process_instruction(
3698            &loader_id,
3699            &instruction,
3700            transaction_accounts.clone(),
3701            vec![buffer_meta, authority_meta, new_authority_meta],
3702            Err(InstructionError::Immutable),
3703        );
3704    }
3705
3706    #[test]
3707    fn test_bpf_loader_upgradeable_close() {
3708        let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap();
3709        let loader_id = bpf_loader_upgradeable::id();
3710        let invalid_authority_address = Pubkey::new_unique();
3711        let authority_address = Pubkey::new_unique();
3712        let authority_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3713        let recipient_address = Pubkey::new_unique();
3714        let recipient_account = AccountSharedData::new(1, 0, &Pubkey::new_unique());
3715        let buffer_address = Pubkey::new_unique();
3716        let mut buffer_account =
3717            AccountSharedData::new(1, UpgradeableLoaderState::size_of_buffer(128), &loader_id);
3718        buffer_account
3719            .set_state(&UpgradeableLoaderState::Buffer {
3720                authority_address: Some(authority_address),
3721            })
3722            .unwrap();
3723        let uninitialized_address = Pubkey::new_unique();
3724        let mut uninitialized_account = AccountSharedData::new(
3725            1,
3726            UpgradeableLoaderState::size_of_programdata(0),
3727            &loader_id,
3728        );
3729        uninitialized_account
3730            .set_state(&UpgradeableLoaderState::Uninitialized)
3731            .unwrap();
3732        let programdata_address = Pubkey::new_unique();
3733        let mut programdata_account = AccountSharedData::new(
3734            1,
3735            UpgradeableLoaderState::size_of_programdata(128),
3736            &loader_id,
3737        );
3738        programdata_account
3739            .set_state(&UpgradeableLoaderState::ProgramData {
3740                slot: 0,
3741                upgrade_authority_address: Some(authority_address),
3742            })
3743            .unwrap();
3744        let program_address = Pubkey::new_unique();
3745        let mut program_account =
3746            AccountSharedData::new(1, UpgradeableLoaderState::size_of_program(), &loader_id);
3747        program_account.set_executable(true);
3748        program_account
3749            .set_state(&UpgradeableLoaderState::Program {
3750                programdata_address,
3751            })
3752            .unwrap();
3753        let clock_account = create_account_for_test(&Clock {
3754            slot: 1,
3755            ..Clock::default()
3756        });
3757        let transaction_accounts = vec![
3758            (buffer_address, buffer_account.clone()),
3759            (recipient_address, recipient_account.clone()),
3760            (authority_address, authority_account.clone()),
3761        ];
3762        let buffer_meta = AccountMeta {
3763            pubkey: buffer_address,
3764            is_signer: false,
3765            is_writable: true,
3766        };
3767        let recipient_meta = AccountMeta {
3768            pubkey: recipient_address,
3769            is_signer: false,
3770            is_writable: true,
3771        };
3772        let authority_meta = AccountMeta {
3773            pubkey: authority_address,
3774            is_signer: true,
3775            is_writable: false,
3776        };
3777
3778        // Case: close a buffer account
3779        let accounts = process_instruction(
3780            &loader_id,
3781            &instruction,
3782            transaction_accounts,
3783            vec![
3784                buffer_meta.clone(),
3785                recipient_meta.clone(),
3786                authority_meta.clone(),
3787            ],
3788            Ok(()),
3789        );
3790        assert_eq!(0, accounts.first().unwrap().lamports());
3791        assert_eq!(2, accounts.get(1).unwrap().lamports());
3792        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3793        assert_eq!(state, UpgradeableLoaderState::Uninitialized);
3794        assert_eq!(
3795            UpgradeableLoaderState::size_of_uninitialized(),
3796            accounts.first().unwrap().data().len()
3797        );
3798
3799        // Case: close with wrong authority
3800        process_instruction(
3801            &loader_id,
3802            &instruction,
3803            vec![
3804                (buffer_address, buffer_account.clone()),
3805                (recipient_address, recipient_account.clone()),
3806                (invalid_authority_address, authority_account.clone()),
3807            ],
3808            vec![
3809                buffer_meta,
3810                recipient_meta.clone(),
3811                AccountMeta {
3812                    pubkey: invalid_authority_address,
3813                    is_signer: true,
3814                    is_writable: false,
3815                },
3816            ],
3817            Err(InstructionError::IncorrectAuthority),
3818        );
3819
3820        // Case: close an uninitialized account
3821        let accounts = process_instruction(
3822            &loader_id,
3823            &instruction,
3824            vec![
3825                (uninitialized_address, uninitialized_account.clone()),
3826                (recipient_address, recipient_account.clone()),
3827                (invalid_authority_address, authority_account.clone()),
3828            ],
3829            vec![
3830                AccountMeta {
3831                    pubkey: uninitialized_address,
3832                    is_signer: false,
3833                    is_writable: true,
3834                },
3835                recipient_meta.clone(),
3836                authority_meta.clone(),
3837            ],
3838            Ok(()),
3839        );
3840        assert_eq!(0, accounts.first().unwrap().lamports());
3841        assert_eq!(2, accounts.get(1).unwrap().lamports());
3842        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3843        assert_eq!(state, UpgradeableLoaderState::Uninitialized);
3844        assert_eq!(
3845            UpgradeableLoaderState::size_of_uninitialized(),
3846            accounts.first().unwrap().data().len()
3847        );
3848
3849        // Case: close a program account
3850        let accounts = process_instruction(
3851            &loader_id,
3852            &instruction,
3853            vec![
3854                (programdata_address, programdata_account.clone()),
3855                (recipient_address, recipient_account.clone()),
3856                (authority_address, authority_account.clone()),
3857                (program_address, program_account.clone()),
3858                (sysvar::clock::id(), clock_account.clone()),
3859            ],
3860            vec![
3861                AccountMeta {
3862                    pubkey: programdata_address,
3863                    is_signer: false,
3864                    is_writable: true,
3865                },
3866                recipient_meta,
3867                authority_meta,
3868                AccountMeta {
3869                    pubkey: program_address,
3870                    is_signer: false,
3871                    is_writable: true,
3872                },
3873            ],
3874            Ok(()),
3875        );
3876        assert_eq!(0, accounts.first().unwrap().lamports());
3877        assert_eq!(2, accounts.get(1).unwrap().lamports());
3878        let state: UpgradeableLoaderState = accounts.first().unwrap().state().unwrap();
3879        assert_eq!(state, UpgradeableLoaderState::Uninitialized);
3880        assert_eq!(
3881            UpgradeableLoaderState::size_of_uninitialized(),
3882            accounts.first().unwrap().data().len()
3883        );
3884
3885        // Try to invoke closed account
3886        programdata_account = accounts.first().unwrap().clone();
3887        program_account = accounts.get(3).unwrap().clone();
3888        process_instruction(
3889            &program_address,
3890            &[],
3891            vec![
3892                (programdata_address, programdata_account.clone()),
3893                (program_address, program_account.clone()),
3894            ],
3895            Vec::new(),
3896            Err(InstructionError::UnsupportedProgramId),
3897        );
3898
3899        // Case: Reopen should fail
3900        process_instruction(
3901            &loader_id,
3902            &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
3903                max_data_len: 0,
3904            })
3905            .unwrap(),
3906            vec![
3907                (recipient_address, recipient_account),
3908                (programdata_address, programdata_account),
3909                (program_address, program_account),
3910                (buffer_address, buffer_account),
3911                (
3912                    sysvar::rent::id(),
3913                    create_account_for_test(&Rent::default()),
3914                ),
3915                (sysvar::clock::id(), clock_account),
3916                (
3917                    system_program::id(),
3918                    AccountSharedData::new(0, 0, &system_program::id()),
3919                ),
3920                (authority_address, authority_account),
3921            ],
3922            vec![
3923                AccountMeta {
3924                    pubkey: recipient_address,
3925                    is_signer: true,
3926                    is_writable: true,
3927                },
3928                AccountMeta {
3929                    pubkey: programdata_address,
3930                    is_signer: false,
3931                    is_writable: true,
3932                },
3933                AccountMeta {
3934                    pubkey: program_address,
3935                    is_signer: false,
3936                    is_writable: true,
3937                },
3938                AccountMeta {
3939                    pubkey: buffer_address,
3940                    is_signer: false,
3941                    is_writable: false,
3942                },
3943                AccountMeta {
3944                    pubkey: sysvar::rent::id(),
3945                    is_signer: false,
3946                    is_writable: false,
3947                },
3948                AccountMeta {
3949                    pubkey: sysvar::clock::id(),
3950                    is_signer: false,
3951                    is_writable: false,
3952                },
3953                AccountMeta {
3954                    pubkey: system_program::id(),
3955                    is_signer: false,
3956                    is_writable: false,
3957                },
3958                AccountMeta {
3959                    pubkey: authority_address,
3960                    is_signer: false,
3961                    is_writable: false,
3962                },
3963            ],
3964            Err(InstructionError::AccountAlreadyInitialized),
3965        );
3966    }
3967
3968    /// fuzzing utility function
3969    fn fuzz<F>(
3970        bytes: &[u8],
3971        outer_iters: usize,
3972        inner_iters: usize,
3973        offset: Range<usize>,
3974        value: Range<u8>,
3975        work: F,
3976    ) where
3977        F: Fn(&mut [u8]),
3978    {
3979        let mut rng = rand::rng();
3980        for _ in 0..outer_iters {
3981            let mut mangled_bytes = bytes.to_vec();
3982            for _ in 0..inner_iters {
3983                let offset = rng.random_range(offset.start..offset.end);
3984                let value = rng.random_range(value.start..value.end);
3985                *mangled_bytes.get_mut(offset).unwrap() = value;
3986                work(&mut mangled_bytes);
3987            }
3988        }
3989    }
3990
3991    #[test]
3992    #[ignore]
3993    fn test_fuzz() {
3994        let loader_id = bpf_loader::id();
3995        let program_id = Pubkey::new_unique();
3996
3997        // Create program account
3998        let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
3999        let mut elf = Vec::new();
4000        file.read_to_end(&mut elf).unwrap();
4001
4002        // Mangle the whole file
4003        fuzz(
4004            &elf,
4005            1_000_000_000,
4006            100,
4007            0..elf.len(),
4008            0..255,
4009            |bytes: &mut [u8]| {
4010                let mut program_account = AccountSharedData::new(1, 0, &loader_id);
4011                program_account.set_data(bytes.to_vec());
4012                program_account.set_executable(true);
4013                process_instruction(
4014                    &program_id,
4015                    &[],
4016                    vec![(program_id, program_account)],
4017                    Vec::new(),
4018                    Ok(()),
4019                );
4020            },
4021        );
4022    }
4023
4024    #[test]
4025    fn test_calculate_heap_cost() {
4026        let heap_cost = 8_u64;
4027
4028        // heap allocations are in 32K block, `heap_cost` of CU is consumed per additional 32k
4029
4030        // assert less than 32K heap should cost zero unit
4031        assert_eq!(0, calculate_heap_cost(31 * 1024, heap_cost));
4032
4033        // assert exact 32K heap should be cost zero unit
4034        assert_eq!(0, calculate_heap_cost(32 * 1024, heap_cost));
4035
4036        // assert slightly more than 32K heap should cost 1 * heap_cost
4037        assert_eq!(heap_cost, calculate_heap_cost(33 * 1024, heap_cost));
4038
4039        // assert exact 64K heap should cost 1 * heap_cost
4040        assert_eq!(heap_cost, calculate_heap_cost(64 * 1024, heap_cost));
4041    }
4042
4043    fn deploy_test_program(
4044        invoke_context: &mut InvokeContext,
4045        program_id: Pubkey,
4046    ) -> Result<(), InstructionError> {
4047        let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
4048        let mut elf = Vec::new();
4049        file.read_to_end(&mut elf).unwrap();
4050        deploy_program!(
4051            invoke_context,
4052            &program_id,
4053            &bpf_loader_upgradeable::id(),
4054            elf.len(),
4055            &elf,
4056            2_u64,
4057            true, // disable_sbpf_v0_v1_v2_deployment
4058        );
4059        Ok(())
4060    }
4061
4062    // Concurrency rationale: these tests construct `ProgramCacheEntry` instances
4063    // directly. The struct's `latest_access_slot: AtomicU64` field is defined in
4064    // `solana-program-runtime`; under the `shuttle-test` feature
4065    // `solana-svm-type-overrides` swaps `std::sync::atomic::AtomicU64` for
4066    // `shuttle::sync::atomic::AtomicU64`, whose Shuttle-backed operations
4067    // (load, fetch_max, and similar) must run inside an active Shuttle
4068    // scheduler. We therefore extract the test bodies into `do_test_*` helpers
4069    // and drive them via `shuttle::check_random` stubs when the feature is on.
4070    // We use `check_random` only (no `check_dfs` companion) because the test
4071    // bodies spawn no Shuttle threads, so DFS gives no meaningful interleaving
4072    // coverage; `check_random` is enough to provide the scheduler context.
4073    // This matches the single-scheduler pattern used in
4074    // `net-utils/src/token_bucket.rs` and `poh/src/record_channels.rs`.
4075    //
4076    // 100 iterations is intentionally low: the test bodies are single-threaded
4077    // (no `shuttle::thread::spawn`), so additional iterations validate only
4078    // the harness wiring, not concurrent interleavings. Bump this if a future
4079    // refactor introduces real concurrency in the test bodies.
4080    #[cfg(feature = "shuttle-test")]
4081    const PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS: usize = 100;
4082
4083    #[test]
4084    fn test_program_usage_count_on_upgrade() {
4085        #[cfg(feature = "shuttle-test")]
4086        shuttle::check_random(
4087            do_test_program_usage_count_on_upgrade,
4088            PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS,
4089        );
4090        #[cfg(not(feature = "shuttle-test"))]
4091        do_test_program_usage_count_on_upgrade();
4092    }
4093
4094    fn do_test_program_usage_count_on_upgrade() {
4095        let transaction_accounts = vec![(
4096            sysvar::epoch_schedule::id(),
4097            create_account_for_test(&EpochSchedule::default()),
4098        )];
4099        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4100        let program_id = Pubkey::new_unique();
4101        let env = ProgramRuntimeEnvironment::from(BuiltinProgram::new_mock());
4102        let stats = ProgramStatistics {
4103            uses: 100.into(),
4104            ..Default::default()
4105        };
4106        let program = ProgramCacheEntry {
4107            program: ProgramCacheEntryType::Unloaded(env),
4108            account_owner: ProgramCacheEntryOwner::LoaderV2,
4109            account_size: 0,
4110            deployment_slot: 0,
4111            effective_slot: 0,
4112            stats: stats.into(),
4113            latest_access_slot: AtomicU64::new(0),
4114        };
4115        invoke_context
4116            .program_cache_for_tx_batch
4117            .replenish(program_id, Arc::new(program));
4118        invoke_context
4119            .program_cache_for_tx_batch
4120            .set_slot_for_tests(2);
4121
4122        assert_matches!(
4123            deploy_test_program(&mut invoke_context, program_id,),
4124            Ok(())
4125        );
4126
4127        let updated_program = invoke_context
4128            .program_cache_for_tx_batch
4129            .find(&program_id)
4130            .expect("Didn't find upgraded program in the cache");
4131
4132        assert_eq!(updated_program.deployment_slot, 2);
4133        assert_eq!(updated_program.stats.uses.load(Ordering::Relaxed), 100);
4134    }
4135
4136    #[test]
4137    fn test_program_usage_count_on_non_upgrade() {
4138        #[cfg(feature = "shuttle-test")]
4139        shuttle::check_random(
4140            do_test_program_usage_count_on_non_upgrade,
4141            PROGRAM_USAGE_COUNT_RANDOM_ITERATIONS,
4142        );
4143        #[cfg(not(feature = "shuttle-test"))]
4144        do_test_program_usage_count_on_non_upgrade();
4145    }
4146
4147    fn do_test_program_usage_count_on_non_upgrade() {
4148        let transaction_accounts = vec![(
4149            sysvar::epoch_schedule::id(),
4150            create_account_for_test(&EpochSchedule::default()),
4151        )];
4152        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
4153        let program_id = Pubkey::new_unique();
4154        let env = ProgramRuntimeEnvironment::from(BuiltinProgram::new_mock());
4155        let stats = ProgramStatistics {
4156            uses: 100.into(),
4157            ..Default::default()
4158        };
4159        let program = ProgramCacheEntry {
4160            program: ProgramCacheEntryType::Unloaded(env),
4161            account_owner: ProgramCacheEntryOwner::LoaderV2,
4162            account_size: 0,
4163            deployment_slot: 0,
4164            effective_slot: 0,
4165            stats: stats.into(),
4166            latest_access_slot: AtomicU64::new(0),
4167        };
4168        invoke_context
4169            .program_cache_for_tx_batch
4170            .replenish(program_id, Arc::new(program));
4171        invoke_context
4172            .program_cache_for_tx_batch
4173            .set_slot_for_tests(2);
4174
4175        let program_id2 = Pubkey::new_unique();
4176        assert_matches!(
4177            deploy_test_program(&mut invoke_context, program_id2),
4178            Ok(())
4179        );
4180
4181        let program2 = invoke_context
4182            .program_cache_for_tx_batch
4183            .find(&program_id2)
4184            .expect("Didn't find upgraded program in the cache");
4185
4186        assert_eq!(program2.deployment_slot, 2);
4187        assert_eq!(program2.stats.uses.load(Ordering::Relaxed), 0);
4188    }
4189}