Skip to content

Commit 91f1927

Browse files
authored
Merge pull request #1191 from pq-code-package/cbmc-explicit-bounds
Introduce explicit upper bounds on lengths of buffers
2 parents 3ba7822 + 3be9017 commit 91f1927

File tree

8 files changed

+66
-6
lines changed

8 files changed

+66
-6
lines changed

mlkem/src/cbmc.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
/***************************************************
99
* Basic replacements for __CPROVER_XXX contracts
1010
***************************************************/
11-
1211
#ifndef CBMC
1312

1413
#define __contract__(x)
1514
#define __loop__(x)
1615

1716
#else /* !CBMC */
1817

18+
#include <stdint.h>
19+
1920
#define __contract__(x) x
2021
#define __loop__(x) x
2122

@@ -59,6 +60,17 @@
5960
#define readable(...) __CPROVER_r_ok(__VA_ARGS__)
6061
#define writeable(...) __CPROVER_w_ok(__VA_ARGS__)
6162

63+
/* Maximum supported buffer size
64+
*
65+
* Larger buffers may be supported, but due to internal modeling constraints
66+
* in CBMC, the proofs of memory- and type-safety won't be able to run.
67+
*
68+
* If you find yourself in need for a buffer size larger than this,
69+
* please contact the maintainers, so we can prioritize work to relax
70+
* this somewhat artificial bound.
71+
*/
72+
#define MLK_MAX_BUFFER_SIZE (SIZE_MAX >> 12)
73+
6274
/*
6375
* History variables
6476
* https://diffblue.github.io/cbmc/contracts-history-variables.html

mlkem/src/fips202/fips202.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
static void mlk_keccak_absorb_once(uint64_t *s, uint32_t r, const uint8_t *m,
6161
size_t mlen, uint8_t p)
6262
__contract__(
63+
requires(mlen <= MLK_MAX_BUFFER_SIZE)
6364
requires(r <= sizeof(uint64_t) * MLK_KECCAK_LANES)
6465
requires(memory_no_alias(s, sizeof(uint64_t) * MLK_KECCAK_LANES))
6566
requires(memory_no_alias(m, mlen))
@@ -153,6 +154,7 @@ __contract__(
153154
static void mlk_keccak_squeeze_once(uint8_t *h, size_t outlen, uint64_t *s,
154155
uint32_t r)
155156
__contract__(
157+
requires(outlen <= MLK_MAX_BUFFER_SIZE)
156158
requires(r <= sizeof(uint64_t) * MLK_KECCAK_LANES)
157159
requires(memory_no_alias(s, sizeof(uint64_t) * MLK_KECCAK_LANES))
158160
requires(memory_no_alias(h, outlen))

mlkem/src/fips202/fips202.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef struct
4747
void mlk_shake128_absorb_once(mlk_shake128ctx *state, const uint8_t *input,
4848
size_t inlen)
4949
__contract__(
50+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
5051
requires(memory_no_alias(state, sizeof(mlk_shake128ctx)))
5152
requires(memory_no_alias(input, inlen))
5253
assigns(memory_slice(state, sizeof(mlk_shake128ctx)))
@@ -96,6 +97,8 @@ void mlk_shake128_release(mlk_shake128ctx *state);
9697
void mlk_shake256(uint8_t *output, size_t outlen, const uint8_t *input,
9798
size_t inlen)
9899
__contract__(
100+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
101+
requires(outlen <= MLK_MAX_BUFFER_SIZE)
99102
requires(memory_no_alias(input, inlen))
100103
requires(memory_no_alias(output, outlen))
101104
assigns(memory_slice(output, outlen))
@@ -116,6 +119,7 @@ __contract__(
116119
**************************************************/
117120
void mlk_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
118121
__contract__(
122+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
119123
requires(memory_no_alias(input, inlen))
120124
requires(memory_no_alias(output, SHA3_256_HASHBYTES))
121125
assigns(memory_slice(output, SHA3_256_HASHBYTES))
@@ -136,6 +140,7 @@ __contract__(
136140
**************************************************/
137141
void mlk_sha3_512(uint8_t *output, const uint8_t *input, size_t inlen)
138142
__contract__(
143+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
139144
requires(memory_no_alias(input, inlen))
140145
requires(memory_no_alias(output, SHA3_512_HASHBYTES))
141146
assigns(memory_slice(output, SHA3_512_HASHBYTES))

mlkem/src/fips202/fips202x4.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static void mlk_keccak_absorb_once_x4(uint64_t *s, uint32_t r,
2828
const uint8_t *in2, const uint8_t *in3,
2929
size_t inlen, uint8_t p)
3030
__contract__(
31+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
3132
requires(memory_no_alias(s, sizeof(uint64_t) * MLK_KECCAK_LANES * MLK_KECCAK_WAY))
3233
requires(r <= sizeof(uint64_t) * MLK_KECCAK_LANES)
3334
requires(memory_no_alias(in0, inlen))
@@ -78,7 +79,8 @@ static void mlk_keccak_squeezeblocks_x4(uint8_t *out0, uint8_t *out1,
7879
size_t nblocks, uint64_t *s, uint32_t r)
7980
__contract__(
8081
requires(r <= sizeof(uint64_t) * MLK_KECCAK_LANES)
81-
requires(nblocks <= 8 /* somewhat arbitrary bound */)
82+
requires(r == SHAKE128_RATE || r == SHAKE256_RATE)
83+
requires(nblocks <= (MLK_MAX_BUFFER_SIZE / SHAKE256_RATE))
8284
requires(memory_no_alias(s, sizeof(uint64_t) * MLK_KECCAK_LANES * MLK_KECCAK_WAY))
8385
requires(memory_no_alias(out0, nblocks * r))
8486
requires(memory_no_alias(out1, nblocks * r))

mlkem/src/fips202/fips202x4.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ void mlk_shake128x4_absorb_once(mlk_shake128x4ctx *state, const uint8_t *in0,
2525
const uint8_t *in1, const uint8_t *in2,
2626
const uint8_t *in3, size_t inlen)
2727
__contract__(
28+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
2829
requires(memory_no_alias(state, sizeof(mlk_shake128x4ctx)))
2930
requires(memory_no_alias(in0, inlen))
3031
requires(memory_no_alias(in1, inlen))
@@ -62,7 +63,8 @@ void mlk_shake256x4(uint8_t *out0, uint8_t *out1, uint8_t *out2, uint8_t *out3,
6263
size_t outlen, uint8_t *in0, uint8_t *in1, uint8_t *in2,
6364
uint8_t *in3, size_t inlen)
6465
__contract__(
65-
requires(outlen <= 8 * SHAKE256_RATE /* somewhat arbitrary bound */)
66+
requires(inlen <= MLK_MAX_BUFFER_SIZE)
67+
requires(outlen <= MLK_MAX_BUFFER_SIZE)
6668
requires(memory_no_alias(in0, inlen))
6769
requires(memory_no_alias(in1, inlen))
6870
requires(memory_no_alias(in2, inlen))

mlkem/src/verify.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ __contract__(ensures(return_value == (cond ? a : b)))
318318
*
319319
* Arguments: const uint8_t *a: pointer to first byte array
320320
* const uint8_t *b: pointer to second byte array
321-
* size_t len: length of the byte arrays
321+
* size_t len: length of the byte arrays, upper-bounded
322+
* to INT_MAX to control proof complexity
322323
*
323324
* Returns 0 if the byte arrays are equal, a non-zero value otherwise
324325
*
@@ -338,9 +339,9 @@ __contract__(ensures(return_value == (cond ? a : b)))
338339
static MLK_INLINE uint8_t mlk_ct_memcmp(const uint8_t *a, const uint8_t *b,
339340
const size_t len)
340341
__contract__(
342+
requires(len <= INT_MAX)
341343
requires(memory_no_alias(a, len))
342344
requires(memory_no_alias(b, len))
343-
requires(len <= INT_MAX)
344345
ensures((return_value == 0) == forall(i, 0, len, (a[i] == b[i]))))
345346
{
346347
uint8_t r = 0, s = 0;
@@ -391,6 +392,7 @@ __contract__(
391392
static MLK_INLINE void mlk_ct_cmov_zero(uint8_t *r, const uint8_t *x,
392393
size_t len, uint8_t b)
393394
__contract__(
395+
requires(len <= MLK_MAX_BUFFER_SIZE)
394396
requires(memory_no_alias(r, len))
395397
requires(memory_no_alias(x, len))
396398
assigns(memory_slice(r, len)))

proofs/cbmc/Makefile.common

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ endif
246246
# * an entire project when added to Makefile-project-defines
247247
# * a specific proof when added to the harness Makefile
248248

249-
CBMC_FLAG_MALLOC_MAY_FAIL ?= # set to --no-malloc-may-fail to disable
249+
CBMC_FLAG_MALLOC_MAY_FAIL ?= --malloc-fail-assert
250250
CBMC_FLAG_BOUNDS_CHECK ?= # set to --no-bounds-check to disable
251251
CBMC_FLAG_CONVERSION_CHECK ?= --conversion-check
252252
CBMC_FLAG_DIV_BY_ZERO_CHECK ?= # set to --no-div-by-zero-check to disable

proofs/cbmc/proof_guide.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,41 @@ for some struct `foo`, you cannot pass `&foo[0]`, `&foo[1]` as arguments to a fu
7373
`memory_no_alias(...)` for both, because `&foo[0]`, `&foo[1]` point to the same object. In mlkem-native, we sometimes
7474
work around this by manually splitting statically-sized arrays into multiple separate objects.
7575

76+
77+
### Maximum buffer sizes
78+
79+
CBMC assumes that allocated objects are less than `__CPROVER_max_malloc_size`
80+
which is an an internal constant defined to be `SIZE_MAX >> (OBJECT_BITS + 1)`
81+
for that particular run of CBMC, where `SIZE_MAX` is an implementation-defined
82+
constant (declared in `stdint.h`) and `OBJECT_BITS` is a command-line parameter
83+
with value typically in the range 8 .. 12
84+
85+
See the [memory bounds checking](https://diffblue.github.io/cbmc/memory-bounds-checking.html)
86+
section of the CBMC manual for more details.
87+
88+
Pragmatically, `SIZE_MAX` will either be `2**64-1` or `2**32-1` depending on the
89+
host platform, and we choose the largest value of `OBJECT_BITS` that is used
90+
for all proofs in this repository.
91+
92+
This matters where a function takes a formal parameter `p` of some pointer type
93+
`t` and a `len` parameter of type `size_t` that denotes the number of elements
94+
pointed to by `p`, and those parameters are subject to a
95+
`memory_no_alias(p, len * sizeof(t))` contract.
96+
97+
In such cases, len must be explicitly bounded to be less that or equal to
98+
MLK_MAX_BUFFER_SIZE which might be defined in `cbmc.h` as:
99+
```c
100+
#define MLK_MAX_BUFFER_SIZE (SIZE_MAX >> 12)
101+
```
102+
and used, for example, as follows:
103+
```c
104+
void f(t *p, size_t len)
105+
__contract__(
106+
requires(len * sizeof(t) <= MLK_MAX_BUFFER_SIZE)
107+
requires(memory_no_alias(p, len * sizeof(t)))
108+
);
109+
```
110+
76111
### Memory footprint
77112

78113
The most common way to specify memory footprint in `assigns(...)` clauses is via `memory_slice(ptr, len)`. This asserts

0 commit comments

Comments
 (0)