Skip to content

Skip function parameters and auxiliary variables when checking assigns clauses and check loop-freeness #6527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion regression/contracts/assigns_validity_pointer_01/test.desc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ SUCCESS
// bar
ASSERT \*foo::x > 0
IF ¬\(\*foo::x = 3\) THEN GOTO \d
IF ¬\(.*0.* = NULL\) THEN GOTO \d
ASSIGN .*::tmp_if_expr := \(\*\(.*0.*\) = 5 \? true : false\)
ASSIGN .*::tmp_if_expr\$\d := .*::tmp_if_expr \? true : false
ASSUME .*::tmp_if_expr\$\d
Expand Down
14 changes: 10 additions & 4 deletions regression/contracts/function_check_02/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ int initialize(int *arr)
)
// clang-format on
{
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;
Comment on lines +18 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this loop?

Copy link
Collaborator Author

@remi-delmas-3000 remi-delmas-3000 Dec 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a loop freeness check before running the instrumentation, so the loop needs to be eliminated to satisfy this loop-freeness check.
This manual unrolling of loops is just a quick and dirty fix. A better solution would be to modify chain.sh add a pass of loop unrolling or loop contracts instrumentation.


return 0;
}
Expand Down
16 changes: 16 additions & 0 deletions regression/contracts/loop-freeness-check/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
int foo(int *arr)
// clang-format off
__CPROVER_assigns(__CPROVER_POINTER_OBJECT(arr))
// clang-format off
{
for(int i = 0; i < 10; i++)
arr[i] = i;

return 0;
}

int main()
{
int arr[10];
foo(arr);
}
13 changes: 13 additions & 0 deletions regression/contracts/loop-freeness-check/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE
main.c
--enforce-contract foo
^--- begin invariant violation report ---$
^Invariant check failed$
^Condition: is_loop_free\(.*\)
^Reason: Loops remain in function 'foo', assigns clause checking instrumentation cannot be applied\.$
^EXIT=(127|134)$
^SIGNAL=0$
--
--
This test checks that loops that remain in the program when attempting to
instrument assigns clauses are detected and trigger an INVARIANT.
28 changes: 20 additions & 8 deletions regression/contracts/quantifiers-exists-ensures-enforce/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ int f1(int *arr)
})
// clang-format on
{
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;

return 0;
}
Expand All @@ -24,10 +30,16 @@ int f2(int *arr)
})
// clang-format on
{
for(int i = 0; i < 10; i++)
{
arr[i] = 0;
}
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;

return 0;
}
Expand Down
28 changes: 22 additions & 6 deletions regression/contracts/quantifiers-exists-requires-enforce/main.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <stdbool.h>
#include <stdlib.h>

#define MAX_LEN 64
#define MAX_LEN 10

