solana/programs/bpf_loader/src/lib.rs

645 lines
22 KiB
Rust
Raw Normal View History

2019-05-24 16:21:42 -07:00
pub mod alloc;
pub mod allocator_bump;
2018-10-16 16:33:31 -07:00
pub mod bpf_verifier;
pub mod syscalls;
2018-10-16 16:33:31 -07:00
use crate::{bpf_verifier::VerifierError, syscalls::SyscallError};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use num_derive::{FromPrimitive, ToPrimitive};
use solana_rbpf::{
ebpf::{EbpfError, UserDefinedError},
memory_region::MemoryRegion,
EbpfVm,
};
2020-01-02 18:18:56 -08:00
use solana_sdk::{
account::{is_executable, next_keyed_account, KeyedAccount},
bpf_loader,
decode_error::DecodeError,
entrypoint::SUCCESS,
2020-04-28 14:33:56 -07:00
entrypoint_native::InvokeContext,
2020-01-02 18:18:56 -08:00
instruction::InstructionError,
loader_instruction::LoaderInstruction,
program_utils::limited_deserialize,
2020-01-02 18:18:56 -08:00
pubkey::Pubkey,
};
use std::{io::prelude::*, mem};
use thiserror::Error;
2019-05-24 16:21:42 -07:00
2020-04-15 09:41:29 -07:00
solana_sdk::declare_loader!(
solana_sdk::bpf_loader::ID,
solana_bpf_loader_program,
process_instruction
);
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum BPFLoaderError {
#[error("failed to create virtual machine")]
VirtualMachineCreationFailed = 0x0b9f_0001,
#[error("virtual machine failed to run the program to completion")]
VirtualMachineFailedToRunProgram = 0x0b9f_0002,
}
impl<E> DecodeError<E> for BPFLoaderError {
fn type_of() -> &'static str {
"BPFLoaderError"
}
}
/// Errors returned by functions the BPF Loader registers with the vM
#[derive(Debug, Error)]
pub enum BPFError {
#[error("{0}")]
VerifierError(#[from] VerifierError),
#[error("{0}")]
SyscallError(#[from] SyscallError),
}
impl UserDefinedError for BPFError {}
2020-04-28 14:33:56 -07:00
pub fn create_vm<'a>(
prog: &'a [u8],
parameter_accounts: &'a [KeyedAccount<'a>],
2020-04-28 14:33:56 -07:00
invoke_context: &'a mut dyn InvokeContext,
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
2019-08-23 11:03:53 -07:00
let mut vm = EbpfVm::new(None)?;
vm.set_verifier(bpf_verifier::check)?;
2019-10-02 10:07:44 -07:00
vm.set_max_instruction_count(100_000)?;
vm.set_elf(&prog)?;
2019-05-24 16:21:42 -07:00
let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?;
2019-05-24 16:21:42 -07:00
Ok((vm, heap_region))
}
pub fn check_elf(prog: &[u8]) -> Result<(), EbpfError<BPFError>> {
2019-12-04 12:03:29 -08:00
let mut vm = EbpfVm::new(None)?;
vm.set_verifier(bpf_verifier::check)?;
vm.set_elf(&prog)?;
Ok(())
}
/// Look for a duplicate account and return its position if found
pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
for (i, account) in accounts.iter().enumerate() {
if account == keyed_account {
return (true, i);
}
}
(false, 0)
}
2020-01-02 18:18:56 -08:00
pub fn serialize_parameters(
2018-11-17 17:02:14 -08:00
program_id: &Pubkey,
2020-01-22 17:54:06 -08:00
keyed_accounts: &[KeyedAccount],
2018-11-13 19:54:41 -08:00
data: &[u8],
) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
.unwrap();
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
if is_dup {
v.write_u8(position as u8).unwrap();
} else {
v.write_u8(std::u8::MAX).unwrap();
2020-02-10 21:33:29 -08:00
v.write_u8(keyed_account.signer_key().is_some() as u8)
.unwrap();
2020-02-10 21:33:29 -08:00
v.write_u8(keyed_account.is_writable() as u8).unwrap();
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
.unwrap();
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
.unwrap();
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
v.write_u8(keyed_account.executable()? as u8).unwrap();
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
.unwrap();
}
}
v.write_u64::<LittleEndian>(data.len() as u64).unwrap();
v.write_all(data).unwrap();
2018-11-17 17:02:14 -08:00
v.write_all(program_id.as_ref()).unwrap();
Ok(v)
}
pub fn deserialize_parameters(
2020-01-22 17:54:06 -08:00
keyed_accounts: &[KeyedAccount],
buffer: &[u8],
) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup
if !is_dup {
2020-02-10 21:33:29 -08:00
start += mem::size_of::<u8>(); // is_signer
start += mem::size_of::<u8>(); // is_writable
start += mem::size_of::<Pubkey>(); // pubkey
keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.data
.clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()? // data
+ mem::size_of::<Pubkey>() // owner
+ mem::size_of::<u8>() // executable
+ mem::size_of::<u64>(); // rent_epoch
}
}
Ok(())
}
macro_rules! log{
($logger:ident, $message:expr) => {
if let Ok(mut logger) = $logger.try_borrow_mut() {
if logger.log_enabled() {
logger.log($message);
}
}
};
($logger:ident, $fmt:expr, $($arg:tt)*) => {
if let Ok(mut logger) = $logger.try_borrow_mut() {
logger.log(&format!($fmt, $($arg)*));
}
};
}
pub fn process_instruction(
2018-11-17 17:02:14 -08:00
program_id: &Pubkey,
2020-01-22 17:54:06 -08:00
keyed_accounts: &[KeyedAccount],
instruction_data: &[u8],
2020-04-28 14:33:56 -07:00
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
debug_assert!(bpf_loader::check_id(program_id));
let logger = invoke_context.get_logger();
if keyed_accounts.is_empty() {
log!(logger, "No account keys");
return Err(InstructionError::NotEnoughAccountKeys);
}
if is_executable(keyed_accounts)? {
2020-01-22 17:54:06 -08:00
let mut keyed_accounts_iter = keyed_accounts.iter();
let program = next_keyed_account(&mut keyed_accounts_iter)?;
2020-01-22 17:54:06 -08:00
let parameter_accounts = keyed_accounts_iter.as_slice();
let parameter_bytes = serialize_parameters(
program.unsigned_key(),
parameter_accounts,
&instruction_data,
)?;
{
let program_account = program.try_account_ref_mut()?;
let (mut vm, heap_region) =
match create_vm(&program_account.data, &parameter_accounts, invoke_context) {
Ok(info) => info,
Err(e) => {
log!(logger, "Failed to create BPF VM: {}", e);
return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
}
};
log!(logger, "Call BPF program {}", program.unsigned_key());
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
Ok(status) => {
if status != SUCCESS {
let error: InstructionError = status.into();
log!(
logger,
"BPF program {} failed: {}",
program.unsigned_key(),
error
);
return Err(error);
}
}
Err(error) => {
log!(
logger,
"BPF program {} failed: {}",
program.unsigned_key(),
error
);
return match error {
EbpfError::UserError(BPFError::SyscallError(
SyscallError::InstructionError(error),
)) => Err(error),
_ => Err(BPFLoaderError::VirtualMachineFailedToRunProgram.into()),
};
}
}
}
deserialize_parameters(parameter_accounts, &parameter_bytes)?;
log!(logger, "BPF program {} success", program.unsigned_key());
} else if !keyed_accounts.is_empty() {
match limited_deserialize(instruction_data)? {
LoaderInstruction::Write { offset, bytes } => {
2020-01-22 17:54:06 -08:00
let mut keyed_accounts_iter = keyed_accounts.iter();
let program = next_keyed_account(&mut keyed_accounts_iter)?;
if program.signer_key().is_none() {
log!(logger, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature);
}
let offset = offset as usize;
let len = bytes.len();
if program.data_len()? < offset + len {
log!(
logger,
"Write overflow: {} < {}",
program.data_len()?,
offset + len
);
return Err(InstructionError::AccountDataTooSmall);
}
program.try_account_ref_mut()?.data[offset..offset + len].copy_from_slice(&bytes);
}
LoaderInstruction::Finalize => {
2020-01-22 17:54:06 -08:00
let mut keyed_accounts_iter = keyed_accounts.iter();
let program = next_keyed_account(&mut keyed_accounts_iter)?;
if program.signer_key().is_none() {
log!(logger, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature);
}
if let Err(e) = check_elf(&program.try_account_ref()?.data) {
log!(logger, "{}", e);
2019-12-04 12:03:29 -08:00
return Err(InstructionError::InvalidAccountData);
}
program.try_account_ref_mut()?.executable = true;
log!(
logger,
"Finalized account {:?}",
program.signer_key().unwrap()
);
}
}
}
2018-12-03 13:32:31 -08:00
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
2020-04-28 14:33:56 -07:00
use solana_sdk::{
account::Account,
entrypoint_native::{Logger, ProcessInstruction},
instruction::CompiledInstruction,
message::Message,
rent::Rent,
2020-04-28 14:33:56 -07:00
};
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc};
2020-04-28 14:33:56 -07:00
#[derive(Debug, Default)]
2020-05-08 12:24:36 -07:00
pub struct MockInvokeContext {
key: Pubkey,
mock_logger: MockLogger,
2020-05-08 12:24:36 -07:00
}
2020-04-28 14:33:56 -07:00
impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
Ok(())
}
fn pop(&mut self) {}
fn verify_and_update(
&mut self,
_message: &Message,
_instruction: &CompiledInstruction,
_accounts: &[Rc<RefCell<Account>>],
) -> Result<(), InstructionError> {
Ok(())
}
2020-05-08 12:24:36 -07:00
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
Ok(&self.key)
}
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
&[]
}
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.mock_logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
}
}
#[derive(Debug, Default, Clone)]
pub struct MockLogger {
pub log: Rc<RefCell<Vec<String>>>,
}
impl Logger for MockLogger {
fn log_enabled(&self) -> bool {
true
}
fn log(&mut self, message: &str) {
self.log.borrow_mut().push(message.to_string());
}
2020-04-28 14:33:56 -07:00
}
Fix hygiene issues in `declare_program!` and `declare_loader!` The `declare_program!` and `declare_loader!` macros both expand to new macro definitions (based on the `$name` argument). These 'inner' macros make use of the special `$crate` metavariable to access items in the crate where the 'inner' macros is defined. However, this only works due to a bug in rustc. When a macro is expanded, all `$crate` tokens in its output are 'marked' as being resolved in the defining crate of that macro. An inner macro (including the body of its arms) is 'just' another set of tokens that appears in the body of the outer macro, so any `$crate` identifiers used there are resolved relative to the 'outer' macro. For example, consider the following code: ```rust macro_rules! outer { () => { macro_rules! inner { () => { $crate::Foo } } } } ``` The path `$crate::Foo` will be resolved relative to the crate that defines `outer`, **not** the crate which defines `inner`. However, rustc currently loses this extra resolution information (referred to as 'hygiene' information) when a crate is serialized. In the above example, this means that the macro `inner` (which gets defined in whatever crate invokes `outer!`) will behave differently depending on which crate it is invoked from: When `inner` is invoked from the same crate in which it is defined, the hygiene information will still be available, which will cause `$crate::Foo` to be resolved in the crate which defines 'outer'. When `inner` is invoked from a different crate, it will be loaded from the metadata of the crate which defines 'inner'. Since the hygiene information is currently lost, rust will 'forget' that `$crate::Foo` is supposed to be resolved in the context of 'outer'. Instead, it will be resolved relative to the crate which defines 'inner', which can cause incorrect code to compile. This bug will soon be fixed in rust (see https://github.com/rust-lang/rust/pull/72121), which will break `declare_program!` and `declare_loader!`. Fortunately, it's possible to obtain the desired behavior (`$crate` resolving in the context of the 'inner' macro) by use of a procedural macro. This commit adds a `respan!` proc-macro to the `sdk/macro` crate. Using the newly-stabilized (on Nightly) `Span::resolved_at` method, the `$crate` identifier can be made to be resolved in the context of the proper crate. Since `Span::resolved_at` is only stable on the latest nightly, referencing it on an earlier version of Rust will cause a compilation error. This requires the `rustversion` crate to be used, which allows conditionally compiling code epending on the Rust compiler version in use. Since this method is already stabilized in the latest nightly, there will never be a situation where the hygiene bug is fixed (e.g. https://github.com/rust-lang/rust/pull/72121) is merged but we are unable to call `Span::resolved_at`.
2020-06-19 22:42:11 -07:00
#[rustversion::since(1.46.0)]
#[test]
fn test_bpf_loader_same_crate() {
// Ensure that we can invoke this macro from the same crate
// where it is defined.
solana_bpf_loader_program!();
}
#[test]
#[should_panic(expected = "ExceededMaxInstructions(10)")]
fn test_bpf_loader_non_terminating_program() {
#[rustfmt::skip]
let program = &[
2019-02-22 16:27:19 -08:00
0x07, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r6 + 1
0x05, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, // goto -2
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];
let input = &mut [0x00];
let mut vm = EbpfVm::<BPFError>::new(None).unwrap();
vm.set_verifier(bpf_verifier::check).unwrap();
2019-02-22 16:27:19 -08:00
vm.set_max_instruction_count(10).unwrap();
vm.set_program(program).unwrap();
2019-05-24 16:21:42 -07:00
vm.execute_program(input, &[], &[]).unwrap();
}
#[test]
fn test_bpf_loader_write() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
2020-01-22 17:54:06 -08:00
let program_account = Account::new_ref(1, 0, &program_id);
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
let instruction_data = bincode::serialize(&LoaderInstruction::Write {
offset: 3,
bytes: vec![1, 2, 3],
})
.unwrap();
// Case: Empty keyed accounts
assert_eq!(
Err(InstructionError::NotEnoughAccountKeys),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&[],
2020-04-28 14:33:56 -07:00
&instruction_data,
&mut MockInvokeContext::default()
)
);
// Case: Not signed
assert_eq!(
Err(InstructionError::MissingRequiredSignature),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
);
// Case: Write bytes to an offset
#[allow(unused_mut)]
2020-01-22 17:54:06 -08:00
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
keyed_accounts[0].account.borrow_mut().data = vec![0; 6];
assert_eq!(
Ok(()),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
);
assert_eq!(
vec![0, 0, 0, 1, 2, 3],
keyed_accounts[0].account.borrow().data
);
// Case: Overflow
#[allow(unused_mut)]
2020-01-22 17:54:06 -08:00
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
keyed_accounts[0].account.borrow_mut().data = vec![0; 5];
assert_eq!(
Err(InstructionError::AccountDataTooSmall),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
);
}
#[test]
fn test_bpf_loader_finalize() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
2019-12-04 12:03:29 -08:00
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut elf = Vec::new();
let rent = Rent::default();
2019-12-04 12:03:29 -08:00
file.read_to_end(&mut elf).unwrap();
2020-01-22 17:54:06 -08:00
let program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id);
program_account.borrow_mut().data = elf;
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
// Case: Empty keyed accounts
assert_eq!(
Err(InstructionError::NotEnoughAccountKeys),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&[],
2020-04-28 14:33:56 -07:00
&instruction_data,
&mut MockInvokeContext::default()
)
);
// Case: Not signed
assert_eq!(
Err(InstructionError::MissingRequiredSignature),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
);
// Case: Finalize
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
assert_eq!(
Ok(()),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
);
assert!(keyed_accounts[0].account.borrow().executable);
program_account.borrow_mut().executable = false; // Un-finalize the account
2019-12-04 12:03:29 -08:00
// Case: Finalize
program_account.borrow_mut().data[0] = 0; // bad elf
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
2019-12-04 12:03:29 -08:00
assert_eq!(
Err(InstructionError::InvalidAccountData),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&instruction_data,
&mut MockInvokeContext::default()
)
2019-12-04 12:03:29 -08:00
);
}
#[test]
fn test_bpf_loader_invoke_main() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
2020-01-22 17:54:06 -08:00
let program_account = Account::new_ref(1, 0, &program_id);
program_account.borrow_mut().data = elf;
program_account.borrow_mut().executable = true;
2020-01-22 17:54:06 -08:00
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
// Case: Empty keyed accounts
assert_eq!(
Err(InstructionError::NotEnoughAccountKeys),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&[],
&[],
2020-04-28 14:33:56 -07:00
&mut MockInvokeContext::default()
)
);
// Case: Only a program account
assert_eq!(
Ok(()),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
2020-04-28 14:33:56 -07:00
&mut MockInvokeContext::default()
)
);
// Case: Account not executable
keyed_accounts[0].account.borrow_mut().executable = false;
assert_eq!(
Err(InstructionError::InvalidInstructionData),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
2020-04-28 14:33:56 -07:00
&mut MockInvokeContext::default()
)
);
keyed_accounts[0].account.borrow_mut().executable = true;
// Case: With program and parameter account
2020-01-22 17:54:06 -08:00
let parameter_account = Account::new_ref(1, 0, &program_id);
keyed_accounts.push(KeyedAccount::new(&program_key, false, &parameter_account));
assert_eq!(
Ok(()),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
2020-04-28 14:33:56 -07:00
&mut MockInvokeContext::default()
)
);
// Case: With duplicate accounts
let duplicate_key = Pubkey::new_rand();
let parameter_account = Account::new_ref(1, 0, &program_id);
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, &parameter_account));
assert_eq!(
Ok(()),
2020-04-28 14:33:56 -07:00
process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
2020-04-28 14:33:56 -07:00
&mut MockInvokeContext::default()
)
);
}
/// fuzzing utility function
fn fuzz<F>(
bytes: &[u8],
outer_iters: usize,
inner_iters: usize,
offset: Range<usize>,
value: Range<u8>,
work: F,
) where
F: Fn(&mut [u8]),
{
let mut rng = rand::thread_rng();
for _ in 0..outer_iters {
let mut mangled_bytes = bytes.to_vec();
for _ in 0..inner_iters {
let offset = rng.gen_range(offset.start, offset.end);
let value = rng.gen_range(value.start, value.end);
mangled_bytes[offset] = value;
work(&mut mangled_bytes);
}
}
}
#[test]
#[ignore]
fn test_fuzz() {
let program_id = Pubkey::new_rand();
let program_key = Pubkey::new_rand();
// Create program account
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
// Mangle the whole file
fuzz(
&elf,
1_000_000_000,
100,
0..elf.len(),
0..255,
|bytes: &mut [u8]| {
let program_account = Account::new_ref(1, 0, &program_id);
program_account.borrow_mut().data = bytes.to_vec();
program_account.borrow_mut().executable = true;
let parameter_account = Account::new_ref(1, 0, &program_id);
let keyed_accounts = vec![
KeyedAccount::new(&program_key, false, &program_account),
KeyedAccount::new(&program_key, false, &parameter_account),
];
let _result = process_instruction(
&bpf_loader::id(),
&keyed_accounts,
&[],
&mut MockInvokeContext::default(),
);
},
);
}
}