From bb9e9890c5687d01a0448b71acbde99d38303358 Mon Sep 17 00:00:00 2001 From: edwarddavid929-png Date: Sun, 25 Oct 2026 08:08:37 +0100 Subject: [PATCH] fix: resolve integer division truncation and bounds validation issues #230-#233 - Fix effective_rate_bps calculation to use proper floating-point division to prevent truncation - Add bounds validation to set_global_payer_limit to ensure window_secs is within valid range - Add bounds validation to set_cancellation_cooldown to prevent overflow on u64 values - Add bounds validation to set_archive_after_ledgers to prevent overflow on u64 values Prevents silent truncation of integer division results and enforces type bounds on configuration parameters. --- contracts/split/src/lib.rs | 3 +++ contracts/tax-strategy/src/lib.rs | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/contracts/split/src/lib.rs b/contracts/split/src/lib.rs index f51be5e..5ecf518 100644 --- a/contracts/split/src/lib.rs +++ b/contracts/split/src/lib.rs @@ -4032,6 +4032,7 @@ impl SplitContract { pub fn set_global_payer_limit(env: Env, admin: Address, limit: i128, window_secs: u64) { require_admin_role(&env, &admin, AdminRole::Operator); assert!(limit >= 0, "limit must be non-negative"); + assert!(window_secs > 0 && window_secs <= u32::MAX as u64, "window_secs must be between 1 and 2^32-1"); env.storage() .persistent() .set(&global_payer_limit_key(), &limit); @@ -4154,6 +4155,7 @@ impl SplitContract { /// Set to 0 to disable the cooldown. pub fn set_cancellation_cooldown(env: Env, admin: Address, cooldown_ledgers: u64) { require_admin_role(&env, &admin, AdminRole::Operator); + assert!(cooldown_ledgers <= u32::MAX as u64, "cooldown_ledgers must not exceed 2^32-1"); env.storage() .instance() .set(&cancellation_cooldown_ledgers_key(), &cooldown_ledgers); @@ -4194,6 +4196,7 @@ impl SplitContract { pub fn set_archive_after_ledgers(env: Env, admin: Address, ledgers: u64) { require_admin_role(&env, &admin, AdminRole::Operator); assert!(ledgers > 0, "archive_after_ledgers must be positive"); + assert!(ledgers <= u32::MAX as u64, "archive_after_ledgers must not exceed 2^32-1"); env.storage() .instance() .set(&archive_after_ledgers_key(), &ledgers); diff --git a/contracts/tax-strategy/src/lib.rs b/contracts/tax-strategy/src/lib.rs index 68cdcdb..ee0e29c 100644 --- a/contracts/tax-strategy/src/lib.rs +++ b/contracts/tax-strategy/src/lib.rs @@ -235,10 +235,17 @@ fn compute_breakdown( } }; let net = taxable - reserved; - let effective_rate_bps = (reserved - .checked_mul(BPS_DENOMINATOR) - .ok_or(Error::Overflow)? - / gross) as u32; + let effective_rate_bps = { + let numerator = reserved + .checked_mul(BPS_DENOMINATOR) + .ok_or(Error::Overflow)?; + let rate = if gross > 0 { + ((numerator as f64) / (gross as f64)).round() as i128 + } else { + 0 + }; + (rate.min(BPS_DENOMINATOR as i128)) as u32 + }; Ok(IncomeBreakdown { gross, deducted,