Skip to content

Commit a028910

Browse files
committed
CBMC: Add spec + proof for poly_add()
Restrict the spec to the case `poly_add(r,r,x)` for now, which is the only one used throughout the codebase. I am unsure about the idiomatic way to capture aliasing of the r and a parameters. It seems that `IS_FRESH(r) && r == a` is contradictory to CBMC, so for now the commit uses `SAME_OBJECT(r,a)` and `POINTER_OFFSET(r) == POINTER_OFFSET(a)`. Care needs to be taken in the spec and loop invariants to account for the fact that `a` is overwritten in the loop. To access the original values, the `LOOP_ENTRY` and `OLD` wrappers are needed. Signed-off-by: Hanno Becker <[email protected]> The docstring for poly_add has been moved to poly.h. This commit removes it from poly.h. Signed-off-by: Hanno Becker <[email protected]>
1 parent 060fef9 commit a028910

File tree

6 files changed

+129
-14
lines changed

6 files changed

+129
-14
lines changed

cbmc/proofs/poly_add/Makefile

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
include ../Makefile_params.common
4+
5+
HARNESS_ENTRY = harness
6+
HARNESS_FILE = poly_add_harness
7+
8+
# This should be a unique identifier for this proof, and will appear on the
9+
# Litani dashboard. It can be human-readable and contain spaces if you wish.
10+
PROOF_UID = poly_add
11+
12+
DEFINES +=
13+
INCLUDES +=
14+
15+
REMOVE_FUNCTION_BODY +=
16+
UNWINDSET +=
17+
18+
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
19+
PROJECT_SOURCES += $(SRCDIR)/mlkem/poly.c
20+
21+
CHECK_FUNCTION_CONTRACTS=$(MLKEM_NAMESPACE)poly_add
22+
USE_FUNCTION_CONTRACTS=
23+
APPLY_LOOP_CONTRACTS=on
24+
USE_DYNAMIC_FRAMES=1
25+
26+
# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
27+
EXTERNAL_SAT_SOLVER=
28+
CBMCFLAGS=--smt2
29+
30+
FUNCTION_NAME = poly_add
31+
32+
# If this proof is found to consume huge amounts of RAM, you can set the
33+
# EXPENSIVE variable. With new enough versions of the proof tools, this will
34+
# restrict the number of EXPENSIVE CBMC jobs running at once. See the
35+
# documentation in Makefile.common under the "Job Pools" heading for details.
36+
# EXPENSIVE = true
37+
38+
# This function is large enough to need...
39+
CBMC_OBJECT_BITS = 8
40+
41+
# If you require access to a file-local ("static") function or object to conduct
42+
# your proof, set the following (and do not include the original source file
43+
# ("mlkem/poly.c") in PROJECT_SOURCES).
44+
# REWRITTEN_SOURCES = $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i
45+
# include ../Makefile.common
46+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_SOURCE = $(SRCDIR)/mlkem/poly.c
47+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_FUNCTIONS = foo bar
48+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_OBJECTS = baz
49+
# Care is required with variables on the left-hand side: REWRITTEN_SOURCES must
50+
# be set before including Makefile.common, but any use of variables on the
51+
# left-hand side requires those variables to be defined. Hence, _SOURCE,
52+
# _FUNCTIONS, _OBJECTS is set after including Makefile.common.
53+
54+
include ../Makefile.common

cbmc/proofs/poly_add/cbmc-proof.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
# This file marks this directory as containing a CBMC proof.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: MIT-0 AND Apache-2.0
3+
4+
/*
5+
* Insert copyright notice
6+
*/
7+
8+
/**
9+
* @file poly_add_harness.c
10+
* @brief Implements the proof harness for basemul_cached function.
11+
*/
12+
#include "poly.h"
13+
14+
/*
15+
* Insert project header files that
16+
* - include the declaration of the function
17+
* - include the types needed to declare function arguments
18+
*/
19+
20+
/**
21+
* @brief Starting point for formal analysis
22+
*
23+
*/
24+
void harness(void) {
25+
poly r, b;
26+
poly_add(&r, &r, &b);
27+
}

mlkem/cbmc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define INVARIANT(...)
1515
#define ASSERT(...)
1616
#define ASSUME(...)
17+
#define SAME_OBJECT(...)
1718

1819
#else // CBMC _is_ defined, therefore we're doing proof
1920

