use {
common::{LoaderV3Features, process_instruction, process_instruction_with_setup},
rand::Rng,
solana_account::{AccountSharedData, WritableAccount},
solana_bpf_loader_program::test_utils,
solana_instruction::AccountMeta,
solana_instruction_error::InstructionError,
solana_pubkey::Pubkey,
solana_rent::Rent,
solana_sdk_ids::{bpf_loader, bpf_loader_deprecated},
std::{fs::File, io::Read, ops::Range},
};
mod common;
fn load_program_account_from_elf(loader_id: &Pubkey, path: &str) -> AccountSharedData {
let mut file = File::open(path).expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let rent = Rent::default();
let mut program_account =
AccountSharedData::new_with_data(rent.minimum_balance(elf.len()), elf, loader_id);
program_account.set_executable(true);
program_account
}
#[test]
fn test_bpf_loader_invoke_main() {
let loader_id = bpf_loader::id();
let program_id = Pubkey::new_unique();
let program_account =
load_program_account_from_elf(&loader_id, "test_elfs/out/sbpfv3_return_ok.so");
let parameter_id = Pubkey::new_unique();
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
let parameter_meta = AccountMeta {
pubkey: parameter_id,
is_signer: false,
is_writable: false,
};
process_instruction(
&loader_id,
&[],
Vec::new(),
Vec::new(),
Err(InstructionError::UnsupportedProgramId),
);
process_instruction(
&program_id,
&[],
vec![(program_id, program_account.clone())],
Vec::new(),
Ok(()),
);
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account.clone()),
(parameter_id, parameter_account.clone()),
],
vec![parameter_meta.clone()],
Ok(()),
);
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account.clone()),
(parameter_id, parameter_account.clone()),
],
vec![parameter_meta.clone(), parameter_meta],
Ok(()),
);
process_instruction_with_setup(
&program_id,
&[],
vec![(program_id, program_account)],
Vec::new(),
LoaderV3Features::all_enabled(),
Err(InstructionError::ProgramFailedToComplete),
|invoke_context| {
invoke_context.mock_set_remaining(0);
test_utils::load_all_invoked_programs(invoke_context);
},
);
process_instruction_with_setup(
&program_id,
&[],
vec![(program_id, parameter_account.clone())],
Vec::new(),
LoaderV3Features::all_enabled(),
Err(InstructionError::UnsupportedProgramId),
|invoke_context| {
test_utils::load_all_invoked_programs(invoke_context);
},
);
process_instruction(
&program_id,
&[],
vec![(program_id, parameter_account)],
Vec::new(),
Err(InstructionError::UnsupportedProgramId),
);
}
#[test]
fn test_bpf_loader_serialize_unaligned() {
let loader_id = bpf_loader_deprecated::id();
let program_id = Pubkey::new_unique();
let program_account =
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_unaligned.so");
let parameter_id = Pubkey::new_unique();
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
let parameter_meta = AccountMeta {
pubkey: parameter_id,
is_signer: false,
is_writable: false,
};
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account.clone()),
(parameter_id, parameter_account.clone()),
],
vec![parameter_meta.clone()],
Ok(()),
);
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account),
(parameter_id, parameter_account),
],
vec![parameter_meta.clone(), parameter_meta],
Ok(()),
);
}
#[test]
fn test_bpf_loader_serialize_aligned() {
let loader_id = bpf_loader::id();
let program_id = Pubkey::new_unique();
let program_account =
load_program_account_from_elf(&loader_id, "test_elfs/out/noop_aligned.so");
let parameter_id = Pubkey::new_unique();
let parameter_account = AccountSharedData::new(1, 0, &loader_id);
let parameter_meta = AccountMeta {
pubkey: parameter_id,
is_signer: false,
is_writable: false,
};
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account.clone()),
(parameter_id, parameter_account.clone()),
],
vec![parameter_meta.clone()],
Ok(()),
);
process_instruction(
&program_id,
&[],
vec![
(program_id, program_account),
(parameter_id, parameter_account),
],
vec![parameter_meta.clone(), parameter_meta],
Ok(()),
);
}
fn fuzz<F>(
bytes: Vec<u8>,
outer_iters: usize,
inner_iters: usize,
offset: Range<usize>,
value: Range<u8>,
work: F,
) where
F: Fn(Vec<u8>),
{
let mut rng = rand::rng();
for _ in 0..outer_iters {
let mut mangled_bytes = bytes.clone();
for _ in 0..inner_iters {
let offset = rng.random_range(offset.start..offset.end);
let value = rng.random_range(value.start..value.end);
*mangled_bytes.get_mut(offset).unwrap() = value;
work(mangled_bytes.clone());
}
}
}
#[test]
#[ignore]
fn test_fuzz() {
let loader_id = bpf_loader::id();
let program_id = Pubkey::new_unique();
let mut file = File::open("test_elfs/out/sbpfv3_return_ok.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let elf_len = elf.len();
fuzz(
elf,
1_000_000_000,
100,
0..elf_len,
0..255,
|bytes: Vec<u8>| {
let mut program_account = AccountSharedData::new_with_data(1, bytes, &loader_id);
program_account.set_executable(true);
process_instruction(
&program_id,
&[],
vec![(program_id, program_account)],
Vec::new(),
Ok(()),
);
},
);
}