Skip to content

Verify core::num::flt2dec memory safety (challenge #28)#601

Open
MavenRain wants to merge 2 commits into
model-checking:mainfrom
MavenRain:verify-flt2dec-challenge-28
Open

Verify core::num::flt2dec memory safety (challenge #28)#601
MavenRain wants to merge 2 commits into
model-checking:mainfrom
MavenRain:verify-flt2dec-challenge-28

Conversation

@MavenRain

@MavenRain MavenRain commented Jun 15, 2026

Copy link
Copy Markdown

Towards #524 (challenge #28: core::num::flt2dec).

What this proves

Kani harnesses for all 12 target functions, proving each unsafe block touches
only initialized, in-bounds memory:

  • flt2dec/mod.rs: digits_to_dec_str, digits_to_exp_str,
    to_shortest_str,
    to_shortest_exp_str, to_exact_exp_str, to_exact_fixed_str
  • strategy/grisu.rs: format_exact_opt, format_shortest_opt,
    format_exact,
    format_shortest
  • strategy/dragon.rs: format_exact, format_shortest

Approach

  • The buffer-safety obligation (assume_init over written cells, buf[i]
    in-bounds) is independent of the bignum/Fp values, so the arithmetic is
    abstracted by sound stubs; cmp/is_zero are nondeterministic so every
    control-flow path is explored. The format_exact/format_shortest wrappers
    are proven by stubbing their callees, which are themselves verified.
  • The shortest-mode loops have no explicit index guard; they terminate within
    MAX_SIG_DIGITS by the Grisu/Loitsch digit-count theorem (Loitsch, PLDI'10;
    Errol, POPL'16, Thm 5). CBMC cannot derive this number-theoretic bound, so it
    is cited as a cfg(kani) assume(i < MAX_SIG_DIGITS). The harnesses
    constrain
    inputs to the exact decode() image (minus == 1, plus ∈ {1,2},
    mant ≤ 2^54), which is the functions' true precondition (they are internal,
    only reached via decode() on a real f64) and is what makes the assume
    sound.

Caveats for review (transparency)

  • The two shortest-mode proofs are conditional on the cited digit-count
    theorem
    ; the rest is unconditional. The assume bounds the digit count
    (an input-precision property); buffer safety follows via the separate
    assert!(buf.len() >= MAX_SIG_DIGITS). Happy to strengthen this (e.g. tie the
    bound to the concrete max_kappa) or to discuss proving the theorem if
    preferred.
  • grisu::format_shortest_opt additionally assumes Fp::mul's documented output
    invariants (ordering minus ≤ v ≤ plus, normalized-mantissa bounds) that the
    cost-reducing stub would otherwise havoc.
  • Harnesses require -C debug-assertions=off (the debug_assert!s are
    digit-correctness, not memory safety). Please advise on the preferred CI
    wiring.

  Add Kani proof harnesses establishing the memory safety of all 12
  safe-functions-with-unsafe-bodies in core::num::flt2dec: the 6 formatting
  entry points (flt2dec/mod.rs) and the 6 Grisu/Dragon strategy functions
  (flt2dec/strategy/{grisu,dragon}.rs).

  Each unsafe block (MaybeUninit::assume_init_* and slice indexing) is proven
  to touch only initialized, in-bounds memory. The bignum/Fp arithmetic is
  abstracted via sound stubbing -- buffer safety is independent of the numeric
  values, and value inspection (cmp/is_zero) is made nondeterministic so all
  control-flow paths are explored.

  The shortest-mode functions (grisu::format_shortest_opt,
  dragon::format_shortest)
  have an implicit loop bound; their digit index is bounded by the Grisu/Loitsch
  digit-count theorem (a 53-bit-precision f64 has <= MAX_SIG_DIGITS = 17
  significant decimal digits), cited as a cfg(kani) assume because CBMC cannot
  derive it from the unwound arithmetic. The harnesses use the tight decode()
  precondition (the functions are internal and only ever receive a decode()
  result for a real f64), which is what makes that assume sound.

  All added annotations are cfg(kani) verification-only and compile out of normal
  builds. Harnesses require -C debug-assertions=off.

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain MavenRain force-pushed the verify-flt2dec-challenge-28 branch from a7ae3ea to e4e3297 Compare June 16, 2026 00:32
…ition-2 OOM)

  - Remove concrete dragon::check_format_exact: full unstubbed bignum (unwind 50)
    exhausted CBMC memory in the verify-std partition (ran ~6h, cancelled).
    format_exact buffer safety is already proven by check_format_exact_stub.
  - Run autoharness with debug-assertions off: the flt2dec harnesses stub the
    bignum/Fp arithmetic, making std debug_assert! digit-correctness checks
    (d<10, mant<scale) unprovable. Those are not memory-safety properties and are
    already dead in the verify-std job (--prove-safety-only). Keeps the two jobs
    consistent; monotonic (only removes checks).

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain MavenRain force-pushed the verify-flt2dec-challenge-28 branch from 98db9cd to e104d40 Compare June 17, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant