Challenge 28: Verify flt2dec safety with Kani#606
Open
v3risec wants to merge 4 commits into
Open
Conversation
…e some lines to fix CI errors
…_opt verification version
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR solves Challenge 28 by adding Kani verification for functions in
core::num::flt2dec, including the formatter layer and the Dragon/Grisu digit-generation strategies.All verification-only code is gated behind
#[cfg(kani)].Verification Coverage Report (12/12 Verified)
digits_to_dec_strharness_digits_to_dec_strverifies symbolic digit buffers, exponent placement, fractional padding, and all returnedPartprefixes.digits_to_exp_strharness_digits_to_exp_strverifies symbolic exponents, significant-digit padding,e/Ecases, and returnedPartinitialization.to_shortest_strf16when available,f32, andf64; covers NaN, infinity, zero, finite inputs, signs, symbolic fractional padding, and initialized formatter output.to_shortest_exp_strto_exact_exp_strndigits > 0, non-finite and finite paths, and exact formatter output.to_exact_fixed_strgrisu::format_shortest_optassume_init_mutcall sites, and theround_and_weedcontract.grisu::format_shortestgrisu::format_exact_optpossibly_round, initialized prefixes, and carry append bounds.grisu::format_exactdragon::format_shortestMAX_SIG_DIGITSreturned-slice bound,round_up, and initialized output.dragon::format_exactVerification Approach
For the formatter layer in
mod.rs, the digit-generation callback is modeled by small symbolic functions that return initialized digit prefixes. This is valid because theto_*functions are generic over the digit-generation strategy: their safety depends on the callback contract that it returns an initialized slice from the provided scratch buffer. Dragon and Grisu are verified separately against that same contract.For Grisu, the expensive scaling and arithmetic portions are abstracted where they are not needed for memory safety. The stubs preserve the bounds used by the real algorithm, such as cached-power exponent ranges, power-of-ten bounds, digit values in
0..10, and maximum digit counts. The real buffer writes, loop structure, prefix initialization checks, and unsafe conversions remain visible to Kani.For Dragon, Big integer operations are stubbed because proving
Big32x40arithmetic is orthogonal to this challenge and causes the normal proof to time out. The stubs over-approximate control flow through nondeterministic comparisons, zero checks, and digit extraction, while preserving the safety-relevant fact that extracted digits are decimal digits. This lets Kani check the actual buffer writes and returned slices without requiring a full numerical proof.The Grisu shortest rounding helper is given a Kani contract and verified with
#[kani::proof_for_contract];format_shortest_optthen uses#[kani::stub_verified]. The Kani-only version returns a boolean acceptance result rather than a borrowed slice because Kani cannot conveniently synthesize arbitrary verified-stub results containing borrowed slice lifetimes. The caller still owns and returns the already-initialized digit slice.Verification Tradeoffs
Running the full algorithms without abstraction inlines cached-power selection, DiyFp multiplication, Big integer arithmetic, and nested digit loops. That path does not finish in a practical CI budget.
The proof cuts are limited to arithmetic facts that are not safety-critical:
MAX_SIG_DIGITS;These assumptions do not initialize buffers or skip unsafe call sites. The initialized-prefix assertions read the prefix before each
assume_init_*; if a path exposes an unwritten byte, Kani fails.Scope Assumptions
#[cfg(kani)]and do not affect normal library behavior.Notes
Verification
All added Challenge 28 harnesses pass locally with Kani.
Resolves #524
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.