Skip to content

forge-stream compute_streamed() multiplies rate_per_second by effective_elapsed as i128 — silent truncation when effective_elapsed exceeds i128 max divided by rate #303

Description

@Austinaminu2

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

  • Add a checked_mul call in compute_streamed(): stream.rate_per_second.checked_mul(effective_elapsed as i128).unwrap_or(total)
  • Cap the result at total (which is rate * duration) to prevent any overflow reaching callers
  • Add a test with rate = i128::MAX / 2 and duration = 3 verifying no overflow occurs
  • Add the same check to create_stream() where total = rate_per_second * duration_seconds as i128
  • Document the overflow protection in the contract README

Labels: bug, forge-stream, security

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions