Skip to content

Fix mod_inv precondition: any x is invertible modulo 1 - #622

Open
tautschnig wants to merge 1 commit into
model-checking:mainfrom
tautschnig:fix-mod-inv-precondition
Open

Fix mod_inv precondition: any x is invertible modulo 1#622
tautschnig wants to merge 1 commit into
model-checking:mainfrom
tautschnig:fix-mod-inv-precondition

Conversation

@tautschnig

@tautschnig tautschnig commented Aug 3, 2026

Copy link
Copy Markdown
Member

The x % 2 != 0 precondition on ptr::mod_inv, added in 6be9ca4 (#457), is violated by mod_inv's only caller: align_offset::<T>(p, 1) with size_of::<T>() > 1 takes the GENERAL_CASE path (since 1 % stride != 0) and computes s2 = (stride & 0) >> 0 = 0, calling mod_inv(0, 1). This is reachable e.g. via <[u16]>::align_to::<u8>().

The violation is currently invisible in CI because run-kani.sh passes --no-assert-contracts; with dependency contracts asserted (the Kani default since model-checking/kani#3802), the harnesses slice::verify::align_to_from_u16::align_to_u8, slice::verify::align_to_mut_from_char::align_to_mut_u8, slice::verify::align_to_mut_from_u32::align_to_mut_u8, and ptr::verify::check_align_offset_u16 all fail on the asserted x % 2 != 0 clause.

The call is mathematically sound — modulo m == 1 every value is trivially an inverse (the unique residue is 0), and align_offset masks the returned value with a2 - 1 == 0 — so it is the precondition that is too strict, not the caller that is wrong: an inverse of x modulo a power of two m exists iff gcd(x, m) == 1, which for m > 1 means odd x but for m == 1 holds for all x. This PR weakens the precondition to m == 1 || x % 2 != 0 and fixes the (kani-disabled) postcondition for the same degenerate case (% m == 1 % m instead of % m == 1, since modulo 1 the result is 0).

Verified with Kani 152c6a8c + CBMC 6.10.0: the four harnesses above now pass with contracts asserted, and the eight ptr::verify::check_align_offset* proof harnesses pass both with and without --no-assert-contracts.

Found while investigating what still blocks removing --no-assert-contracts from run-kani.sh: this is one of two genuine latent contract violations that asserting dependency contracts surfaces (the other: #623).

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

The `x % 2 != 0` precondition added to `mod_inv` in commit
6be9ca4 ("Remove spurious comments
about the need for quantifiers (model-checking#457)") is violated by `mod_inv`'s only
caller: `align_offset::<T>(p, 1)` with `size_of::<T>() > 1` takes the
GENERAL_CASE path (since `1 % stride != 0`) and computes
`s2 = (stride & 0) >> 0 = 0`, calling `mod_inv(0, 1)`. This happens for
example via `<[u16]>::align_to::<u8>()`, so any harness reaching
`align_to` with a target alignment of 1 fails the asserted precondition
when Kani runs without --no-assert-contracts (the violation is
currently masked in CI, which passes that flag).

The call is mathematically sound: modulo `m == 1` every value is
trivially an inverse of every other (the unique residue is 0), and
`align_offset` masks the returned value with `a2 - 1 == 0`. It is the
precondition that is too strict, not the caller that is wrong: an
inverse of `x` modulo a power of two `m` exists iff
`gcd(x, m) == 1`, which for `m > 1` means odd `x`, but for `m == 1`
holds for all `x`. Weaken the precondition to `m == 1 || x % 2 != 0`
accordingly.

Also fix the (kani-disabled) postcondition for the same degenerate
case: modulo 1, `wrapping_mul(*result, x) % m` is 0, not 1, so compare
against `1 % m` instead of `1`.

Verified (Kani 152c6a8c + CBMC 6.10.0) that the four harnesses that
fail without --no-assert-contracts on this precondition
(slice::verify::align_to_from_u16::align_to_u8,
slice::verify::align_to_mut_from_char::align_to_mut_u8,
slice::verify::align_to_mut_from_u32::align_to_mut_u8,
ptr::verify::check_align_offset_u16) now pass with contracts asserted,
and that the eight ptr::verify::check_align_offset* proof harnesses
still pass in both configurations.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@tautschnig
tautschnig requested a review from a team as a code owner August 3, 2026 16:40
Copilot AI review requested due to automatic review settings August 3, 2026 16:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the verification contract on ptr::mod_inv inside core::ptr::align_offset to correctly handle the degenerate modulus case m == 1, which arises from a real call path (mod_inv(0, 1)) when aligning with a == 1. The change prevents latent contract violations from surfacing once dependency contract assertions are enabled in Kani.

Changes:

  • Weakens the mod_inv precondition from “x must be odd” to “m == 1 || x is odd”, matching modular arithmetic for m == 1.
  • Updates the (non-Kani) postcondition to compare against 1 % m, so the ensured property remains correct when m == 1.

@feliperodri feliperodri added the Maintenance Maintenance related issues for the challange label Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Maintenance Maintenance related issues for the challange

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants