Summary
In compute_streamed():
let effective_elapsed = raw_elapsed.saturating_sub(paused_time);
stream.rate_per_second * effective_elapsed as i128
effective_elapsed is a u64 cast to i128. If rate_per_second is large and effective_elapsed is large, the multiplication can overflow i128::MAX. Unlike forge-vesting which uses division to bound the result, forge-stream does not cap the multiplication before it occurs. The test_high_rate_near_max test uses rate = i128::MAX / duration specifically to avoid overflow, but intermediate values between these bounds are not tested.
Tasks
Labels: bug, forge-stream, security
Summary
In
compute_streamed():effective_elapsedis au64cast toi128. Ifrate_per_secondis large andeffective_elapsedis large, the multiplication can overflowi128::MAX. Unlikeforge-vestingwhich uses division to bound the result,forge-streamdoes not cap the multiplication before it occurs. Thetest_high_rate_near_maxtest usesrate = i128::MAX / durationspecifically to avoid overflow, but intermediate values between these bounds are not tested.Tasks
checked_mulcall incompute_streamed():stream.rate_per_second.checked_mul(effective_elapsed as i128).unwrap_or(total)total(which israte * duration) to prevent any overflow reaching callersrate = i128::MAX / 2andduration = 3verifying no overflow occurscreate_stream()wheretotal = rate_per_second * duration_seconds as i128Labels:
bug,forge-stream,security