Skip to content

Commit 8c7ba86

Browse files
authored
Unbounded proofs of memory safety (#146)
* Add a CBMC proof harness for multiSearch function Signed-off-by: Felipe R. Monteiro <[email protected]> * Make all CBMC proofs unbounded Signed-off-by: Felipe R. Monteiro <[email protected]> * Make coreJSON more verification friendly In order to turn all bounded CBMC proofs into unbounded proofs, we must make some small changes to coreJSON. Basically, we want to avoid uninitialized variables (CBMC sees them as non-deterministic values) and make sure the code in loops is resilient enough when working with inputs of arbitrary size. The latter is important because we must annotate all loops with loop invariants. The implementation should work together with these annotations in order to prove memory safety for inputs of arbitrary size. Signed-off-by: Felipe R. Monteiro <[email protected]> * Include patch with loop invariants Modify existing CBMC proofs to remove any assumptions over the size of the input (a.k.a. bound). Instrument all loops with Hoare-style loop invariants and assigns clauses (define the modified memory within a loop), which CBMC uses to prove correctness. Annotate all loops using decreases clauses, which CBMC uses to prove termination. Signed-off-by: Felipe R. Monteiro <[email protected]> * Add patch for loop invariants in CI Signed-off-by: Felipe R. Monteiro <[email protected]> --------- Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent 121d5fc commit 8c7ba86

File tree

25 files changed

+466
-221
lines changed

25 files changed

+466
-221
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,4 @@ jobs:
161161
uses: FreeRTOS/CI-CD-Github-Actions/run_cbmc@main
162162
with:
163163
proofs_dir: test/cbmc/proofs
164+
run_cbmc_proofs_command: cd ../../../; git apply -v loop_invariants.patch; cd test/cbmc/proofs; ./run-cbmc-proofs.py

loop_invariants.patch

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
diff --git a/source/core_json.c b/source/core_json.c
2+
index 901b2e1..8bdd89c 100644
3+
--- a/source/core_json.c
4+
+++ b/source/core_json.c
5+
@@ -62,6 +62,21 @@ typedef union
6+
#define isSquareOpen_( x ) ( ( x ) == '[' )
7+
#define isSquareClose_( x ) ( ( x ) == ']' )
8+
9+
+/**
10+
+ * Renaming all loop-contract clauses from CBMC for readability.
11+
+ * For more information about loop contracts in CBMC, see
12+
+ * https://diffblue.github.io/cbmc/contracts-user.html.
13+
+ */
14+
+#ifdef CBMC
15+
+#define loopInvariant(...) __CPROVER_loop_invariant(__VA_ARGS__)
16+
+#define decreases(...) __CPROVER_decreases(__VA_ARGS__)
17+
+#define assigns(...) __CPROVER_assigns(__VA_ARGS__)
18+
+#else
19+
+#define loopInvariant(...)
20+
+#define decreases(...)
21+
+#define assigns(...)
22+
+#endif
23+
+
24+
/**
25+
* @brief Advance buffer index beyond whitespace.
26+
*
27+
@@ -78,6 +93,9 @@ static void skipSpace( const char * buf,
28+
assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
29+
30+
for( i = *start; i < max; i++ )
31+
+ assigns( i )
32+
+ loopInvariant( *start <= i && i <= max )
33+
+ decreases( max - i )
34+
{
35+
if( !isspace_( buf[ i ] ) )
36+
{
37+
@@ -102,6 +120,13 @@ static size_t countHighBits( uint8_t c )
38+
size_t i = 0;
39+
40+
while( ( n & 0x80U ) != 0U )
41+
+ assigns( i, n )
42+
+ loopInvariant (
43+
+ ( 0U <= i ) && ( i <= 8U )
44+
+ && ( n == ( c & ( 0xFF >> i ) ) << i )
45+
+ && ( ( ( c >> ( 8U - i ) ) + 1U ) == ( 1U << i ) )
46+
+ )
47+
+ decreases( 8U - i )
48+
{
49+
i++;
50+
n = ( n & 0x7FU ) << 1U;
51+
@@ -210,6 +235,13 @@ static bool skipUTF8MultiByte( const char * buf,
52+
/* The bit count is 1 greater than the number of bytes,
53+
* e.g., when j is 2, we skip one more byte. */
54+
for( j = bitCount - 1U; j > 0U; j-- )
55+
+ assigns( j, i, value, c.c )
56+
+ loopInvariant(
57+
+ ( 0 <= j ) && ( j <= bitCount - 1 )
58+
+ && ( *start <= i ) && ( i <= max )
59+
+ && ( ( i == max ) ==> ( j > 0 ) )
60+
+ )
61+
+ decreases( j )
62+
{
63+
i++;
64+
65+
@@ -345,6 +377,12 @@ static bool skipOneHexEscape( const char * buf,
66+
if( ( end < max ) && ( buf[ i ] == '\\' ) && ( buf[ i + 1U ] == 'u' ) )
67+
{
68+
for( i += 2U; i < end; i++ )
69+
+ assigns( value, i )
70+
+ loopInvariant(
71+
+ ( *start + 2U <= i ) && ( i <= end ) &&
72+
+ ( 0U <= value ) && ( value < ( 1U << ( 4U * ( i - ( 2U + *start ) ) ) ) )
73+
+ )
74+
+ decreases( end - i )
75+
{
76+
uint8_t n = hexToInt( buf[ i ] );
77+
78+
@@ -522,6 +560,9 @@ static bool skipString( const char * buf,
79+
i++;
80+
81+
while( i < max )
82+
+ assigns( i )
83+
+ loopInvariant( *start + 1U <= i && i <= max )
84+
+ decreases( max - i )
85+
{
86+
if( buf[ i ] == '"' )
87+
{
88+
@@ -580,6 +621,9 @@ static bool strnEq( const char * a,
89+
assert( ( a != NULL ) && ( b != NULL ) );
90+
91+
for( i = 0; i < n; i++ )
92+
+ assigns( i )
93+
+ loopInvariant( i <= n )
94+
+ decreases( n - i )
95+
{
96+
if( a[ i ] != b[ i ] )
97+
{
98+
@@ -681,6 +725,7 @@ static bool skipAnyLiteral( const char * buf,
99+
* false otherwise.
100+
*/
101+
#define MAX_FACTOR ( MAX_INDEX_VALUE / 10 )
102+
+
103+
static bool skipDigits( const char * buf,
104+
size_t * start,
105+
size_t max,
106+
@@ -695,6 +740,9 @@ static bool skipDigits( const char * buf,
107+
saveStart = *start;
108+
109+
for( i = *start; i < max; i++ )
110+
+ assigns( value, i )
111+
+ loopInvariant( *start <= i && i <= max )
112+
+ decreases( max - i )
113+
{
114+
if( !isdigit_( buf[ i ] ) )
115+
{
116+
@@ -944,6 +992,9 @@ static void skipArrayScalars( const char * buf,
117+
i = *start;
118+
119+
while( i < max )
120+
+ assigns( i )
121+
+ loopInvariant( *start <= i && i <= max )
122+
+ decreases( max - i )
123+
{
124+
if( skipAnyScalar( buf, &i, max ) != true )
125+
{
126+
@@ -986,6 +1037,13 @@ static void skipObjectScalars( const char * buf,
127+
i = *start;
128+
129+
while( i < max )
130+
+ assigns( i, *start, comma )
131+
+ loopInvariant(
132+
+ i >= *start
133+
+ && __CPROVER_loop_entry( i ) <= i && i <= max
134+
+ && __CPROVER_loop_entry( *start ) <= *start && *start <= max
135+
+ )
136+
+ decreases( max - i )
137+
{
138+
if( skipString( buf, &i, max ) != true )
139+
{
140+
@@ -1082,6 +1140,14 @@ static JSONStatus_t skipCollection( const char * buf,
141+
i = *start;
142+
143+
while( i < max )
144+
+ assigns( i, depth, c, __CPROVER_object_whole( stack ), ret )
145+
+ loopInvariant(
146+
+ -1 <= depth && depth <= JSON_MAX_DEPTH
147+
+ && *start <= i && i <= max
148+
+ && ( ( ret == JSONSuccess ) ==> i >= *start + 2U )
149+
+ && ( ret == JSONSuccess || ret == JSONPartial || ret == JSONIllegalDocument || ret == JSONMaxDepthExceeded )
150+
+ )
151+
+ decreases( max - i )
152+
{
153+
c = buf[ i ];
154+
i++;
155+
@@ -1363,6 +1429,9 @@ static bool objectSearch( const char * buf,
156+
skipSpace( buf, &i, max );
157+
158+
while( i < max )
159+
+ assigns( i, key, keyLength, value, valueLength )
160+
+ loopInvariant( __CPROVER_loop_entry( i ) <= i && i <= max )
161+
+ decreases( max - i )
162+
{
163+
if( nextKeyValuePair( buf, &i, max, &key, &keyLength,
164+
&value, &valueLength ) != true )
165+
@@ -1430,6 +1499,9 @@ static bool arraySearch( const char * buf,
166+
skipSpace( buf, &i, max );
167+
168+
while( i < max )
169+
+ assigns( i, currentIndex, value, valueLength )
170+
+ loopInvariant( __CPROVER_loop_entry( i ) <= i && i <= max && currentIndex < i )
171+
+ decreases( max - i )
172+
{
173+
if( nextValue( buf, &i, max, &value, &valueLength ) != true )
174+
{
175+
@@ -1495,6 +1567,9 @@ static bool skipQueryPart( const char * buf,
176+
while( ( i < max ) &&
177+
!isSeparator_( buf[ i ] ) &&
178+
!isSquareOpen_( buf[ i ] ) )
179+
+ assigns( i )
180+
+ loopInvariant( i <= max )
181+
+ decreases( max - i )
182+
{
183+
i++;
184+
}
185+
@@ -1541,6 +1616,17 @@ static JSONStatus_t multiSearch( const char * buf,
186+
assert( ( max > 0U ) && ( queryLength > 0U ) );
187+
188+
while( i < queryLength )
189+
+ assigns( i, start, queryStart, value, length )
190+
+ loopInvariant(
191+
+ 0U <= start && start < max
192+
+ && 0U < length && length <= max
193+
+ && start + length <= max
194+
+ && ( ( i == queryLength && ret == JSONSuccess && buf[ start ] == '"' ) ==> length >= 2U )
195+
+ && 0U <= value && value < max
196+
+ && 0U <= i && i <= queryLength
197+
+ && 0U <= queryStart && queryStart <= queryLength
198+
+ )
199+
+ decreases( queryLength - i )
200+
{
201+
bool found = false;
202+

0 commit comments

Comments
 (0)