Skip to content

ir: NewBigVal accepts binary-exponent and leading-zero literals, storing non-JSON canonical forms #45

Description

@OmarAlJarrah

Summary

BigVal's documented contract is "always a JSON-valid numeric literal" in decimal form. Two classes of input break it:

  1. Binary exponents. The comment above the big.ParseFloat(s, 10, 0, …) call claims it "accepts decimal and e/E exponent forms only". It does not: math/big also accepts binary exponents p/P in any base. So NewBigVal accepts "1p4", "2.5p-2", "5.p3" and stores them verbatim — these are not decimal literals at all (1p4 = 1×2⁴ = 16; 2.5p-2 = 0.625). canonicalDecimal splits the mantissa only on e/E, so affix normalization also misfires ("5.p3" keeps its trailing dot). A decimal consumer that stops at p reads 2.5p-2 as 2.5 — a 4× silent value corruption; a consumer splicing the string into JSON or generated code produces invalid output.
  2. Leading zeros. JSON forbids them, but NewBigVal accepts "05", "007", "-012", "00.5", "05e2", storing canonical forms for which json.Valid is false. canonicalDecimal handles exactly three affixes (leading +, leading dot, trailing dot) and misses this fourth JSON-invalid class.

Reproduction

NewBigVal("1p4")    → accepted, stored "1p4"    (actual value 16), json.Valid=false
NewBigVal("2.5p-2") → accepted, stored "2.5p-2" (actual value 0.625)
NewBigVal("5.p3")   → accepted, stored "5.p3"   (trailing dot not normalized)
NewBigVal("05")     → accepted, stored "05",    json.Valid=false
NewBigVal("+05")    → accepted, stored "05",    json.Valid=false

(Verified twice, independently.) Other garbage is correctly rejected: " 1", "1_000", "0x10", "--5", "NaN", "Inf", huge exponents.

Root cause

ir/bigval.go:33 (the parse call accepts more than the comment claims) and :49-68 (canonicalDecimal never strips leading zeros, never rejects p forms).

Expected

NewBigVal rejects any input that isn't a decimal/e-exponent literal (a pre-parse syntax check, or reject on the presence of p/P), and canonicalization strips leading zeros so the stored form always satisfies json.Valid. A property test asserting json.Valid([]byte(v.String())) over accepted inputs would pin the whole contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions