diff --git a/cli/src/cli.rs b/cli/src/cli.rs index d0ffad4cbc6442..59ddd630fac162 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -164,6 +164,7 @@ pub enum CliCommand { use_deprecated_loader: bool, allow_excessive_balance: bool, skip_fee_check: bool, + enable_sol_alloc_free: bool, }, Program(ProgramCliCommand), // Stake Commands @@ -650,6 +651,7 @@ pub fn parse_command( use_deprecated_loader: matches.is_present("use_deprecated_loader"), allow_excessive_balance: matches.is_present("allow_excessive_balance"), skip_fee_check, + enable_sol_alloc_free: matches.is_present("enable_sol_alloc_free"), }, signers, }) @@ -1029,6 +1031,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { use_deprecated_loader, allow_excessive_balance, skip_fee_check, + enable_sol_alloc_free, } => process_deploy( rpc_client, config, @@ -1037,6 +1040,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { *use_deprecated_loader, *allow_excessive_balance, *skip_fee_check, + *enable_sol_alloc_free, ), CliCommand::Program(program_subcommand) => { process_program_subcommand(rpc_client, config, program_subcommand) @@ -1871,6 +1875,7 @@ mod tests { use_deprecated_loader: false, allow_excessive_balance: false, skip_fee_check: false, + enable_sol_alloc_free: false, }, signers: vec![read_keypair_file(&keypair_file).unwrap().into()], } @@ -1894,6 +1899,7 @@ mod tests { use_deprecated_loader: false, allow_excessive_balance: false, skip_fee_check: false, + enable_sol_alloc_free: false, }, signers: vec![ read_keypair_file(&keypair_file).unwrap().into(), @@ -2289,6 +2295,7 @@ mod tests { use_deprecated_loader: false, allow_excessive_balance: false, skip_fee_check: false, + enable_sol_alloc_free: false, }; config.output_format = OutputFormat::JsonCompact; let result = process_command(&config); @@ -2310,6 +2317,7 @@ mod tests { use_deprecated_loader: false, allow_excessive_balance: false, skip_fee_check: false, + enable_sol_alloc_free: false, }; assert!(process_command(&config).is_err()); } diff --git a/cli/src/program.rs b/cli/src/program.rs index 53e82f179af4aa..51911d2d6a82a9 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -67,6 +67,7 @@ pub enum ProgramCliCommand { max_len: Option, allow_excessive_balance: bool, skip_fee_check: bool, + enable_sol_alloc_free: bool, }, WriteBuffer { program_location: String, @@ -75,6 +76,7 @@ pub enum ProgramCliCommand { buffer_authority_signer_index: Option, max_len: Option, skip_fee_check: bool, + enable_sol_alloc_free: bool, }, SetBufferAuthority { buffer_pubkey: Pubkey, @@ -176,6 +178,13 @@ impl ProgramSubCommands for App<'_, '_> { .long("allow-excessive-deploy-account-balance") .takes_value(false) .help("Use the designated program id even if the account already holds a large balance of SOL") + ) + .arg( + Arg::with_name("enable_sol_alloc_free") + .long("enable_sol_alloc_free") + .takes_value(false) + .hidden(true) + .help("Enable support for _sol_alloc_free syscall") ), ) .subcommand( @@ -421,6 +430,13 @@ impl ProgramSubCommands for App<'_, '_> { .long("skip-fee-check") .hidden(true) .takes_value(false) + ) + .arg( + Arg::with_name("enable_sol_alloc_free") + .long("enable_sol_alloc_free") + .takes_value(false) + .hidden(true) + .help("Enable support for _sol_alloc_free syscall") ), ) } @@ -499,6 +515,7 @@ pub fn parse_program_subcommand( max_len, allow_excessive_balance: matches.is_present("allow_excessive_balance"), skip_fee_check, + enable_sol_alloc_free: matches.is_present("enable_sol_alloc_free"), }), signers: signer_info.signers, } @@ -545,6 +562,7 @@ pub fn parse_program_subcommand( .index_of_or_none(buffer_authority_pubkey), max_len, skip_fee_check, + enable_sol_alloc_free: matches.is_present("enable_sol_alloc_free"), }), signers: signer_info.signers, } @@ -694,6 +712,7 @@ pub fn process_program_subcommand( max_len, allow_excessive_balance, skip_fee_check, + enable_sol_alloc_free, } => process_program_deploy( rpc_client, config, @@ -707,6 +726,7 @@ pub fn process_program_subcommand( *max_len, *allow_excessive_balance, *skip_fee_check, + *enable_sol_alloc_free, ), ProgramCliCommand::WriteBuffer { program_location, @@ -715,6 +735,7 @@ pub fn process_program_subcommand( buffer_authority_signer_index, max_len, skip_fee_check, + enable_sol_alloc_free, } => process_write_buffer( rpc_client, config, @@ -724,6 +745,7 @@ pub fn process_program_subcommand( *buffer_authority_signer_index, *max_len, *skip_fee_check, + *enable_sol_alloc_free, ), ProgramCliCommand::SetBufferAuthority { buffer_pubkey, @@ -822,6 +844,7 @@ fn process_program_deploy( max_len: Option, allow_excessive_balance: bool, skip_fee_check: bool, + enable_sol_alloc_free: bool, ) -> ProcessResult { let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?; let (buffer_provided, buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index { @@ -915,7 +938,7 @@ fn process_program_deploy( }; let (program_data, program_len) = if let Some(program_location) = program_location { - let program_data = read_and_verify_elf(program_location)?; + let program_data = read_and_verify_elf(program_location, enable_sol_alloc_free)?; let program_len = program_data.len(); (program_data, program_len) } else if buffer_provided { @@ -1015,6 +1038,7 @@ fn process_write_buffer( buffer_authority_signer_index: Option, max_len: Option, skip_fee_check: bool, + enable_sol_alloc_free: bool, ) -> ProcessResult { // Create ephemeral keypair to use for Buffer account, if not provided let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?; @@ -1059,7 +1083,7 @@ fn process_write_buffer( } } - let program_data = read_and_verify_elf(program_location)?; + let program_data = read_and_verify_elf(program_location, enable_sol_alloc_free)?; let buffer_data_len = if let Some(len) = max_len { len } else { @@ -1670,6 +1694,7 @@ pub fn process_deploy( use_deprecated_loader: bool, allow_excessive_balance: bool, skip_fee_check: bool, + enable_sol_alloc_free: bool, ) -> ProcessResult { // Create ephemeral keypair to use for Buffer account, if not provided let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?; @@ -1679,7 +1704,7 @@ pub fn process_deploy( &buffer_keypair }; - let program_data = read_and_verify_elf(program_location)?; + let program_data = read_and_verify_elf(program_location, enable_sol_alloc_free)?; let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(program_data.len())?; let loader_id = if use_deprecated_loader { bpf_loader_deprecated::id() @@ -2027,7 +2052,10 @@ fn do_process_program_upgrade( Ok(config.output_format.formatted_string(&program_id)) } -fn read_and_verify_elf(program_location: &str) -> Result, Box> { +fn read_and_verify_elf( + program_location: &str, + enable_sol_alloc_free: bool, +) -> Result, Box> { let mut file = File::open(program_location) .map_err(|err| format!("Unable to open program file: {}", err))?; let mut program_data = Vec::new(); @@ -2044,7 +2072,7 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box u64 { @@ -50,6 +50,7 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { SUCCESS } +custom_heap_default!(); custom_panic_default!(); #[cfg(test)] diff --git a/programs/bpf/rust/alloc/src/lib.rs b/programs/bpf/rust/alloc/src/lib.rs index 57d84bad7a60d2..09919b12e45881 100644 --- a/programs/bpf/rust/alloc/src/lib.rs +++ b/programs/bpf/rust/alloc/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/call_depth/src/lib.rs b/programs/bpf/rust/call_depth/src/lib.rs index 888c491d98a2f6..42e32df6d634a2 100644 --- a/programs/bpf/rust/call_depth/src/lib.rs +++ b/programs/bpf/rust/call_depth/src/lib.rs @@ -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!(); diff --git a/programs/bpf/rust/iter/src/lib.rs b/programs/bpf/rust/iter/src/lib.rs index a262deb89f42f7..3fda481a4b24bd 100644 --- a/programs/bpf/rust/iter/src/lib.rs +++ b/programs/bpf/rust/iter/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/many_args/src/lib.rs b/programs/bpf/rust/many_args/src/lib.rs index 0c27b1ba32010a..6d8819b1accbd5 100644 --- a/programs/bpf/rust/many_args/src/lib.rs +++ b/programs/bpf/rust/many_args/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/membuiltins/src/lib.rs b/programs/bpf/rust/membuiltins/src/lib.rs index 0ef07a111044e6..440c89074d3af8 100644 --- a/programs/bpf/rust/membuiltins/src/lib.rs +++ b/programs/bpf/rust/membuiltins/src/lib.rs @@ -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!(); diff --git a/programs/bpf/rust/param_passing/src/lib.rs b/programs/bpf/rust/param_passing/src/lib.rs index 3c5006620f6021..590fbf13f90849 100644 --- a/programs/bpf/rust/param_passing/src/lib.rs +++ b/programs/bpf/rust/param_passing/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/secp256k1_recover/src/lib.rs b/programs/bpf/rust/secp256k1_recover/src/lib.rs index 2e9ccdbdc76b02..58fe2c7b96ba0f 100644 --- a/programs/bpf/rust/secp256k1_recover/src/lib.rs +++ b/programs/bpf/rust/secp256k1_recover/src/lib.rs @@ -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!(); diff --git a/programs/bpf/rust/sha/src/lib.rs b/programs/bpf/rust/sha/src/lib.rs index a3b5e7e7d61a56..bc1579b22bd744 100644 --- a/programs/bpf/rust/sha/src/lib.rs +++ b/programs/bpf/rust/sha/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/zk_token_elgamal/src/lib.rs b/programs/bpf/rust/zk_token_elgamal/src/lib.rs index e48404bf821280..9bffd741118594 100644 --- a/programs/bpf/rust/zk_token_elgamal/src/lib.rs +++ b/programs/bpf/rust/zk_token_elgamal/src/lib.rs @@ -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!(); diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 70e0a96bcd661b..4908c0c9ed4d59 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -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::::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), ]); } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 0f06e2f8461758..696325c6c51b58 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -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, 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 = diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 1a184203da238e..ee7a2347606635 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -127,6 +127,7 @@ macro_rules! register_feature_gated_syscall { pub fn register_syscalls( invoke_context: &mut InvokeContext, + disable_deploy_of_alloc_free_syscall: bool, ) -> Result> { let secp256k1_recover_syscall_enabled = invoke_context .feature_set @@ -306,7 +307,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, diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index ff8f965372cca5..6010336ca40e25 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -185,6 +185,13 @@ native machine code before execting it in the virtual machine.", .short('c') .long("coverage"), ) + .arg( + Arg::new("enable_sol_alloc_free") + .help("Enable support for _sol_alloc_free syscall") + .long("enable_sol_alloc_free") + .takes_value(false) + .hide(true), + ) .get_matches(); log::set_boxed_logger(Box::new(Logger::new(matches.is_present("verbose")))).unwrap(); @@ -270,7 +277,11 @@ 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, + !matches.is_present("enable_sol_alloc_free"), + ) + .unwrap(); let mut executable = if magic == [0x7f, 0x45, 0x4c, 0x46] { Executable::::from_elf( &contents, diff --git a/sdk/bpf/c/inc/sol/string.h b/sdk/bpf/c/inc/sol/string.h index 589676d33669fd..159c9fd9e2df26 100644 --- a/sdk/bpf/c/inc/sol/string.h +++ b/sdk/bpf/c/inc/sol/string.h @@ -56,22 +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. */ -void *sol_alloc_free_(uint64_t size, void *ptr); +#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 diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 04187b29c58f23..f153076d7c9454 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -383,6 +383,10 @@ pub mod stake_allow_zero_undelegated_amount { solana_sdk::declare_id!("sTKz343FM8mqtyGvYWvbLpTThw3ixRM4Xk8QvZ985mw"); } +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 = [ @@ -472,7 +476,8 @@ lazy_static! { (spl_token_v3_4_0::id(), "SPL Token Program version 3.4.0 release #24740"), (spl_associated_token_account_v1_1_0::id(), "SPL Associated Token Account Program version 1.1.0 release #24741"), (default_units_per_instruction::id(), "Default max tx-wide compute units calculated per instruction"), - (stake_allow_zero_undelegated_amount::id(), "Allow zero-lamport undelegated amount for initialized stakes #24670") + (stake_allow_zero_undelegated_amount::id(), "Allow zero-lamport undelegated amount for initialized stakes #24670"), + (disable_deploy_of_alloc_free_syscall::id(), "disable new deployments of deprecated sol_alloc_free_ syscall"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()