Disallow deployment of deprecated _sol_alloc_free syscall (#24986)

* Disallow deployment of deprecated _sol_alloc_free syscall

* remove cli argument
This commit is contained in:
Jack May 2022-05-10 22:22:49 -07:00 committed by GitHub
parent 4c7a030f13
commit 8f1d4c1665
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 223 additions and 43 deletions

View File

@ -2044,7 +2044,7 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
reject_broken_elfs: true,
..Config::default()
},
register_syscalls(&mut invoke_context).unwrap(),
register_syscalls(&mut invoke_context, true).unwrap(),
)
.map_err(|err| format!("ELF error: {}", err))?;

View File

@ -109,7 +109,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
register_syscalls(invoke_context).unwrap(),
register_syscalls(invoke_context, true).unwrap(),
)
.unwrap();
Executable::<BpfError, ThisInstructionMeter>::jit_compile(&mut executable).unwrap();
@ -228,7 +228,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
register_syscalls(invoke_context).unwrap(),
register_syscalls(invoke_context, true).unwrap(),
)
.unwrap();
@ -263,7 +263,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
&elf,
None,
Config::default(),
register_syscalls(invoke_context).unwrap(),
register_syscalls(invoke_context, true).unwrap(),
)
.unwrap();
let compute_meter = invoke_context.get_compute_meter();

View File

@ -9,7 +9,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
// Confirm large allocation fails
void *ptr = sol_calloc(1, UINT64_MAX);
if (ptr != NULL) {
sol_log("Error: Alloc of very larger buffer should fail");
sol_log("Error: Alloc of very large type should fail");
sol_panic();
}
}
@ -18,7 +18,7 @@ extern uint64_t entrypoint(const uint8_t *input) {
// Confirm large allocation fails
void *ptr = sol_calloc(UINT64_MAX, 1);
if (ptr != NULL) {
sol_log("Error: Alloc of very larger buffer should fail");
sol_log("Error: Alloc of very large number of items should fail");
sol_panic();
}
}
@ -42,16 +42,37 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_log("Error: Alloc failed");
sol_panic();
}
for (int i = 0; i < iters; i++) {
for (uint64_t i = 0; i < iters; i++) {
*(ptr + i) = i;
}
for (int i = 0; i < iters; i++) {
for (uint64_t i = 0; i < iters; i++) {
sol_assert(*(ptr + i) == i);
}
sol_log_64(0x3, 0, 0, 0, *(ptr + 42));
sol_assert(*(ptr + 42) == 42);
sol_free(ptr);
}
// Alloc to exhaustion
for (uint64_t i = 0; i < 31; i++) {
uint8_t *ptr = sol_calloc(1024, 1);
if (ptr == NULL) {
sol_log("large alloc failed");
sol_panic();
}
}
for (uint64_t i = 0; i < 760; i++) {
uint8_t *ptr = sol_calloc(1, 1);
if (ptr == NULL) {
sol_log("small alloc failed");
sol_panic();
}
}
uint8_t *ptr = sol_calloc(1, 1);
if (ptr != NULL) {
sol_log("final alloc did not fail");
sol_panic();
}
return SUCCESS;
}

View File

