Skip to content

Conversation

@willieyz
Copy link
Contributor

@willieyz willieyz commented Dec 15, 2025

  • Resolves: CBMC: Prove that x86_64 backend fulfills native api.h contracts #1335

  • Rebased on x86_64 tomont_avx2: Remove unused qdata argument #1432, tomont isn't actually use mlk_qdata.

  • This PR also added proof corresponding these two hol-light: HOL-Light: Add x86 frombytes and tomont correctness proof  #1418

  • This commit introduces CBMC verification for several x86-64 assembly functions. The contracts for these functions are derived from their corresponding HOL-Light correctness proofs.

  • The following functions now have CBMC proofs based on their
    HOL-Light specifications:

    • polyvec_basemul_acc_montgomery_cached_asm_k2/k3/k4:

      • Pre-conditions:
        <= &2 pow 12(HOL_LIGHT) v.s. MLKEM_UINT12_LIMIT + 1(CBMC)
      • Adds a CBMC pre-condition:
        requires(array_abs_bound(a, 0, 4 * MLKEM_N, MLKEM_UINT12_LIMIT +
        1))
    • ntt_avx2:

      • Pre-condition:
        <= &8191(HOL_LIGHT) v.s. 8191 + 1(CBMC)
      • Post-condition:
        <= &23594(HOL_LIGHT) v.s. 23594 + 1(CBMC)
      • Adds a CBMC pre-condition:
        requires(array_abs_bound(r, 0, MLKEM_N, 8192))
      • Adds a CBMC post-condition:
        ensures(array_abs_bound(r, 0, MLKEM_N, 23595))
    • intt_avx2:

      • Post-condition:
        <= &26631 v.s. 26631 + 1(CBMC)
      • Adds a CBMC post-condition:
        ensures(array_abs_bound(r, 0, MLKEM_N, 26632))
    • reduce_avx2:

      • Post-condition:
        < &3329(HOL_LIGHT) v.s. 3329 == MLKEM_Q(CBMC)
      • Adds a CBMC post-condition:
        ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
    • tobytes_avx2:

      • Pre-condition:
        (!i. i < LENGTH l ==> val(EL i l) < 3329)(HOL_LIGHT)
        v.s.
        MLKEM_UINT12_LIMIT(CBMC)
      • Adds a CBMC pre-condition:
        requires(array_bound(a, 0, MLKEM_N, 0, MLKEM_Q))
    • frombytes_avx2:

      • Post-conditions:
        read (memory :> bytes(a,dimindex(:32) * 12)) s0 = num_of_wordlist (l:(12 word)list)
      • Adds a CBMC post-condition:
        ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_UINT12_LIMIT))
    • mlk_rej_uniform_asm:

      • This proof are reference from aarch64 one, and change requires:
        requires(buflen % 24 == 0) to requires(buflen % 12 == 0)
    • mlk_tomont_avx2:

      • Post conditions:
        (ival z_i == (tomont_3329 (ival o x)) i) (mod &3329) /\ abs(ival z_i) <= &3328)
        v.s.
        ensures(array_abs_bound(r, 0, MLKEM_N, MLKEM_Q))
      • Add a CBMC post-condition:
        ensures(array_abs_bound(r, 0, MLKEM_N, 3328 + 1))
    • mlk_poly_mulcache_compute_avx2:

      • Post conditions:
         read(memory :> bytes16(word_add r (word(2 * i)))) s in
                      (ival zi == avx2_mulcache (ival o x) i) (mod &3329) /\
                      (abs(ival zi) <= &3328))
        
        v.s.
        ensures(array_abs_bound(cache, 0, MLKEM_N/2, MLKEM_Q))
        Add a CBMC post-condition:
        ensures(array_abs_bound(out, 0, MLKEM_N/2, 3329))
    • mlk_nttunpack_avx2:

      • Pre-condition:
        read(memory :> bytes(a, 512)) s = num_of_wordlist (unpermute_list l)))
        v.s.
        requires(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
      • Post-condition:
        read(memory :> bytes(a, 512)) s = num_of_wordlist (unpermute_list l)))
        v.s.
        ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
      • Add CBMC pre-condition and post-condition:
        requires(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
        ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))

@willieyz willieyz changed the title Cbmc x86 backend CBMC: x86-64 backend Dec 15, 2025
@willieyz willieyz marked this pull request as ready for review December 15, 2025 09:23
@willieyz willieyz requested a review from a team as a code owner December 15, 2025 09:23
@willieyz willieyz marked this pull request as draft December 15, 2025 09:28
@willieyz willieyz marked this pull request as ready for review December 15, 2025 09:28
Copy link
Contributor

@mkannwischer mkannwischer left a comment

Choose a reason for hiding this comment

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

Thanks @willieyz - but the contracts need work. Please also make sure that the comments pointing to the specification are actually the correct files.

