Skip to content

Commit c0e26dc

Browse files
author
Remi Delmas
committed
Soudness fix: pointer predicates can fail in assume contexts, yielding invalid pointers.
1 parent 66ba661 commit c0e26dc

File tree

1 file changed

+37
-2
lines changed

1 file changed

+37
-2
lines changed

src/ansi-c/library/cprover_contracts.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,7 @@ void __CPROVER_contracts_make_invalid_pointer(void **ptr)
11651165

11661166
/// \brief Implementation of the `is_fresh` front-end predicate.
11671167
///
1168-
/// The behaviour depends on the boolean flags carried by \p set
1168+
/// The behaviour depends on the boolean flags carried by \p write_set
11691169
/// which reflect the invocation context: checking vs. replacing a contract,
11701170
/// in a requires or an ensures clause context.
11711171
/// \param elem First argument of the `is_fresh` predicate
@@ -1232,6 +1232,13 @@ __CPROVER_HIDE:;
12321232
__CPROVER_assume(size <= __CPROVER_max_malloc_size);
12331233
}
12341234

1235+
// SOUNDNESS: allow predicate to fail
1236+
if(__VERIFIER_nondet___CPROVER_bool())
1237+
{
1238+
__CPROVER_contracts_make_invalid_pointer(elem);
1239+
return 0;
1240+
}
1241+
12351242
void *ptr = __CPROVER_allocate(size, 0);
12361243
*elem = ptr;
12371244

@@ -1286,6 +1293,13 @@ __CPROVER_HIDE:;
12861293
__CPROVER_assume(size <= __CPROVER_max_malloc_size);
12871294
}
12881295

1296+
// SOUNDNESS: allow predicate to fail
1297+
if(__VERIFIER_nondet___CPROVER_bool())
1298+
{
1299+
__CPROVER_contracts_make_invalid_pointer(elem);
1300+
return 0;
1301+
}
1302+
12891303
void *ptr = __CPROVER_allocate(size, 0);
12901304
*elem = ptr;
12911305

@@ -1338,7 +1352,7 @@ __CPROVER_HIDE:;
13381352
if(seen->elems[object_id] != 0)
13391353
return 0;
13401354
#endif
1341-
// record fresh object in the object set
1355+
// record fresh object in the object set
13421356
#ifdef __CPROVER_DFCC_DEBUG_LIB
13431357
// manually inlined below
13441358
__CPROVER_contracts_obj_set_add(seen, ptr);
@@ -1360,6 +1374,23 @@ __CPROVER_HIDE:;
13601374
}
13611375
}
13621376

1377+
/// \brief Implementation of the `pointer_in_range_dfcc` front-end predicate.
1378+
///
1379+
/// The behaviour depends on the boolean flags carried by \p write_set
1380+
/// which reflect the invocation context: checking vs. replacing a contract,
1381+
/// in a requires or an ensures clause context.
1382+
/// \param lb Lower bound pointer
1383+
/// \param ptr Target pointer of the predicate
1384+
/// \param ub Upper bound pointer
1385+
/// \param write_set Write set in which seen/allocated objects are recorded;
1386+
///
1387+
/// \details The behaviour is as follows:
1388+
/// - When \p set->assume_requires_ctx or \p set->assume_ensures_ctx is `true`,
1389+
/// the predicate checks that \p lb and \p ub are valid, into the same object,
1390+
/// ordered, and checks that \p ptr is between \p lb and \p ub.
1391+
/// - When \p set->assert_requires_ctx or \p set->assert_ensures_ctx is `true`,
1392+
/// the predicate checks that \p lb and \p ub are valid, into the same object,
1393+
/// ordered, and assigns \p ptr to some nondet offset between \p lb and \p ub.
13631394
__CPROVER_bool __CPROVER_contracts_pointer_in_range_dfcc(
13641395
void *lb,
13651396
void **ptr,
@@ -1386,7 +1417,11 @@ __CPROVER_HIDE:;
13861417
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
13871418
{
13881419
if(__VERIFIER_nondet___CPROVER_bool())
1420+
{
1421+
// SOUNDNESS: allow predicate to fail
1422+
__CPROVER_contracts_make_invalid_pointer(ptr);
13891423
return 0;
1424+
}
13901425

13911426
// add nondet offset
13921427
__CPROVER_size_t offset = __VERIFIER_nondet_size();

0 commit comments

Comments
 (0)