@ -1,7 +1,7 @@
//! Example Rust-based BPF program tests loop iteration
extern crate solana_program;
use solana_program::{custom_panic_default, entrypoint::SUCCESS};
use solana_program::{custom_heap_default, custom_panic_default, entrypoint::SUCCESS};
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
@ -50,6 +50,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -3,7 +3,9 @@
#[macro_use]
extern crate alloc;
use {
solana_program::{custom_panic_default, entrypoint::SUCCESS, log::sol_log_64, msg},
solana_program::{
custom_heap_default, custom_panic_default, entrypoint::SUCCESS, log::sol_log_64, msg,
},
std::{alloc::Layout, mem},
};
@ -83,6 +85,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -1,6 +1,8 @@
//! Example Rust-based BPF program that tests call depth and stack usage
use solana_program::{custom_panic_default, entrypoint::SUCCESS, log::sol_log_64, msg};
use solana_program::{
custom_heap_default, custom_panic_default, entrypoint::SUCCESS, log::sol_log_64, msg,
};
#[inline(never)]
pub fn recurse(data: &mut [u8]) {
@ -26,4 +28,5 @@ pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();

View File

@ -1,7 +1,9 @@
//! Example Rust-based BPF program tests loop iteration
extern crate solana_program;
use solana_program::{custom_panic_default, entrypoint::SUCCESS, log::sol_log_64};
use solana_program::{
custom_heap_default, custom_panic_default, entrypoint::SUCCESS, log::sol_log_64,
};
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
@ -18,6 +20,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -2,7 +2,7 @@
mod helper;
extern crate solana_program;
use solana_program::{custom_panic_default, entrypoint::SUCCESS, msg};
use solana_program::{custom_heap_default, custom_panic_default, entrypoint::SUCCESS, msg};
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
@ -26,6 +26,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -6,7 +6,7 @@
extern crate compiler_builtins;
use {
solana_bpf_rust_mem::{run_mem_tests, MemOps},
solana_program::{custom_panic_default, entrypoint::SUCCESS},
solana_program::{custom_heap_default, custom_panic_default, entrypoint::SUCCESS},
};
#[no_mangle]
@ -38,4 +38,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();

View File

@ -3,7 +3,9 @@
extern crate solana_program;
use {
solana_bpf_rust_param_passing_dep::{Data, TestDep},
solana_program::{custom_panic_default, entrypoint::SUCCESS, log::sol_log_64},
solana_program::{
custom_heap_default, custom_panic_default, entrypoint::SUCCESS, log::sol_log_64,
},
};
#[no_mangle]
@ -25,6 +27,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
SUCCESS
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -1,7 +1,7 @@
//! Secp256k1Recover Syscall test
extern crate solana_program;
use solana_program::{custom_panic_default, msg};
use solana_program::{custom_heap_default, custom_panic_default, msg};
fn test_secp256k1_recover() {
use solana_program::secp256k1_recover::secp256k1_recover;
@ -41,4 +41,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
0
}
custom_heap_default!();
custom_panic_default!();

View File

@ -1,7 +1,7 @@
//! SHA Syscall test
extern crate solana_program;
use solana_program::{custom_panic_default, msg};
use solana_program::{custom_heap_default, custom_panic_default, msg};
fn test_sha256_hasher() {
use solana_program::hash::{hashv, Hasher};
@ -39,6 +39,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
0
}
custom_heap_default!();
custom_panic_default!();
#[cfg(test)]

View File

@ -2,7 +2,7 @@
extern crate solana_program;
use {
solana_program::{custom_panic_default, msg},
solana_program::{custom_heap_default, custom_panic_default, msg},
solana_zk_token_sdk::zk_token_elgamal::{
ops,
pod::{ElGamalCiphertext, Zeroable},
@ -50,4 +50,5 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
0
}
custom_heap_default!();
custom_panic_default!();

View File

@ -40,7 +40,7 @@ use {
},
},
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
account::{AccountSharedData, ReadableAccount, WritableAccount},
account_utils::StateMut,
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
client::SyncClient,
@ -226,7 +226,7 @@ fn run_program(name: &str) -> u64 {
&data,
None,
config,
register_syscalls(invoke_context).unwrap(),
register_syscalls(invoke_context, false /* no sol_alloc_free */).unwrap(),
)
.unwrap();
Executable::<BpfError, ThisInstructionMeter>::jit_compile(&mut executable).unwrap();
@ -563,6 +563,10 @@ fn test_program_bpf_loader_deprecated() {
.accounts
.remove(&solana_sdk::feature_set::disable_deprecated_loader::id())
.unwrap();
genesis_config
.accounts
.remove(&solana_sdk::feature_set::disable_deploy_of_alloc_free_syscall::id())
.unwrap();
let mut bank = Bank::new_for_tests(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
bank.add_builtin(&name, &id, entrypoint);
@ -581,6 +585,84 @@ fn test_program_bpf_loader_deprecated() {
}
}
#[test]
fn test_sol_alloc_free_no_longer_deployable() {
solana_logger::setup();
let program_keypair = Keypair::new();
let program_address = program_keypair.pubkey();
let loader_address = bpf_loader_deprecated::id();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new_for_tests(&genesis_config);
bank.deactivate_feature(&solana_sdk::feature_set::disable_deprecated_loader::id());
let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
bank.add_builtin(&name, &id, entrypoint);
// Populate loader account with elf that depends on _sol_alloc_free syscall
let elf = read_bpf_program("solana_bpf_rust_deprecated_loader");
let mut program_account = AccountSharedData::new(1, elf.len(), &loader_address);
program_account
.data_as_mut_slice()
.get_mut(..)
.unwrap()
.copy_from_slice(&elf);
bank.store_account(&program_address, &program_account);
let finalize_tx = Transaction::new(
&[&mint_keypair, &program_keypair],
Message::new(
&[loader_instruction::finalize(
&program_keypair.pubkey(),
&loader_address,
)],
Some(&mint_keypair.pubkey()),
),
bank.last_blockhash(),
);
let invoke_tx = Transaction::new(
&[&mint_keypair],
Message::new(
&[Instruction::new_with_bytes(
program_address,
&[1],
vec![AccountMeta::new(mint_keypair.pubkey(), true)],
)],
Some(&mint_keypair.pubkey()),
),
bank.last_blockhash(),
);
// Try and deploy a program that depends on _sol_alloc_free
assert_eq!(
bank.process_transaction(&finalize_tx).unwrap_err(),
TransactionError::InstructionError(0, InstructionError::InvalidAccountData)
);
// Enable _sol_alloc_free syscall
bank.deactivate_feature(&solana_sdk::feature_set::disable_deploy_of_alloc_free_syscall::id());
bank.clear_signatures();
// Try and finalize the program now that sol_alloc_free is re-enabled
assert!(bank.process_transaction(&finalize_tx).is_ok());
// invoke the program
assert!(bank.process_transaction(&invoke_tx).is_ok());
// disable _sol_alloc_free
bank.deactivate_feature(&solana_sdk::feature_set::disable_deploy_of_alloc_free_syscall::id());
bank.clear_signatures();
// invoke should still succeed on execute because the program is already deployed
assert!(bank.process_transaction(&invoke_tx).is_ok());
}
#[test]
fn test_program_bpf_duplicate_accounts() {
solana_logger::setup();
@ -1437,7 +1519,7 @@ fn assert_instruction_count() {
{
programs.extend_from_slice(&[
("solana_bpf_rust_128bit", 584),
("solana_bpf_rust_alloc", 4459),
("solana_bpf_rust_alloc", 4581),
("solana_bpf_rust_custom_heap", 469),
("solana_bpf_rust_dep_crate", 2),
("solana_bpf_rust_external_spend", 338),
@ -1450,7 +1532,7 @@ fn assert_instruction_count() {
("solana_bpf_rust_rand", 429),
("solana_bpf_rust_sanity", 52290),
("solana_bpf_rust_secp256k1_recover", 25707),
("solana_bpf_rust_sha", 25251),
("solana_bpf_rust_sha", 25265),
]);
}

View File

@ -42,9 +42,10 @@ use {
entrypoint::{HEAP_LENGTH, SUCCESS},
feature_set::{
cap_accounts_data_len, disable_bpf_deprecated_load_instructions,
disable_bpf_unresolved_symbols_at_runtime, disable_deprecated_loader,
do_support_realloc, error_on_syscall_bpf_function_hash_collisions,
reduce_required_deploy_balance, reject_callx_r10, requestable_heap_size,
disable_bpf_unresolved_symbols_at_runtime, disable_deploy_of_alloc_free_syscall,
disable_deprecated_loader, do_support_realloc,
error_on_syscall_bpf_function_hash_collisions, reduce_required_deploy_balance,
reject_callx_r10, requestable_heap_size,
},
instruction::{AccountMeta, InstructionError},
loader_instruction::LoaderInstruction,
@ -109,9 +110,11 @@ pub fn create_executor(
invoke_context: &mut InvokeContext,
use_jit: bool,
reject_deployment_of_broken_elfs: bool,
disable_deploy_of_alloc_free_syscall: bool,
) -> Result<Arc<BpfExecutor>, InstructionError> {
let mut register_syscalls_time = Measure::start("register_syscalls_time");
let register_syscall_result = syscalls::register_syscalls(invoke_context);
let register_syscall_result =
syscalls::register_syscalls(invoke_context, disable_deploy_of_alloc_free_syscall);
register_syscalls_time.stop();
invoke_context.timings.create_executor_register_syscalls_us = invoke_context
.timings
@ -383,6 +386,7 @@ fn process_instruction_common(
invoke_context,
use_jit,
false,
true, // allow _sol_alloc_free syscall for execution
)?;
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
@ -626,6 +630,9 @@ fn process_loader_upgradeable_instruction(
invoke_context,
use_jit,
true,
invoke_context
.feature_set
.is_active(&disable_deploy_of_alloc_free_syscall::id()),
)?;
invoke_context.update_executor(&new_program_id, executor);
@ -805,6 +812,9 @@ fn process_loader_upgradeable_instruction(
invoke_context,
use_jit,
true,
invoke_context
.feature_set
.is_active(&disable_deploy_of_alloc_free_syscall::id()),
)?;
invoke_context.update_executor(&new_program_id, executor);
@ -1100,8 +1110,16 @@ fn process_loader_instruction(
ic_msg!(invoke_context, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature);
}
let executor =
create_executor(first_instruction_account, 0, invoke_context, use_jit, true)?;
let executor = create_executor(
first_instruction_account,
0,
invoke_context,
use_jit,
true,
invoke_context
.feature_set
.is_active(&disable_deploy_of_alloc_free_syscall::id()),
)?;
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let mut program =

View File

@ -128,6 +128,7 @@ macro_rules! register_feature_gated_syscall {
pub fn register_syscalls(
invoke_context: &mut InvokeContext,
disable_deploy_of_alloc_free_syscall: bool,
) -> Result<SyscallRegistry, EbpfError<BpfError>> {
let secp256k1_recover_syscall_enabled = invoke_context
.feature_set
@ -328,7 +329,9 @@ pub fn register_syscalls(
)?;
// Memory allocator
syscall_registry.register_syscall_by_name(
register_feature_gated_syscall!(
syscall_registry,
!disable_deploy_of_alloc_free_syscall,
b"sol_alloc_free_",
SyscallAllocFree::init,
SyscallAllocFree::call,

View File

@ -270,7 +270,7 @@ native machine code before execting it in the virtual machine.",
file.seek(SeekFrom::Start(0)).unwrap();
let mut contents = Vec::new();
file.read_to_end(&mut contents).unwrap();
let syscall_registry = register_syscalls(&mut invoke_context).unwrap();
let syscall_registry = register_syscalls(&mut invoke_context, true).unwrap();
let mut executable = if magic == [0x7f, 0x45, 0x4c, 0x46] {
Executable::<BpfError, ThisInstructionMeter>::from_elf(
&contents,

View File

@ -56,30 +56,63 @@ static size_t sol_strlen(const char *s) {
}
/**
* Internal memory alloc/free function
* Start address of the memory region used for program heap.
*/
#ifndef SOL_SBFV2
void* sol_alloc_free_(uint64_t, void *);
#else
typedef void*(*sol_alloc_free__pointer_type)(uint64_t, void *);
static void* sol_alloc_free_(uint64_t arg1, void * arg2) {
sol_alloc_free__pointer_type sol_alloc_free__pointer = (sol_alloc_free__pointer_type) 2213547663;
return sol_alloc_free__pointer(arg1, arg2);
}
#endif
#define HEAP_START_ADDRESS (0x300000000)
/**
* Length of the heap memory region used for program heap.
*/
#define HEAP_LENGTH (32 * 1024)
/**
* Alloc zero-initialized memory
*/
static void *sol_calloc(size_t nitems, size_t size) {
return sol_alloc_free_(nitems * size, 0);
// Bump allocator
uint64_t* pos_ptr = (uint64_t*)HEAP_START_ADDRESS;
uint64_t pos = *pos_ptr;
if (pos == 0) {
/** First time, set starting position */
pos = HEAP_START_ADDRESS + HEAP_LENGTH;
}
uint64_t bytes = (uint64_t)(nitems * size);
if (size == 0 ||
!(nitems == 0 || size == 0) &&
!(nitems == bytes / size)) {
/** Overflow */
return NULL;
}
if (pos < bytes) {
/** Saturated */
pos = 0;
} else {
pos -= bytes;
}
uint64_t align = size;
align--;
align |= align >> 1;
align |= align >> 2;
align |= align >> 4;
align |= align >> 8;
align |= align >> 16;
align |= align >> 32;
align++;
pos &= ~(align - 1);
if (pos < HEAP_START_ADDRESS + sizeof(uint8_t*)) {
return NULL;
}
*pos_ptr = pos;
return (void*)pos;
}
/**
* Deallocates the memory previously allocated by sol_calloc
*/
static void sol_free(void *ptr) {
(void) sol_alloc_free_(0, ptr);
// I'm a bump allocator, I don't free
}
#ifdef __cplusplus

View File

@ -396,6 +396,10 @@ pub mod stake_raise_minimum_delegation_to_1_sol {
solana_sdk::declare_id!("4xmyBuR2VCXzy9H6qYpH9ckfgnTuMDQFPFBfTs4eBCY1");
}
pub mod disable_deploy_of_alloc_free_syscall {
solana_sdk::declare_id!("79HWsX9rpnnJBPcdNURVqygpMAfxdrAirzAGAVmf92im");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -488,6 +492,7 @@ lazy_static! {
(stake_allow_zero_undelegated_amount::id(), "Allow zero-lamport undelegated amount for initialized stakes #24670"),
(require_static_program_ids_in_transaction::id(), "require static program ids in versioned transactions"),
(stake_raise_minimum_delegation_to_1_sol::id(), "Raise minimum stake delegation to 1.0 SOL #24357"),
(disable_deploy_of_alloc_free_syscall::id(), "disable new deployments of deprecated sol_alloc_free_ syscall"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()