@mkannwischer mkannwischer marked this pull request as draft December 15, 2025 10:23
mkannwischer added a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
mkannwischer added a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
mkannwischer added a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
mkannwischer added a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
hanno-becker pushed a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
hanno-becker pushed a commit that referenced this pull request Dec 16, 2025
During #1393, we noticed
that the HOL-Light proof for the x86 INTT proves the bound <=26632, while
our contract requires <= 26631.
This commit corrects the bound in the HOL-Light specification.

Signed-off-by: Matthias J. Kannwischer <[email protected]>
@willieyz willieyz force-pushed the cbmc-x86-backend branch 3 times, most recently from 87acd30 to 2d3d8be Compare December 17, 2025 08:05
Copy link
Contributor

@mkannwischer mkannwischer left a comment

Choose a reason for hiding this comment

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

Please also add the contract and proof for rej_uniform (#1409)

@willieyz willieyz marked this pull request as ready for review December 18, 2025 06:32
@willieyz willieyz marked this pull request as draft December 18, 2025 06:34
@willieyz willieyz force-pushed the cbmc-x86-backend branch 2 times, most recently from 7229fe3 to 98b01ad Compare December 18, 2025 07:49
@willieyz willieyz marked this pull request as ready for review December 18, 2025 07:55
@willieyz willieyz marked this pull request as draft December 22, 2025 09:32
@willieyz willieyz force-pushed the cbmc-x86-backend branch 4 times, most recently from 2b60254 to 438ad7f Compare December 23, 2025 09:12
@willieyz willieyz marked this pull request as ready for review December 23, 2025 10:23
@willieyz willieyz marked this pull request as ready for review December 24, 2025 11:49
@mkannwischer mkannwischer changed the title CBMC: x86-64 backend CBMC: Add and prove x86_64 backend contracts Dec 25, 2025
Copy link
Contributor

@mkannwischer mkannwischer left a comment

Choose a reason for hiding this comment

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

Thanks @willieyz. Almost there.

@willieyz willieyz force-pushed the cbmc-x86-backend branch 4 times, most recently from 6b47650 to 885467c Compare December 26, 2025 03:23
Copy link
Contributor

@mkannwischer mkannwischer left a comment

Choose a reason for hiding this comment

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

Thanks @willieyz for all that work! LGTM.

@hanno-becker, please take another look.

Comment on lines 22 to 23
/* This must be kept in sync with the HOL-Light specification
* in proofs/hol_light/x86/proofs/mlkem_rej_uniform.ml. */
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add symmetric notes to all the x86 .ML proof scripts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hello @hanno-becker, thank you for the review.
Following your feedback, I have added symmetric notes to all .ML HOL-Light proofs that have corresponding CBMC proofs in mlkem/src/native/x86_64/src/arith_native_x86_64.h.

Copy link
Contributor

@hanno-becker hanno-becker left a comment

Choose a reason for hiding this comment

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

This can't be completed because the HOL-Light spec for mulcache_compute is too weak. We need to fix this first.

@mkannwischer
Copy link
Contributor

This can't be completed because the HOL-Light spec for mulcache_compute is too weak. We need to fix this first.

@willieyz, can you remove the contract and proof for mulcache_compute first, please? Please also double check that the same mistake wasn't made in the other contracts.

@hanno-becker
Copy link
Contributor

mulcache_compute has now been fixed, so we can get the contract back in.

@willieyz willieyz force-pushed the cbmc-x86-backend branch 4 times, most recently from 49fdcab to 483e42f Compare January 2, 2026 06:16
@willieyz willieyz requested a review from hanno-becker January 2, 2026 08:44
@willieyz
Copy link
Contributor Author

willieyz commented Jan 2, 2026

mulcache_compute has now been fixed, so we can get the contract back in.

Hello, @hanno-becker , thank you for your help, I had rebase this PR on main, and add back the mulcache_compute's x86_64 backend contracts and proof

Comment on lines -65 to +162
void mlk_nttfrombytes_avx2(int16_t *r, const uint8_t *a);
void mlk_nttfrombytes_avx2(int16_t *r, const uint8_t *a)
Copy link
Contributor

@hanno-becker hanno-becker Jan 15, 2026

Choose a reason for hiding this comment

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

We need to think about if/how we can capture alignment constraints.

Contrary to AArch64, 32-byte parameter alignment is mandatory for most of the AVX2 backend, and indeed part of the HOL-Light specs. However, we don't express this in the C signature, nor in the CBMC contract (and we probably can't).

