Skip to content

Amount: bounds-check fromToman/add for integer overflow #5

@navidkashani

Description

@navidkashani

Context

Surfaced during the pre-merge audit of #4. Low-risk edge case, deferred to 0.6.1.

Problem

In Amount::fromToman() and Amount::add(), the arithmetic $t * 10 (and $a->rials + $b->rials) can silently promote to float once the result exceeds PHP_INT_MAX. On 64-bit PHP that's ~9.22e18 rials, or ~9.22e17 toman (~92 quadrillion toman, ~$2T USD at current rates) — absurd in IRR terms, but the Amount VO's design philosophy is refuse ambiguous values early, not work most of the time.

The float slips past the int $rials constructor because PHP silently casts it back, producing a value with lost precision and no error raised.

Repro

// 64-bit PHP
$huge = intdiv(PHP_INT_MAX, 10) + 1; // first toman value that overflows *10
Amount::fromToman($huge); // silently wrong — no FormatException

Proposed fix

  1. Guard fromToman(): if $t > intdiv(PHP_INT_MAX, 10) → throw FormatException with a new ErrorCode::AMOUNT_OVERFLOW (or reuse AMOUNT_NEGATIVE's sibling — preference: new code, it's a distinct invariant).
  2. Guard add(): if $a->rials > PHP_INT_MAX - $b->rials → same throw.
  3. Regression tests covering both boundaries (at-limit = ok, one-past-limit = throws).

Why 0.6.1, not a 0.6 blocker

Practical IRR amounts won't hit this in a hundred lifetimes. But the fix is cheap, the invariant is the VO's whole point, and untested overflow paths rot. Ship it as a patch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions