Skip to content

Commit 3fcb729

Browse files
committed
Fix pointer validity instrumentation for incomplete array types
Use the same fall-back as is done for void pointers. Fixes: #8700
1 parent 1505c36 commit 3fcb729

File tree

4 files changed

+98
-22
lines changed

4 files changed

+98
-22
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// This test checks that dereferencing a pointer to an incomplete array type
2+
// does not trigger an invariant violation.
3+
// Related to issues #5293 and #4930
4+
5+
int (*a)[];
6+
7+
void b()
8+
{
9+
*a; // This should not cause an invariant violation
10+
}
11+
12+
void c()
13+
{
14+
int(*p)[];
15+
*p; // Another case with a local variable
16+
}
17+
18+
void test_pointer_arithmetic()
19+
{
20+
int(*p)[] = a;
21+
int(*q)[] = p + 1; // Pointer arithmetic on incomplete array type
22+
int(*r)[] = q - 1; // Subtraction
23+
}
24+
25+
int main()
26+
{
27+
b();
28+
c();
29+
test_pointer_arithmetic();
30+
return 0;
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
4+
^\[b.pointer_dereference.1\] line 9 dereference failure: pointer NULL in \*a: FAILURE$
5+
^\[c.pointer_dereference.1\] line 15 dereference failure: pointer NULL in \*p: FAILURE$
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring
11+
^CONVERSION ERROR$
12+
^Invariant check failed
13+
--
14+
Test that dereferencing pointers to incomplete array types does not cause
15+
invariant violations.

src/ansi-c/goto-conversion/goto_check_c.cpp

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,24 +1407,31 @@ void goto_check_ct::pointer_overflow_check(
14071407
if(object_type.id() != ID_empty)
14081408
{
14091409
auto size_of_expr_opt = size_of_expr(object_type, ns);
1410-
CHECK_RETURN(size_of_expr_opt.has_value());
1411-
exprt object_size = size_of_expr_opt.value();
1412-
1413-
const binary_exprt &binary_expr = to_binary_expr(expr);
1414-
exprt offset_operand = binary_expr.lhs().type().id() == ID_pointer
1415-
? binary_expr.rhs()
1416-
: binary_expr.lhs();
1417-
mult_exprt mul{
1418-
offset_operand,
1419-
typecast_exprt::conditional_cast(object_size, offset_operand.type())};
1420-
mul.add_source_location() = offset_operand.source_location();
1421-
1422-
flag_overridet override_overflow(offset_operand.source_location());
1423-
override_overflow.set_flag(
1424-
enable_signed_overflow_check, true, "signed_overflow_check");
1425-
override_overflow.set_flag(
1426-
enable_unsigned_overflow_check, true, "unsigned_overflow_check");
1427-
integer_overflow_check(mul, guard);
1410+
1411+
// For incomplete types (e.g., incomplete arrays), we cannot perform
1412+
// overflow checking since the size is unknown. We skip the overflow check
1413+
// for such types.
1414+
if(size_of_expr_opt.has_value())
1415+
{
1416+
exprt object_size = size_of_expr_opt.value();
1417+
1418+
const binary_exprt &binary_expr = to_binary_expr(expr);
1419+
exprt offset_operand = binary_expr.lhs().type().id() == ID_pointer
1420+
? binary_expr.rhs()
1421+
: binary_expr.lhs();
1422+
mult_exprt mul{
1423+
offset_operand,
1424+
typecast_exprt::conditional_cast(object_size, offset_operand.type())};
1425+
mul.add_source_location() = offset_operand.source_location();
1426+
1427+
flag_overridet override_overflow(offset_operand.source_location());
1428+
override_overflow.set_flag(
1429+
enable_signed_overflow_check, true, "signed_overflow_check");
1430+
override_overflow.set_flag(
1431+
enable_unsigned_overflow_check, true, "unsigned_overflow_check");
1432+
integer_overflow_check(mul, guard);
1433+
}
1434+
// else: incomplete type - cannot check overflow
14281435
}
14291436

14301437
// the result must be within object bounds or one past the end
@@ -1467,8 +1474,19 @@ void goto_check_ct::pointer_validity_check(
14671474
else
14681475
{
14691476
auto size_of_expr_opt = size_of_expr(expr.type(), ns);
1470-
CHECK_RETURN(size_of_expr_opt.has_value());
1471-
size = size_of_expr_opt.value();
1477+
1478+
// For incomplete array types (e.g., when dereferencing int (*p)[]),
1479+
// size_of_expr returns an empty optional since the size is not known. In
1480+
// this case, we perform a minimal validity check similar to void pointers.
1481+
if(!size_of_expr_opt.has_value())
1482+
{
1483+
// Cannot determine size for incomplete types; use minimal check
1484+
size = from_integer(1, size_type());
1485+
}
1486+
else
1487+
{
1488+
size = size_of_expr_opt.value();
1489+
}
14721490
}
14731491

14741492
auto conditions = get_pointer_dereferenceable_conditions(pointer, size);

src/goto-instrument/contracts/utils.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Date: September 2021
1010

1111
#include "utils.h"
1212

13+
#include <util/arith_tools.h>
1314
#include <util/c_types.h>
1415
#include <util/fresh_symbol.h>
1516
#include <util/graph.h>
@@ -179,9 +180,20 @@ exprt all_dereferences_are_valid(const exprt &expr, const namespacet &ns)
179180
if(auto deref = expr_try_dynamic_cast<dereference_exprt>(expr))
180181
{
181182
const auto size_of_expr_opt = size_of_expr(expr.type(), ns);
182-
CHECK_RETURN(size_of_expr_opt.has_value());
183183

184-
validity_checks.push_back(r_ok_exprt{deref->pointer(), *size_of_expr_opt});
184+
// For incomplete types (e.g., incomplete arrays), size_of_expr returns
185+
// an empty optional. In such cases, we use a minimal size check.
186+
if(size_of_expr_opt.has_value())
187+
{
188+
validity_checks.push_back(
189+
r_ok_exprt{deref->pointer(), *size_of_expr_opt});
190+
}
191+
else
192+
{
193+
// Cannot determine size for incomplete types; use minimal size
194+
validity_checks.push_back(
195+
r_ok_exprt{deref->pointer(), from_integer(1, size_type())});
196+
}
185197
}
186198

187199
for(const auto &op : expr.operands())

0 commit comments

Comments
 (0)