diff --git a/soroban-env-host/src/budget/limits.rs b/soroban-env-host/src/budget/limits.rs index bc418d65c..836592d7a 100644 --- a/soroban-env-host/src/budget/limits.rs +++ b/soroban-env-host/src/budget/limits.rs @@ -96,7 +96,9 @@ impl DepthLimiter for BudgetImpl { // `leave` should be called in tandem with `enter` such that the depth // doesn't exceed the initial depth limit. fn leave(&mut self) -> Result<(), HostError> { - self.depth_limit = self.depth_limit.saturating_add(1); + self.depth_limit = self.depth_limit.checked_add(1).ok_or_else(|| { + Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError) + })?; Ok(()) } } diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 17d4f8591..59eef095f 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -954,10 +954,13 @@ impl VmCallerEnv for Host { let msg = String::from_utf8_lossy(&msg); let VmSlice { vm, pos, len } = self.decode_vmslice(vals_pos, vals_len)?; - Vec::::charge_bulk_init_cpy(len.saturating_add(1) as u64, self)?; + Vec::::charge_bulk_init_cpy((len as u64).saturating_add(1), self)?; let mut vals: Vec = vec![Val::VOID.to_val(); len as usize]; // charge for conversion from bytes to `Val`s - self.charge_budget(ContractCostType::MemCpy, Some(len.saturating_mul(8) as u64))?; + self.charge_budget( + ContractCostType::MemCpy, + Some((len as u64).saturating_mul(8)), + )?; self.metered_vm_read_vals_from_linear_memory::<8, Val>( vmcaller, &vm, @@ -1510,7 +1513,10 @@ impl VmCallerEnv for Host { Vec::::charge_bulk_init_cpy(len as u64, self)?; let mut vals: Vec = vec![Val::VOID.into(); len as usize]; // charge for conversion from bytes to `Val`s - self.charge_budget(ContractCostType::MemCpy, Some(len.saturating_mul(8) as u64))?; + self.charge_budget( + ContractCostType::MemCpy, + Some((len as u64).saturating_mul(8)), + )?; self.metered_vm_read_vals_from_linear_memory::<8, Val>( vmcaller, &vm, @@ -1581,7 +1587,7 @@ impl VmCallerEnv for Host { // Step 2: write all vals. // charges memcpy of converting map entries into bytes - self.charge_budget(ContractCostType::MemCpy, Some(len.saturating_mul(8) as u64))?; + self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?; self.metered_vm_write_vals_to_linear_memory( vmcaller, &vm, @@ -1806,7 +1812,10 @@ impl VmCallerEnv for Host { Vec::::charge_bulk_init_cpy(len as u64, self)?; let mut vals: Vec = vec![Val::VOID.to_val(); len as usize]; // charge for conversion from bytes to `Val`s - self.charge_budget(ContractCostType::MemCpy, Some(len.saturating_mul(8) as u64))?; + self.charge_budget( + ContractCostType::MemCpy, + Some((len as u64).saturating_mul(8)), + )?; self.metered_vm_read_vals_from_linear_memory::<8, Val>( vmcaller, &vm, @@ -1838,7 +1847,7 @@ impl VmCallerEnv for Host { )); } // charges memcpy of converting vec entries into bytes - self.charge_budget(ContractCostType::MemCpy, Some(len.saturating_mul(8) as u64))?; + self.charge_budget(ContractCostType::MemCpy, Some((len as u64).saturating_mul(8)))?; self.metered_vm_write_vals_to_linear_memory( vmcaller, &vm, @@ -2416,8 +2425,10 @@ impl VmCallerEnv for Host { let vnew = self.visit_obj(b, |hv: &ScBytes| { self.validate_index_lt_bound(i, hv.len())?; let mut vnew: Vec = hv.metered_clone(self)?.into(); - // len > i has been verified above but use saturating_sub just in case - let n_elts = (hv.len() as u64).saturating_sub(i as u64); + // len > i has been verified above but use checked_sub just in case + let n_elts = (hv.len() as u64).checked_sub(i as u64).ok_or_else(|| { + Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError) + })?; // remove elements incurs the cost of moving bytes, it does not incur // allocation/deallocation metered_clone::charge_shallow_copy::(n_elts, self)?; diff --git a/soroban-env-host/src/host/ledger_info_helper.rs b/soroban-env-host/src/host/ledger_info_helper.rs index fbcc5e7e9..c7a0fe628 100644 --- a/soroban-env-host/src/host/ledger_info_helper.rs +++ b/soroban-env-host/src/host/ledger_info_helper.rs @@ -1,6 +1,7 @@ -use soroban_env_common::xdr::{ContractDataDurability, LedgerKey}; - -use crate::{Host, HostError, LedgerInfo}; +use crate::{ + xdr::{ContractDataDurability, LedgerKey, ScErrorCode, ScErrorType}, + Error, Host, HostError, LedgerInfo, +}; impl Host { pub(crate) fn get_min_live_until_ledger( @@ -16,16 +17,36 @@ impl Host { self.with_ledger_info(|li: &LedgerInfo| Ok(li.min_persistent_entry_ttl))? } }; - Ok(ledger_seq.saturating_add(min_live_until.saturating_sub(1))) + ledger_seq + .checked_add(min_live_until.saturating_sub(1)) + .ok_or_else(|| { + // overflowing here means a misconfiguration of the network (the + // ttl is too large), in which case we immediately flag it as an + // unrecoverable `InternalError`, even though the source is + // external to the host. + HostError::from(Error::from_type_and_code( + ScErrorType::Context, + ScErrorCode::InternalError, + )) + }) } pub(crate) fn max_live_until_ledger(&self) -> Result { self.with_ledger_info(|li| { - Ok(li - .sequence_number + li.sequence_number // Entry can live for at most max_entry_live_until ledgers from // now, counting the current one. - .saturating_add(li.max_entry_ttl.saturating_sub(1))) + .checked_add(li.max_entry_ttl.saturating_sub(1)) + .ok_or_else(|| { + // overflowing here means a misconfiguration of the network + // (the ttl is too large), in which case we immediately flag + // it as an unrecoverable `InternalError`, even though the + // source is external to the host. + HostError::from(Error::from_type_and_code( + ScErrorType::Context, + ScErrorCode::InternalError, + )) + }) }) } } diff --git a/soroban-env-host/src/storage.rs b/soroban-env-host/src/storage.rs index 243928b37..2b6ef11c9 100644 --- a/soroban-env-host/src/storage.rs +++ b/soroban-env-host/src/storage.rs @@ -9,14 +9,12 @@ use std::rc::Rc; -use soroban_env_common::xdr::{ContractDataDurability, ScErrorCode, ScErrorType}; -use soroban_env_common::{Env, Val}; - -use crate::budget::Budget; -use crate::host::ledger_info_helper::get_key_durability; -use crate::xdr::{LedgerEntry, LedgerKey}; -use crate::Host; -use crate::{host::metered_map::MeteredOrdMap, HostError}; +use crate::{ + budget::Budget, + host::{ledger_info_helper::get_key_durability, metered_map::MeteredOrdMap}, + xdr::{ContractDataDurability, LedgerEntry, LedgerKey, ScErrorCode, ScErrorType}, + Env, Error, Host, HostError, Val, +}; pub type FootprintMap = MeteredOrdMap, AccessType, Budget>; pub type EntryWithLiveUntil = (Rc, Option); @@ -422,8 +420,18 @@ impl Storage { )); } - let mut new_live_until = - host.with_ledger_info(|li| Ok(li.sequence_number.saturating_add(extend_to)))?; + let mut new_live_until = host.with_ledger_info(|li| { + li.sequence_number.checked_add(extend_to).ok_or_else(|| { + // overflowing here means a misconfiguration of the network (the + // ttl is too large), in which case we immediately flag it as an + // unrecoverable `InternalError`, even though the source is + // external to the host. + HostError::from(Error::from_type_and_code( + ScErrorType::Context, + ScErrorCode::InternalError, + )) + }) + })?; if new_live_until > host.max_live_until_ledger()? { if let Some(durability) = get_key_durability(&key) {