@@ -34,6 +35,8 @@
3435
#define ASSERT(...) __CPROVER_assert(__VA_ARGS__)
3536
#define ASSUME(...) __CPROVER_assume(__VA_ARGS__)
3637

38+
#define SAME_OBJECT(...) __CPROVER_same_object(__VA_ARGS__)
39+
3740
///////////////////////////////////////////////////
3841
// Macros for "expression" forms that may appear
3942
// _inside_ top-level contracts.

mlkem/poly.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -555,20 +555,19 @@ void poly_reduce(poly *r) {
555555
}
556556
#endif /* MLKEM_USE_NATIVE_POLY_REDUCE */
557557

558-
/*************************************************
559-
* Name: poly_add
560-
*
561-
* Description: Add two polynomials; no modular reduction is performed
562-
*
563-
* Arguments: - poly *r: pointer to output polynomial
564-
* - const poly *a: pointer to first input polynomial
565-
* - const poly *b: pointer to second input polynomial
566-
**************************************************/
567558
void poly_add(poly *r, const poly *a, const poly *b) {
568-
unsigned int i;
569-
for (i = 0; i < MLKEM_N; i++) {
570-
r->coeffs[i] = a->coeffs[i] + b->coeffs[i];
571-
}
559+
int i;
560+
for (i = 0; i < MLKEM_N; i++)
561+
// clang-format off
562+
ASSIGNS(i, OBJECT_WHOLE(r))
563+
INVARIANT(i >= 0 && i <= MLKEM_N)
564+
INVARIANT(FORALL(int, k0, i, MLKEM_N - 1, r->coeffs[k0] == LOOP_ENTRY(*r).coeffs[k0]))
565+
INVARIANT(FORALL(int, k1, 0, i - 1, r->coeffs[k1] == LOOP_ENTRY(*a).coeffs[k1] + b->coeffs[k1]))
566+
DECREASES(MLKEM_N - i)
567+
// clang-format on
568+
{
569+
r->coeffs[i] = a->coeffs[i] + b->coeffs[i];
570+
}
572571
}
573572

574573
/*************************************************

mlkem/poly.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,36 @@ ASSIGNS(OBJECT_WHOLE(x));
304304
void poly_reduce(poly *r);
305305

306306
#define poly_add MLKEM_NAMESPACE(poly_add)
307-
void poly_add(poly *r, const poly *a, const poly *b);
307+
/************************************************************
308+
* Name: poly_add
309+
*
310+
* Description: Computes the coefficient-wise addition of two polynomials
311+
*
312+
* Arguments: - r: Pointer to output polynomial
313+
* - a: Pointer to first input polynomial. Must be the same as r.
314+
* - b: Pointer to second input polynomial. Must be disjoint from a
315+
* and r.
316+
*
317+
* The coefficients of a and b must be so that the addition does
318+
* not overflow. Otherwise, the behaviour of this function is undefined.
319+
*
320+
* NOTE: The restriction r == a is not needed, but we currently only
321+
* prove a CBMC spec under that assumption.
322+
*
323+
************************************************************/
324+
void poly_add(poly *r, const poly *a, const poly *b)
325+
// clang-format off
326+
// So far, poly_add is only used with r == a, so restrict proof to this case
327+
// TODO: Is this the right way to capture the aliasing constraint a==r?
328+
REQUIRES(SAME_OBJECT(r,a))
329+
REQUIRES(__CPROVER_POINTER_OFFSET(r)==__CPROVER_POINTER_OFFSET(a))
330+
REQUIRES(b != NULL && IS_FRESH(b, sizeof(poly)))
331+
REQUIRES(FORALL(int, k0, 0, MLKEM_N - 1, (int32_t) r->coeffs[k0] + b->coeffs[k0] <= INT16_MAX))
332+
REQUIRES(FORALL(int, k1, 0, MLKEM_N - 1, (int32_t) r->coeffs[k1] + b->coeffs[k1] >= INT16_MIN))
333+
ENSURES(FORALL(int, k, 0, MLKEM_N - 1, r->coeffs[k] == OLD(*a).coeffs[k] + b->coeffs[k]))
334+
ASSIGNS(OBJECT_WHOLE(r));
335+
// clang-format on
336+
308337
#define poly_sub MLKEM_NAMESPACE(poly_sub)
309338
void poly_sub(poly *r, const poly *a, const poly *b);
310339

0 commit comments

Comments
 (0)