// clang-format off
bool f1(int *arr, int len)
Expand All @@ -18,11 +18,27 @@ bool f1(int *arr, int len)
// clang-format on
{
bool found_four = false;
for(int i = 0; i <= MAX_LEN; i++)
{
if(i < len)
found_four |= (arr[i] == 4);
}
if(0 < len)
found_four |= (arr[0] == 4);
if(1 < len)
found_four |= (arr[1] == 4);
if(2 < len)
found_four |= (arr[2] == 4);
if(3 < len)
found_four |= (arr[3] == 4);
if(4 < len)
found_four |= (arr[4] == 4);
if(5 < len)
found_four |= (arr[5] == 4);
if(6 < len)
found_four |= (arr[6] == 4);
if(7 < len)
found_four |= (arr[7] == 4);
if(8 < len)
found_four |= (arr[8] == 4);

if(9 < len)
found_four |= (arr[9] == 4);

// clang-format off
return (len > 0 ==> found_four);
Expand Down
28 changes: 22 additions & 6 deletions regression/contracts/quantifiers-forall-ensures-enforce/main.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <stdlib.h>

#define MAX_LEN 16
#define MAX_LEN 10

// clang-format off
int f1(int *arr, int len)
Expand All @@ -12,11 +12,27 @@ int f1(int *arr, int len)
})
// clang-format on
{
for(int i = 0; i < MAX_LEN; i++)
{
if(i < len)
arr[i] = 0;
}
if(0 < len)
arr[0] = 0;
if(1 < len)
arr[1] = 0;
if(2 < len)
arr[2] = 0;
if(3 < len)
arr[3] = 0;
if(4 < len)
arr[4] = 0;
if(5 < len)
arr[5] = 0;
if(6 < len)
arr[6] = 0;
if(7 < len)
arr[7] = 0;
if(8 < len)
arr[8] = 0;
if(9 < len)
arr[9] = 0;

return 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ main.c
^EXIT=0$
^SIGNAL=0$
^\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS$
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)i\] is assignable: SUCCESS$
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)\d\] is assignable: SUCCESS$
^VERIFICATION SUCCESSFUL$
--
^warning: ignoring
^\[f1.\d+\] line \d+ Check that arr\[\(.*\)\d\] is assignable: FAILURE$
--
The purpose of this test is to ensure that we can safely use __CPROVER_forall
within positive contexts (enforced ENSURES clauses).
Expand Down
12 changes: 10 additions & 2 deletions regression/contracts/quantifiers-forall-requires-enforce/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ bool f1(int *arr)
// clang-format on
{
bool is_identity = true;
for(int i = 0; i < 10; ++i)
is_identity &= (arr[i] == i);
is_identity &= (arr[0] == 0);
is_identity &= (arr[1] == 1);
is_identity &= (arr[2] == 2);
is_identity &= (arr[3] == 3);
is_identity &= (arr[4] == 4);
is_identity &= (arr[5] == 5);
is_identity &= (arr[6] == 6);
is_identity &= (arr[7] == 7);
is_identity &= (arr[8] == 8);
is_identity &= (arr[9] == 9);
return is_identity;
}

Expand Down
14 changes: 10 additions & 4 deletions regression/contracts/quantifiers-nested-01/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ int f1(int *arr)
})
// clang-format on
{
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;

return 0;
}
Expand Down
14 changes: 10 additions & 4 deletions regression/contracts/quantifiers-nested-03/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ __CPROVER_assigns(__CPROVER_POINTER_OBJECT(arr))
)
// clang-format on
{
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
arr[5] = 5;
arr[6] = 6;
arr[7] = 7;
arr[8] = 8;
arr[9] = 9;

return 0;
}
Expand Down
29 changes: 21 additions & 8 deletions src/goto-instrument/contracts/contracts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
return true;
}

if(check_for_looped_mallocs(function_obj->second.body))
auto &function_body = function_obj->second.body;

if(check_for_looped_mallocs(function_body))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is no longer necessary, you can remove it. It checks whether there is malloc within loops, but we assume contract instrumentation happens after all loops have been unrolled.

{
return true;
}
Expand All @@ -1142,21 +1144,24 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
function,
symbol_table);

// detect and add static local variables
// Detect and add static local variables
assigns.add_static_locals_to_write_set(goto_functions, function);

// Add formal parameters to write set
for(const auto &param : to_code_type(target.type).parameters())
assigns.add_to_write_set(ns.lookup(param.get_identifier()).symbol_expr());

auto instruction_it = function_obj->second.body.instructions.begin();
auto instruction_it = function_body.instructions.begin();
for(const auto &car : assigns.get_write_set())
{
auto snapshot_instructions = car.generate_snapshot_instructions();
insert_before_swap_and_advance(
function_obj->second.body, instruction_it, snapshot_instructions);
function_body, instruction_it, snapshot_instructions);
};

// Restore internal coherence in the programs
goto_functions.update();

// Full inlining of the function body
// Inlining is performed so that function calls to a same function
// occurring under different write sets get instrumented specifically
Expand All @@ -1168,15 +1173,23 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
decorated.get_recursive_function_warnings_count() == 0,
"Recursive functions found during inlining");

// restore internal invariants
// Clean up possible fake loops that are due to `IF 0!=0 GOTO i` instructions
simplify_gotos(function_body, ns);

// Restore internal coherence in the programs
goto_functions.update();

INVARIANT(
is_loop_free(function_body, ns, log),
"Loops remain in function '" + id2string(function) +
"', assigns clause checking instrumentation cannot be applied.");

// Insert write set inclusion checks.
check_frame_conditions(
function_obj->first,
function_obj->second.body,
function,
function_body,
instruction_it,
function_obj->second.body.instructions.end(),
function_body.instructions.end(),
assigns,
// skip checks on function parameter assignments
true);
Expand Down
Loading