void mlk_nttfrombytes_avx2(int16_t *r, const uint8_t *a);
void mlk_nttfrombytes_avx2(int16_t *r, const uint8_t *a)
/* This must be kept in sync with the HOL-Light specification in
* proofs/hol_light/x86/proofs/mlkem_frombytes.ml.
Copy link
Contributor

Choose a reason for hiding this comment

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

Those paths need adjusting, it's x86_64 now

Comment on lines +67 to +69
requires(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
assigns(memory_slice(r, sizeof(int16_t) * MLKEM_N))
ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))
Copy link
Contributor

Choose a reason for hiding this comment

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

The HOL-Light proof does not have any bounds requires/ensures clauses.

Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't the HOL-LIght spec say the output is a permutation of the input?
That would imply that the bounds are maintained, wouldn't it?

- This commit introduces CBMC verification for several x86-64 optimized
  assembly functions. The contracts for these functions are derived from
  their corresponding HOL-Light correctness proofs.

- For each assembly function, a CBMC contract has been added to
  `arith_native_x86_64.h`. Additionally, new proof harnesses have been
   creat to formally verify that the generic C implementations (`*_native`)
   correctly utilize these assembly backends and adhere to the top-level
   API contracts.

- The following functions now have CBMC proofs based on their
  HOL-Light specifications:

  - polyvec_basemul_acc_montgomery_cached_asm_k2/k3/k4:
    - Pre-conditions:
      `<= &2 pow 12`(HOL_LIGHT) v.s. `MLKEM_UINT12_LIMIT + 1`(CBMC)
    - Adds a CBMC pre-condition:
      `requires(array_abs_bound(a, 0, 4 * MLKEM_N, MLKEM_UINT12_LIMIT +`
                                                                   `1))`
  - ntt_avx2:
    - Pre-condition:
      `<= &8191`(HOL_LIGHT) v.s. `8191 + 1`(CBMC)
    - Post-condition:
      `<= &23594`(HOL_LIGHT) v.s. `23594 + 1`(CBMC)
    - Adds a CBMC pre-condition:
      `requires(array_abs_bound(r, 0, MLKEM_N, 8192))`
    - Adds a CBMC post-condition:
      `ensures(array_abs_bound(r, 0, MLKEM_N, 23595))`

  - intt_avx2:
    - Post-condition:
      `<= &26631` v.s. `26631 + 1`(CBMC)
    - Adds a CBMC post-condition:
      `ensures(array_abs_bound(r, 0, MLKEM_N, 26632))`

   - reduce_avx2:
     - Post-condition:
       `< &3329`(HOL_LIGHT) v.s. `3329 == MLKEM_Q`(CBMC)
     - Adds a CBMC post-condition:
       `ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))`

   - tobytes_avx2:
     - Pre-condition:
       `(!i. i < LENGTH l ==> val(EL i l) < 3329)`(HOL_LIGHT)
       v.s.
       `MLKEM_UINT12_LIMIT`(CBMC)
     - Adds a CBMC pre-condition:
       `requires(array_bound(a, 0, MLKEM_N, 0, MLKEM_Q))`

   - frombytes_avx2:
     - Post-conditions:
       `read (memory :> bytes(a,dimindex(:32) * 12)) s0 =
                                      num_of_wordlist (l:(12 word)list)`
     - Adds a CBMC post-condition:
       `ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_UINT12_LIMIT))`

   - mlk_rej_uniform_asm:
     - This proof are reference from aarch64 one, and change requires:
       `requires(buflen % 24 == 0)` to `requires(buflen % 12 == 0)`

   - mlk_tomont_avx2:
     - Post conditions:
       `(ival z_i == (tomont_3329 (ival o x)) i) (mod &3329) /\
                        abs(ival z_i) <= &3328)`
       v.s.
       `ensures(array_abs_bound(r, 0, MLKEM_N, MLKEM_Q))`
     - Add a CBMC post-condition:
       `ensures(array_abs_bound(r, 0, MLKEM_N, 3328 + 1))`

   - mlk_poly_mulcache_compute_avx2:
     - Post conditions:
       ```
        (ival zi == avx2_mulcache (ival o x) i) (mod &3329)) /\
                      (abs(ival zi) <= &3328))
       ```
       v.s.
       `ensures(array_abs_bound(cache, 0, MLKEM_N/2, MLKEM_Q))`
     - Add a CBMC post-condition:
       `ensures(array_abs_bound(out, 0, MLKEM_N/2, 3329))`

   - mlk_nttunpack_avx2:
     - Pre-condition:
       `read(memory :> bytes(a, 512)) s =
                     num_of_wordlist (unpermute_list l)`
       v.s.
       `requires(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))`
     - Post-condition:
       `read(memory :> bytes(a, 512)) s =
                       num_of_wordlist (unpermute_list l)))`
       v.s.
       `ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))`
     - Add CBMC pre-condition and post-condition:
       `requires(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))`
       `ensures(array_bound(r, 0, MLKEM_N, 0, MLKEM_Q))`

Signed-off-by: willieyz <[email protected]>
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.

CBMC: Prove that x86_64 backend fulfills native api.h contracts

4 participants