diff --git a/cli/src/program.rs b/cli/src/program.rs index 0bcb30303e..7475f25bb9 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2071,7 +2071,7 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box::jit_compile(&mut executable).unwrap(); @@ -224,7 +224,7 @@ fn bench_create_vm(bencher: &mut Bencher) { &elf, None, Config::default(), - register_syscalls(invoke_context).unwrap(), + register_syscalls(invoke_context, true).unwrap(), ) .unwrap(); @@ -265,7 +265,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(); diff --git a/programs/bpf/c/src/alloc/alloc.c b/programs/bpf/c/src/alloc/alloc.c index f7b3b93bbb..db6157e193 100644 --- a/programs/bpf/c/src/alloc/alloc.c +++ b/programs/bpf/c/src/alloc/alloc.c @@ -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; } diff --git a/programs/bpf/rust/128bit/src/lib.rs b/programs/bpf/rust/128bit/src/lib.rs index 49a44d744e..dd81952301 100644 --- a/programs/bpf/rust/128bit/src/lib.rs +++ b/programs/bpf/rust/128bit/src/lib.rs @@ -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)] diff --git a/programs/bpf/rust/alloc/src/lib.rs b/programs/bpf/rust/alloc/src/lib.rs index 57d84bad7a..09919b12e4 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 888c491d98..42e32df6d6 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/inner_instruction_alignment_check/src/lib.rs b/programs/bpf/rust/inner_instruction_alignment_check/src/lib.rs index 109e52db9a..410d1e8ffc 100644 --- a/programs/bpf/rust/inner_instruction_alignment_check/src/lib.rs +++ b/programs/bpf/rust/inner_instruction_alignment_check/src/lib.rs @@ -2,6 +2,7 @@ use solana_program::{ account_info::AccountInfo, + custom_heap_default, entrypoint_deprecated::ProgramResult, instruction::{AccountMeta, Instruction}, msg, @@ -9,12 +10,6 @@ use solana_program::{ pubkey::Pubkey, }; -#[no_mangle] -fn custom_panic(info: &core::panic::PanicInfo<'_>) { - // Full panic reporting - msg!(&format!("{}", info)); -} - solana_program::entrypoint_deprecated!(process_instruction); #[allow(clippy::unnecessary_wraps)] fn process_instruction( @@ -39,3 +34,11 @@ fn process_instruction( Ok(()) } + +custom_heap_default!(); + +#[no_mangle] +fn custom_panic(info: &core::panic::PanicInfo<'_>) { + // Full panic reporting + msg!(&format!("{}", info)); +} diff --git a/programs/bpf/rust/iter/src/lib.rs b/programs/bpf/rust/iter/src/lib.rs index a262deb89f..3fda481a4b 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 0c27b1ba32..6d8819b1ac 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 12a5da6b14..6ad5c37e73 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 3c5006620f..590fbf13f9 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 2e9ccdbdc7..58fe2c7b96 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 a3b5e7e7d6..bc1579b22b 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 e48404bf82..9bffd74111 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 4793025f10..079266d057 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, true /* no sol_alloc_free */).unwrap(), ) .unwrap(); Executable::::jit_compile(&mut executable).unwrap(); @@ -565,6 +565,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); @@ -583,6 +587,91 @@ 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(); + bank.clear_executors(); + + // 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.activate_feature(&solana_sdk::feature_set::disable_deploy_of_alloc_free_syscall::id()); + bank.clear_signatures(); + + // invoke should still succeed because cached + assert!(bank.process_transaction(&invoke_tx).is_ok()); + + bank.clear_signatures(); + bank.clear_executors(); + + // 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(); @@ -1439,7 +1528,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), @@ -1452,7 +1541,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 7555fc6236..e3644172a0 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,7 +386,9 @@ fn process_instruction_common( program_data_offset, invoke_context, use_jit, - false, + false, /* reject_deployment_of_broken_elfs */ + // allow _sol_alloc_free syscall for execution + false, /* disable_sol_alloc_free_syscall */ )?; let transaction_context = &invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; @@ -627,6 +632,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); @@ -806,6 +814,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); @@ -1101,8 +1112,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 c2bf69c686..7d48435d2a 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -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> { 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, diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index ebbbd28899..08f5d3ccc9 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -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::::from_elf( &contents, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 0fb87987d4..438f64ef39 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -481,6 +481,10 @@ impl CachedExecutors { let _ = self.executors.remove(pubkey); } + fn clear(&mut self) { + *self = CachedExecutors::default(); + } + fn get_primer_count_upper_bound_inclusive(counts: &[(&Pubkey, u64)]) -> u64 { const PRIMER_COUNT_TARGET_PERCENTILE: u64 = 85; #[allow(clippy::assertions_on_constants)] @@ -4163,6 +4167,11 @@ impl Bank { Arc::make_mut(&mut cache).remove(pubkey); } + pub fn clear_executors(&self) { + let mut cache = self.cached_executors.write().unwrap(); + Arc::make_mut(&mut cache).clear(); + } + /// Execute a transaction using the provided loaded accounts and update /// the executors cache if the transaction was successful. #[allow(clippy::too_many_arguments)] diff --git a/sdk/bpf/c/inc/sol/string.h b/sdk/bpf/c/inc/sol/string.h index 8a329a67ca..159c9fd9e2 100644 --- a/sdk/bpf/c/inc/sol/string.h +++ b/sdk/bpf/c/inc/sol/string.h @@ -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