Skip to content

Commit f1b09e2

Browse files
Merge pull request #7445 from remi-delmas-3000/map-contract-name
CONTRACTS: add function-contract mapping to the CLI
2 parents 50a795e + 7d0ebd5 commit f1b09e2

File tree

13 files changed

+255
-50
lines changed

13 files changed

+255
-50
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
bool my_contract(char *arr, size_t size, char *out)
5+
// clang-format off
6+
__CPROVER_requires(__CPROVER_is_fresh(arr, size))
7+
__CPROVER_requires(__CPROVER_is_fresh(out, sizeof(*out)))
8+
__CPROVER_assigns((size > 0) : arr[0], *out)
9+
__CPROVER_ensures(
10+
(size > 0) ==> __CPROVER_return_value &&
11+
(arr[0] == 0) &&
12+
(*out == __CPROVER_old(arr[0])))
13+
__CPROVER_ensures(
14+
(size == 0) ==> !__CPROVER_return_value)
15+
// clang-format on
16+
;
17+
18+
bool bar(char *arr, size_t size, char *out)
19+
{
20+
if(size > 0)
21+
{
22+
*out = arr[0];
23+
arr[0] = 0;
24+
return true;
25+
}
26+
return false;
27+
}
28+
29+
bool foo(char *arr, size_t size, char *out)
30+
{
31+
return bar(arr, size, out);
32+
}
33+
34+
void main()
35+
{
36+
char *arr;
37+
size_t size;
38+
char *out;
39+
foo(arr, size, out);
40+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo/ _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^Invalid function-contract mapping$
5+
^Reason: couldn't find contract name after '/' in 'foo/'$
6+
^EXIT=(10|6)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that when the contract name is missing an error is triggered.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract /my_contract _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^Invalid function-contract mapping$
5+
^Reason: couldn't find function name before '/' in '/my_contract'$
6+
^EXIT=(10|6)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that when the function name is missing an error is triggered.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo/bar/my_contract _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^Invalid function-contract mapping$
5+
^Reason: couldn't parse 'foo/bar/my_contract'$
6+
^EXIT=(10|6)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that when the function name is missing an error is triggered.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo/my_contract _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
This test demonstrates the ability to specify a function_name/contract_name
10+
mapping for contract checking.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo/my_contract --replace-call-with-contract bar/my_contract _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
This test demonstrates the ability to check specify a function_name/contract_name
10+
mapping for contract checking and contract replacement.
11+
12+
The same contract `my_contract` is used for both `foo` and `bar` functions in
13+
checking and replacement mode, respectively.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null --dfcc main --enforce-contract foo/my_contractt _ --pointer-check --pointer-primitive-check --pointer-overflow-check
4+
^Contract 'my_contractt' not found, deriving empty pure contract 'contract::my_contractt' from function 'foo'$
5+
^VERIFICATION FAILED$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that we get a warning that a default contract is derived from the
11+
signature of the function being checked when specifying a non-existing contract.

src/goto-instrument/contracts/contracts.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,22 @@ Date: February 2016
2929
#include <string>
3030
#include <unordered_set>
3131

32+
// clang-format off
3233
#define FLAG_LOOP_CONTRACTS "apply-loop-contracts"
3334
#define HELP_LOOP_CONTRACTS \
3435
" --apply-loop-contracts\n" \
3536
" check and use loop contracts when provided\n"
3637

3738
#define FLAG_REPLACE_CALL "replace-call-with-contract"
3839
#define HELP_REPLACE_CALL \
39-
" --replace-call-with-contract <fun>\n" \
40-
" replace calls to fun with fun's contract\n"
40+
" --replace-call-with-contract <function>[/contract]\n" \
41+
" replace calls to function with contract\n"
4142

4243
#define FLAG_ENFORCE_CONTRACT "enforce-contract"
4344
#define HELP_ENFORCE_CONTRACT \
44-
" --enforce-contract <fun> wrap fun with an assertion of its contract\n"
45+
" --enforce-contract <function>[/contract]" \
46+
" wrap function with an assertion of contract\n"
47+
// clang-format on
4548

4649
class local_may_aliast;
4750

src/goto-instrument/contracts/doc/user/contracts-cli.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@
55
The program transformation takes the following parameters:
66

77
```
8-
goto-instrument [--apply-loop-contracts] [--enforce-contract f] (--replace-call-with-contract g)* in.gb out.gb
8+
goto-instrument [--apply-loop-contracts] [--enforce-contract <function>] (--replace-call-with-contract <function>)* in.gb out.gb
99
```
1010

1111
Where:
1212
- `--apply-loop-contracts` is optional and specifies to apply loop contracts globally;
13-
- `--enforce-contract f` is optional and specifies that `f` must be checked against its contract.
14-
- `--replace-call-with-contract g` is optional and specifies that all calls to `g` must be replaced with its contract;
13+
- `--enforce-contract <function>` is optional and specifies that `function` must be checked against its contract.
14+
- `--replace-call-with-contract <function>` is optional and specifies that all calls to `function` must be replaced with its contract;
1515

1616
## Applying the function contracts transformation (with the dynamic frames method)
1717

1818
The program transformation takes the following parameters:
1919

2020
```
21-
goto-instrument --dfcc harness [--enforce-contract f] (--replace-call-with-contract g)* [--apply-loop-contracts] in.gb out.gb
21+
goto-instrument --dfcc harness [--enforce-contract <function>[/<contract>]] (--replace-call-with-contract <function>[/<contract>])* in.gb out.gb
2222
```
2323

2424
Where:
2525
- `--dfcc harness` specifies the proof harness (i.e. the entry point of the analysis);
26-
- `--enforce-contract f` is optional and specifies that `f` must be checked against its contract.
27-
- `--replace-call-with-contract g` is optional and specifies that all calls to `g` must be replaced with its contract;
26+
- `--enforce-contract <function>[/<contract>]` is optional and specifies that `function` must be checked against `contract`.
27+
When `contract` is not specfied, the contract is assumed to be carried by the `function` itself.
28+
- `--replace-call-with-contract <function>[/<contract>]` is optional and specifies that all calls to `function` must be replaced with `contract`.
29+
When `contract` is not specfied, the contract is assumed to be carried by the `function` itself.
30+
2831

src/goto-instrument/contracts/dynamic-frames/dfcc.cpp

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Author: Remi Delmas, [email protected]
2222
#include <util/pointer_predicates.h>
2323
#include <util/prefix.h>
2424
#include <util/std_expr.h>
25+
#include <util/string_utils.h>
2526

2627
#include <goto-programs/goto_convert_functions.h>
2728
#include <goto-programs/goto_functions.h>
@@ -47,6 +48,72 @@ Author: Remi Delmas, [email protected]
4748

4849
#include "dfcc_lift_memory_predicates.h"
4950

51+
invalid_function_contract_pair_exceptiont::
52+
invalid_function_contract_pair_exceptiont(
53+
std::string reason,
54+
std::string correct_format)
55+
: cprover_exception_baset(std::move(reason)),
56+
correct_format(std::move(correct_format))
57+
{
58+
}
59+
60+
std::string invalid_function_contract_pair_exceptiont::what() const
61+
{
62+
std::string res;
63+
64+
res += "Invalid function-contract mapping";
65+
res += "\nReason: " + reason;
66+
67+
if(!correct_format.empty())
68+
{
69+
res += "\nFormat: " + correct_format;
70+
}
71+
72+
return res;
73+
}
74+
75+
#include <iostream>
76+
77+
static std::pair<irep_idt, irep_idt>
78+
parse_function_contract_pair(const irep_idt &cli_flag)
79+
{
80+
auto const correct_format_message =
81+
"the format for function and contract pairs is "
82+
"`<function_name>[/<contract_name>]`";
83+
84+
std::string cli_flag_str = id2string(cli_flag);
85+
86+
auto split = split_string(cli_flag_str, '/', true, false);
87+
88+
if(split.size() == 1)
89+
{
90+
return std::make_pair(cli_flag, cli_flag);
91+
}
92+
else if(split.size() == 2)
93+
{
94+
auto function_name = split[0];
95+
if(function_name.size() == 0)
96+
{
97+
throw invalid_function_contract_pair_exceptiont{
98+
"couldn't find function name before '/' in '" + cli_flag_str + "'",
99+
correct_format_message};
100+
}
101+
auto contract_name = split[1];
102+
if(contract_name.size() == 0)
103+
{
104+
throw invalid_function_contract_pair_exceptiont{
105+
"couldn't find contract name after '/' in '" + cli_flag_str + "'",
106+
correct_format_message};
107+
}
108+
return std::make_pair(function_name, contract_name);
109+
}
110+
else
111+
{
112+
throw invalid_function_contract_pair_exceptiont{
113+
"couldn't parse '" + cli_flag_str + "'", correct_format_message};
114+
}
115+
}
116+
50117
void dfcc(
51118
const optionst &options,
52119
goto_modelt &goto_model,
@@ -59,17 +126,15 @@ void dfcc(
59126
message_handlert &message_handler)
60127
{
61128
std::map<irep_idt, irep_idt> to_replace_map;
62-
for(const auto &function_id : to_replace)
63-
to_replace_map.insert({function_id, function_id});
129+
for(const auto &cli_flag : to_replace)
130+
to_replace_map.insert(parse_function_contract_pair(cli_flag));
64131

65132
dfcc(
66133
options,
67134
goto_model,
68135
harness_id,
69-
to_check.has_value()
70-
? optionalt<std::pair<irep_idt, irep_idt>>(
71-
std::pair<irep_idt, irep_idt>(to_check.value(), to_check.value()))
72-
: optionalt<std::pair<irep_idt, irep_idt>>{},
136+
to_check.has_value() ? parse_function_contract_pair(to_check.value())
137+
: optionalt<std::pair<irep_idt, irep_idt>>{},
73138
allow_recursive_calls,
74139
to_replace_map,
75140
apply_loop_contracts,
@@ -164,7 +229,7 @@ void dfcct::check_transform_goto_model_preconditions()
164229
"' either not found or has no body");
165230

166231
// triggers signature compatibility checking
167-
contract_handler.get_pure_contract_symbol(pair.second);
232+
contract_handler.get_pure_contract_symbol(pair.second, pair.first);
168233

169234
PRECONDITION_WITH_DIAGNOSTICS(
170235
pair.first != harness_id,
@@ -196,7 +261,7 @@ void dfcct::check_transform_goto_model_preconditions()
196261
"Function to replace '" + id2string(pair.first) + "' not found");
197262

198263
// triggers signature compatibility checking
199-
contract_handler.get_pure_contract_symbol(pair.second);
264+
contract_handler.get_pure_contract_symbol(pair.second, pair.first);
200265

201266
PRECONDITION_WITH_DIAGNOSTICS(
202267
pair.first != harness_id,

src/goto-instrument/contracts/dynamic-frames/dfcc.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Author: Remi Delmas, [email protected]
2929
#ifndef CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_H
3030
#define CPROVER_GOTO_INSTRUMENT_CONTRACTS_DYNAMIC_FRAMES_DFCC_H
3131

32+
#include <util/exception_utils.h>
3233
#include <util/irep.h>
3334
#include <util/message.h>
3435

@@ -56,19 +57,32 @@ class optionst;
5657

5758
// clang-format off
5859
#define HELP_DFCC \
59-
"--dfcc activate dynamic frame condition checking for function\n"\
60-
" contracts using given function as entry point"
61-
// clang-format on
60+
"--dfcc <harness> activate dynamic frame condition checking for function\n"\
61+
" contracts using the given harness as entry point"
6262

63-
// clang-format off
6463
#define FLAG_ENFORCE_CONTRACT_REC "enforce-contract-rec"
6564
#define OPT_ENFORCE_CONTRACT_REC "(" FLAG_ENFORCE_CONTRACT_REC "):"
6665
#define HELP_ENFORCE_CONTRACT_REC \
67-
" --enforce-contract-rec <fun> wrap fun with an assertion of its contract\n"\
66+
" --enforce-contract-rec <function>[/<contract>]" \
67+
" wrap fun with an assertion of the contract\n"\
6868
" and assume recursive calls to fun satisfy \n"\
6969
" the contract"
7070
// clang-format on
7171

72+
/// Exception thrown for bad function/contract specification pairs passed on
73+
/// the CLI.
74+
class invalid_function_contract_pair_exceptiont : public cprover_exception_baset
75+
{
76+
public:
77+
explicit invalid_function_contract_pair_exceptiont(
78+
std::string reason,
79+
std::string correct_format = "");
80+
81+
std::string what() const override;
82+
83+
std::string correct_format;
84+
};
85+
7286
/// \ingroup dfcc-module
7387
/// \brief Applies function contracts transformation to GOTO model,
7488
/// using the dynamic frame condition checking approach.

0 commit comments

Comments
 (0)