Skip to content

Commit 00b7109

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 4fe3ade commit 00b7109

File tree

4 files changed

+94
-22
lines changed

4 files changed

+94
-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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
9+
^CONVERSION ERROR$
10+
--
11+
Test that dereferencing pointers to incomplete array types does not cause
12+
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.
1480+
// In 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: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,20 @@ exprt all_dereferences_are_valid(const exprt &expr, const namespacet &ns)
179179
if(auto deref = expr_try_dynamic_cast<dereference_exprt>(expr))
180180
{
181181
const auto size_of_expr_opt = size_of_expr(expr.type(), ns);
182-
CHECK_RETURN(size_of_expr_opt.has_value());
183182

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

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

0 commit comments

Comments
 (0)