diff --git a/.clang-format-ignore b/.clang-format-ignore index ea36ef5632a..bf05754242f 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,3 +1,4 @@ jbmc/src/miniz/miniz.cpp +src/cprover/wcwidth.c src/nonstd/optional.hpp unit/catch/catch.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8945f9429c6..ebd3ad7f41f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "$" "$" "$" + "$" "$" "$" "$" @@ -214,6 +215,8 @@ cprover_default_properties( cbmc cbmc-lib cpp + cprover + cprover-lib crangler crangler-lib driver diff --git a/regression/cbmc/pointer-predicates/in_range1.c b/regression/cbmc/pointer-predicates/in_range1.c new file mode 100644 index 00000000000..8e27b5f99df --- /dev/null +++ b/regression/cbmc/pointer-predicates/in_range1.c @@ -0,0 +1,13 @@ +int x; + +int main() +{ + __CPROVER_assert(__CPROVER_pointer_in_range(&x, &x, &x), "property 1"); + __CPROVER_assert(__CPROVER_pointer_in_range(&x, &x, &x + 1), "property 2"); + __CPROVER_assert( + __CPROVER_pointer_in_range(&x, &x + 1, &x + 1), "property 3"); + __CPROVER_assert(!__CPROVER_pointer_in_range(&x, &x + 1, &x), "property 4"); + __CPROVER_assert(!__CPROVER_pointer_in_range(0, &x, &x), "property 5"); + __CPROVER_assert(!__CPROVER_pointer_in_range(&x, 0, &x), "property 6"); + __CPROVER_assert(!__CPROVER_pointer_in_range(&x, &x, 0), "property 7"); +} diff --git a/regression/cbmc/pointer-predicates/in_range1.desc b/regression/cbmc/pointer-predicates/in_range1.desc new file mode 100644 index 00000000000..3465178e375 --- /dev/null +++ b/regression/cbmc/pointer-predicates/in_range1.desc @@ -0,0 +1,8 @@ +CORE +in_range1.c + +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +-- diff --git a/regression/cprover/Makefile b/regression/cprover/Makefile new file mode 100644 index 00000000000..7ba46755339 --- /dev/null +++ b/regression/cprover/Makefile @@ -0,0 +1,13 @@ +default: test-no-p + +include ../../src/config.inc +include ../../src/common + +test: + @../test.pl -e -p -c '../../../src/cprover/cprover' + +test-no-p: + @../test.pl -e -c '../../../src/cprover/cprover' + +clean: + $(RM) tests.log diff --git a/regression/cprover/arrays/array1.c b/regression/cprover/arrays/array1.c new file mode 100644 index 00000000000..8b451311cb8 --- /dev/null +++ b/regression/cprover/arrays/array1.c @@ -0,0 +1,10 @@ +int array[10]; + +int main() +{ + array[1l] = 10; + array[2l] = 20; + __CPROVER_assert(array[1l] == 10, "property 1"); // passes + __CPROVER_assert(array[2l] == 20, "property 2"); // passes + __CPROVER_assert(array[2l] == 30, "property 3"); // fails +} diff --git a/regression/cprover/arrays/array1.desc b/regression/cprover/arrays/array1.desc new file mode 100644 index 00000000000..75cf67d5703 --- /dev/null +++ b/regression/cprover/arrays/array1.desc @@ -0,0 +1,14 @@ +CORE +array1.c +--text --solve --inline --no-safety +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[element_address\(❝array❞, 1\):=10\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[element_address\(❝array❞, 2\):=20\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(element_address\(❝array❞, 1\)\) = 10\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(element_address\(❝array❞, 2\)\) = 20\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(ς\(element_address\(❝array❞, 2\)\) = 30\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: REFUTED$ +-- diff --git a/regression/cprover/arrays/array2.c b/regression/cprover/arrays/array2.c new file mode 100644 index 00000000000..733e355d963 --- /dev/null +++ b/regression/cprover/arrays/array2.c @@ -0,0 +1,8 @@ +int main() +{ + int array[10]; + int i, j, k; + __CPROVER_assume(i == j); + __CPROVER_assert(array[i] == array[j], "property 1"); // passes + __CPROVER_assert(array[i] == array[k], "property 2"); // fails +} diff --git a/regression/cprover/arrays/array2.desc b/regression/cprover/arrays/array2.desc new file mode 100644 index 00000000000..75f86008155 --- /dev/null +++ b/regression/cprover/arrays/array2.desc @@ -0,0 +1,11 @@ +CORE +array2.c +--text --solve --inline --no-safety +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. \(S23\(ς\) ∧ ς\(❝main::1::i❞\) = ς\(❝main::1::j❞\)\) ⇒ S24\(ς\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(ς\(element_address\(❝main::1::array❞, cast\(ς\(❝main::1::i❞\), signedbv\[64\]\)\)\) = ς\(element_address\(❝main::1::array❞, cast\(ς\(❝main::1::j❞\), signedbv\[64\]\)\)\)\)$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(element_address\(❝main::1::array❞, cast\(ς\(❝main::1::i❞\), signedbv\[64\]\)\)\) = ς\(element_address\(❝main::1::array❞, cast\(ς\(❝main::1::k❞\), signedbv\[64\]\)\)\)\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/arrays/array4.c b/regression/cprover/arrays/array4.c new file mode 100644 index 00000000000..f148237d110 --- /dev/null +++ b/regression/cprover/arrays/array4.c @@ -0,0 +1,7 @@ +int main() +{ + int a[100]; + int *p = a; + __CPROVER_assert(p[23] == a[23], "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/arrays/array4.desc b/regression/cprover/arrays/array4.desc new file mode 100644 index 00000000000..f7cc79fa687 --- /dev/null +++ b/regression/cprover/arrays/array4.desc @@ -0,0 +1,7 @@ +CORE +array4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/arrays/array_literal1.c b/regression/cprover/arrays/array_literal1.c new file mode 100644 index 00000000000..100a4945fb9 --- /dev/null +++ b/regression/cprover/arrays/array_literal1.c @@ -0,0 +1,8 @@ +int array[10] = {0, 1, 2, 3, 4}; + +int main() +{ + __CPROVER_assert(array[0l] == 0, "property 1"); // passes + __CPROVER_assert(array[1l] == 1, "property 2"); // passes + return 0; +} diff --git a/regression/cprover/arrays/array_literal1.desc b/regression/cprover/arrays/array_literal1.desc new file mode 100644 index 00000000000..4921d0263be --- /dev/null +++ b/regression/cprover/arrays/array_literal1.desc @@ -0,0 +1,8 @@ +CORE +array_literal1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/arrays/array_literal2.c b/regression/cprover/arrays/array_literal2.c new file mode 100644 index 00000000000..3962b4b0c11 --- /dev/null +++ b/regression/cprover/arrays/array_literal2.c @@ -0,0 +1,8 @@ +int array[] = {'a', 'b', 'c', 'd', 'e', 'f'}; + +int main() +{ + int i; + if(i >= 0 && i <= 5) + __CPROVER_assert(array[i] == 'a' + i, "property 1"); // passes +} diff --git a/regression/cprover/arrays/array_r_ok1.c b/regression/cprover/arrays/array_r_ok1.c new file mode 100644 index 00000000000..5d272647477 --- /dev/null +++ b/regression/cprover/arrays/array_r_ok1.c @@ -0,0 +1,7 @@ +int main() +{ + unsigned char array[10]; + __CPROVER_assert(__CPROVER_r_ok(array, 10), "property 1"); + unsigned char *array_ptr = array; + __CPROVER_assert(__CPROVER_r_ok(array_ptr, 10), "property 2"); +} diff --git a/regression/cprover/arrays/array_r_ok1.desc b/regression/cprover/arrays/array_r_ok1.desc new file mode 100644 index 00000000000..5b10508c6e0 --- /dev/null +++ b/regression/cprover/arrays/array_r_ok1.desc @@ -0,0 +1,8 @@ +CORE +array_r_ok1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/arrays/array_set1.c b/regression/cprover/arrays/array_set1.c new file mode 100644 index 00000000000..c0649de2af8 --- /dev/null +++ b/regression/cprover/arrays/array_set1.c @@ -0,0 +1,8 @@ +int array[10]; + +int main() +{ + __CPROVER_array_set(array, 123); + __CPROVER_assert(array[5l] == 123, "property 1"); // passes + return 0; +} diff --git a/regression/cprover/arrays/array_set1.desc b/regression/cprover/arrays/array_set1.desc new file mode 100644 index 00000000000..277d6d0f130 --- /dev/null +++ b/regression/cprover/arrays/array_set1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +array_set1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/arrays/iterate_over_array1.c b/regression/cprover/arrays/iterate_over_array1.c new file mode 100644 index 00000000000..49f729cb814 --- /dev/null +++ b/regression/cprover/arrays/iterate_over_array1.c @@ -0,0 +1,7 @@ +int array[10]; + +int main() +{ + for(__CPROVER_size_t i = 0; i < sizeof(array) / sizeof(int); i++) + array[i] = 0; +} diff --git a/regression/cprover/arrays/iterate_over_array1.desc b/regression/cprover/arrays/iterate_over_array1.desc new file mode 100644 index 00000000000..3a8abebd4c0 --- /dev/null +++ b/regression/cprover/arrays/iterate_over_array1.desc @@ -0,0 +1,7 @@ +CORE +iterate_over_array1.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.array_bounds\.1\] line \d+ array bounds in array\[.*i\]: SUCCESS$ +-- diff --git a/regression/cprover/arrays/iterate_over_array2.c b/regression/cprover/arrays/iterate_over_array2.c new file mode 100644 index 00000000000..26311bde2e5 --- /dev/null +++ b/regression/cprover/arrays/iterate_over_array2.c @@ -0,0 +1,31 @@ +#define size_t __CPROVER_size_t +#define false 0 +#define true 1 + +_Bool find_zero(const void *const array, const size_t array_len) +{ + const unsigned char *array_bytes = array; + + // iterate over array + for(size_t i = 0; i < array_len; ++i) + // clang-format off + __CPROVER_loop_invariant(i >= 0 && i <= array_len) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(array_bytes) == 0) + __CPROVER_loop_invariant(__CPROVER_r_ok(array_bytes, array_len)) + // clang-format on + { + if(array_bytes[i] == 0) + { + return true; + } + } + + return false; +} + +int main() +{ + unsigned char array[10]; + size_t array_len = 10; + find_zero(array, array_len); +} diff --git a/regression/cprover/arrays/iterate_over_array2.desc b/regression/cprover/arrays/iterate_over_array2.desc new file mode 100644 index 00000000000..815eff0673d --- /dev/null +++ b/regression/cprover/arrays/iterate_over_array2.desc @@ -0,0 +1,7 @@ +CORE +iterate_over_array2.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[find_zero\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +-- diff --git a/regression/cprover/aws-c-common/aws_array_eq_c_str.desc b/regression/cprover/aws-c-common/aws_array_eq_c_str.desc new file mode 100644 index 00000000000..21a384e6911 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_c_str.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_array_eq_c_str_contract.c +-I aws-c-common/include aws-c-common/source/byte_buf.c +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_array_eq_c_str_contract.c b/regression/cprover/aws-c-common/aws_array_eq_c_str_contract.c new file mode 100644 index 00000000000..f36d35396a4 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_c_str_contract.c @@ -0,0 +1,20 @@ +// Function: aws_array_eq_c_str +// Source: aws-c-common/source/byte_buf.c + +#include + +// bool aws_array_eq_c_str(const void *const array, const size_t array_len, const char *const c_str) + +int main() +{ + const void *array; + size_t array_len; + const char *c_str; + + __CPROVER_assume(__CPROVER_r_ok(array, array_len)); + __CPROVER_assume(__CPROVER_is_cstring(c_str)); + + aws_array_eq_c_str(array, array_len, c_str); + + return 0; +} diff --git a/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case.desc b/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case.desc new file mode 100644 index 00000000000..4edefe812c5 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_array_eq_c_str_ignore_case_contract.c +--safety aws-c-common/source/byte_buf.c -I aws-c-common/include +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case_contract.c b/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case_contract.c new file mode 100644 index 00000000000..f7d7333c0c5 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_c_str_ignore_case_contract.c @@ -0,0 +1,20 @@ +// Function: aws_array_eq_c_str_ignore_case +// Source: aws-c-common/source/byte_buf.c + +#include + +// bool aws_array_eq_c_str_ignore_case(const void *const array, const size_t array_len, const char *const c_str) + +int main() +{ + const void *array; + size_t array_len; + const char *c_str; + + __CPROVER_assume(__CPROVER_r_ok(array, array_len)); + __CPROVER_assume(__CPROVER_is_cstring(c_str)); + + aws_array_eq_c_str_ignore_case(array, array_len, c_str); + + return 0; +} diff --git a/regression/cprover/aws-c-common/aws_array_eq_ignore_case.desc b/regression/cprover/aws-c-common/aws_array_eq_ignore_case.desc new file mode 100644 index 00000000000..84bb334a63d --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_ignore_case.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_array_eq_ignore_case_contract.c +--safety aws-c-common/source/byte_buf.c -I aws-c-common/include +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_array_eq_ignore_case_contract.c b/regression/cprover/aws-c-common/aws_array_eq_ignore_case_contract.c new file mode 100644 index 00000000000..7bdc77e6ef0 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_array_eq_ignore_case_contract.c @@ -0,0 +1,23 @@ +// Function: aws_array_eq_ignore_case +// Source: aws-c-common/source/byte_buf.c + +#include + +// bool aws_array_eq_ignore_case( +// const void *const array_a, +// const size_t len_a, +// const void *const array_b, +// const size_t len_b) + +int main() +{ + const void *array_a, *array_b; + size_t len_a, len_b; + + __CPROVER_assume(__CPROVER_r_ok(array_a, len_a)); + __CPROVER_assume(__CPROVER_r_ok(array_b, len_b)); + + aws_array_eq_ignore_case(array_a, len_a, array_b, len_b); + + return 0; +} diff --git a/regression/cprover/aws-c-common/aws_hash_table_clear.desc b/regression/cprover/aws-c-common/aws_hash_table_clear.desc new file mode 100644 index 00000000000..9a4a0e86a31 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_clear.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_hash_table_clear_contract.c +-I aws-c-common/include aws-c-common/source/hash_table.c +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_hash_table_clear_contract.c b/regression/cprover/aws-c-common/aws_hash_table_clear_contract.c new file mode 100644 index 00000000000..ca146702064 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_clear_contract.c @@ -0,0 +1,15 @@ +// Function: aws_hash_table_clear +// Source: source/hash_table.c + +#include + +// void aws_hash_table_clear(struct aws_hash_table *map) + +int main() +{ + struct aws_hash_table *map; + + aws_hash_table_clear(map); + + return 0; +} diff --git a/regression/cprover/aws-c-common/aws_hash_table_eq.desc b/regression/cprover/aws-c-common/aws_hash_table_eq.desc new file mode 100644 index 00000000000..845de35ef6c --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_eq.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_hash_table_eq_contract.c +-I aws-c-common/include aws-c-common/source/hash_table.c +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_hash_table_eq_contract.c b/regression/cprover/aws-c-common/aws_hash_table_eq_contract.c new file mode 100644 index 00000000000..e6e555124c0 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_eq_contract.c @@ -0,0 +1,20 @@ +// Function: aws_hash_table_eq +// Source: source/hash_table.c + +#include + +// bool aws_hash_table_eq( +// const struct aws_hash_table *a, +// const struct aws_hash_table *b, +// aws_hash_callback_eq_fn *value_eq) + +int main() +{ + const struct aws_hash_table *a; + const struct aws_hash_table *b; + aws_hash_callback_eq_fn *value_eq; + + aws_hash_table_eq(a, b, value_eq); + + return 0; +} diff --git a/regression/cprover/aws-c-common/aws_hash_table_foreach.desc b/regression/cprover/aws-c-common/aws_hash_table_foreach.desc new file mode 100644 index 00000000000..0e07e0d4251 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_foreach.desc @@ -0,0 +1,6 @@ +KNOWNBUG +aws_hash_table_foreach_contract.c +-I aws-c-common/include aws-c-common/source/hash_table.c +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/aws_hash_table_foreach_contract.c b/regression/cprover/aws-c-common/aws_hash_table_foreach_contract.c new file mode 100644 index 00000000000..b8cedde6f94 --- /dev/null +++ b/regression/cprover/aws-c-common/aws_hash_table_foreach_contract.c @@ -0,0 +1,20 @@ +// Function: aws_hash_table_foreach +// Source: source/hash_table.c + +#include + +// int aws_hash_table_foreach( +// struct aws_hash_table *map, +// int (*callback)(void *context, struct aws_hash_element *pElement), +// void *context) + +int main() +{ + struct aws_hash_table *map; + int (*callback)(void *context, struct aws_hash_element *pElement); + void *context; + + aws_hash_table_foreach(map, callback, context); + + return 0; +} diff --git a/regression/cprover/aws-c-common/setup b/regression/cprover/aws-c-common/setup new file mode 100644 index 00000000000..9320fa116bf --- /dev/null +++ b/regression/cprover/aws-c-common/setup @@ -0,0 +1,6 @@ +#!/bin/sh + +git clone https://github.com/awslabs/aws-c-common -b v0.6.13 + +echo "/* nothing */" > aws-c-common/include/aws/common/config.h + diff --git a/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap.desc b/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap.desc new file mode 100644 index 00000000000..589f4153a64 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap.desc @@ -0,0 +1,6 @@ +CORE +aws_array_list_mem_swap_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap_contract.c b/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap_contract.c new file mode 100644 index 00000000000..d8cbf3238d3 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_array_list_mem_swap_contract.c @@ -0,0 +1,6 @@ +// Function: aws_array_list_mem_swap + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup.desc b/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup.desc new file mode 100644 index 00000000000..182b9b8ed07 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup.desc @@ -0,0 +1,6 @@ +CORE +aws_byte_buf_append_with_lookup_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup_contract.c b/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup_contract.c new file mode 100644 index 00000000000..798127b21d9 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_byte_buf_append_with_lookup_contract.c @@ -0,0 +1,6 @@ +// Function: aws_byte_buf_append_with_lookup + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case.desc b/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case.desc new file mode 100644 index 00000000000..81a0951c6ba --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case.desc @@ -0,0 +1,6 @@ +CORE +aws_hash_array_ignore_case_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case_contract.c b/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case_contract.c new file mode 100644 index 00000000000..399ff9cb87a --- /dev/null +++ b/regression/cprover/aws-c-common/todo/aws_hash_array_ignore_case_contract.c @@ -0,0 +1,6 @@ +// Function: aws_hash_array_ignore_case + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/hashlittle2.desc b/regression/cprover/aws-c-common/todo/hashlittle2.desc new file mode 100644 index 00000000000..79671d136a2 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/hashlittle2.desc @@ -0,0 +1,6 @@ +CORE +hashlittle2_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/hashlittle2_contract.c b/regression/cprover/aws-c-common/todo/hashlittle2_contract.c new file mode 100644 index 00000000000..779fc545905 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/hashlittle2_contract.c @@ -0,0 +1,6 @@ +// Function: hashlittle2 + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl.desc b/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl.desc new file mode 100644 index 00000000000..ba227d70208 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl.desc @@ -0,0 +1,6 @@ +CORE +memcpy_using_uint64_impl_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl_contract.c b/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl_contract.c new file mode 100644 index 00000000000..8fc5b9fdcab --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memcpy_using_uint64_impl_contract.c @@ -0,0 +1,6 @@ +// Function: memcpy_using_uint64_impl + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/memset_override_0_impl.desc b/regression/cprover/aws-c-common/todo/memset_override_0_impl.desc new file mode 100644 index 00000000000..05a8aad2bab --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memset_override_0_impl.desc @@ -0,0 +1,6 @@ +CORE +memset_override_0_impl_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/memset_override_0_impl_contract.c b/regression/cprover/aws-c-common/todo/memset_override_0_impl_contract.c new file mode 100644 index 00000000000..ef92559e5f6 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memset_override_0_impl_contract.c @@ -0,0 +1,6 @@ +// Function: memset_override_0_impl + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/memset_using_uint64_impl.desc b/regression/cprover/aws-c-common/todo/memset_using_uint64_impl.desc new file mode 100644 index 00000000000..20edd110665 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memset_using_uint64_impl.desc @@ -0,0 +1,6 @@ +CORE +memset_using_uint64_impl_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/memset_using_uint64_impl_contract.c b/regression/cprover/aws-c-common/todo/memset_using_uint64_impl_contract.c new file mode 100644 index 00000000000..f63c3b23f0b --- /dev/null +++ b/regression/cprover/aws-c-common/todo/memset_using_uint64_impl_contract.c @@ -0,0 +1,6 @@ +// Function: memset_using_uint64_impl + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/s_find_entry1.desc b/regression/cprover/aws-c-common/todo/s_find_entry1.desc new file mode 100644 index 00000000000..22def401755 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_find_entry1.desc @@ -0,0 +1,6 @@ +CORE +s_find_entry1_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/s_find_entry1_contract.c b/regression/cprover/aws-c-common/todo/s_find_entry1_contract.c new file mode 100644 index 00000000000..9326144f4cf --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_find_entry1_contract.c @@ -0,0 +1,6 @@ +// Function: s_find_entry1 + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/s_get_next_element.desc b/regression/cprover/aws-c-common/todo/s_get_next_element.desc new file mode 100644 index 00000000000..4ac02e41e8c --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_get_next_element.desc @@ -0,0 +1,6 @@ +CORE +s_get_next_element_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/s_get_next_element_contract.c b/regression/cprover/aws-c-common/todo/s_get_next_element_contract.c new file mode 100644 index 00000000000..cc25d1a917f --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_get_next_element_contract.c @@ -0,0 +1,6 @@ +// Function: s_get_next_element + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/s_sift_down.desc b/regression/cprover/aws-c-common/todo/s_sift_down.desc new file mode 100644 index 00000000000..a27386b65ea --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_sift_down.desc @@ -0,0 +1,6 @@ +CORE +s_sift_down_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/s_sift_down_contract.c b/regression/cprover/aws-c-common/todo/s_sift_down_contract.c new file mode 100644 index 00000000000..fba47a319da --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_sift_down_contract.c @@ -0,0 +1,6 @@ +// Function: s_sift_down + +int main() +{ + return 0; +} diff --git a/regression/cprover/aws-c-common/todo/s_sift_up.desc b/regression/cprover/aws-c-common/todo/s_sift_up.desc new file mode 100644 index 00000000000..84b60a6ac04 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_sift_up.desc @@ -0,0 +1,6 @@ +CORE +s_sift_up_contract.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/aws-c-common/todo/s_sift_up_contract.c b/regression/cprover/aws-c-common/todo/s_sift_up_contract.c new file mode 100644 index 00000000000..d15ecf63be7 --- /dev/null +++ b/regression/cprover/aws-c-common/todo/s_sift_up_contract.c @@ -0,0 +1,6 @@ +// Function: s_sift_up + +int main() +{ + return 0; +} diff --git a/regression/cprover/basic/assume1.c b/regression/cprover/basic/assume1.c new file mode 100644 index 00000000000..4a61fb2ace0 --- /dev/null +++ b/regression/cprover/basic/assume1.c @@ -0,0 +1,8 @@ +int main() +{ + int x; + + __CPROVER_assume(x >= 10); + __CPROVER_assert(x >= 5, "property 1"); // should pass + __CPROVER_assert(x >= 15, "property 2"); // should fail +} diff --git a/regression/cprover/basic/assume1.desc b/regression/cprover/basic/assume1.desc new file mode 100644 index 00000000000..9ce490abc7c --- /dev/null +++ b/regression/cprover/basic/assume1.desc @@ -0,0 +1,14 @@ +CORE +assume1.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^ \(\d+\) ∀ ς : state \. initial_state\(ς\) ⇒ SInitial\(ς\)$ +^ \(\d+\) S0 = SInitial$ +^ \(\d+\) S1 = S0$ +^\(\d+\) ∀ ς : state \. \(S20\(ς\) ∧ ς\(❝main::1::x❞\) ≥ 10\) ⇒ S21\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ \(ς\(❝main::1::x❞\) ≥ 5\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(❝main::1::x❞\) ≥ 15\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/basic/basic1.c b/regression/cprover/basic/basic1.c new file mode 100644 index 00000000000..d79811088ce --- /dev/null +++ b/regression/cprover/basic/basic1.c @@ -0,0 +1,8 @@ +int x; + +int main() +{ + x = 1; + __CPROVER_assert(x == 1, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/basic1.desc b/regression/cprover/basic/basic1.desc new file mode 100644 index 00000000000..11a7b0ad24c --- /dev/null +++ b/regression/cprover/basic/basic1.desc @@ -0,0 +1,12 @@ +CORE +basic1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^ \(\d+\) ∀ ς : state \. initial_state\(ς\) ⇒ SInitial\(ς\)$ +^ \(\d+\) S0 = SInitial$ +^\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝x❞:=0\]\)$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝x❞:=1\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ \(ς\(❝x❞\) = 1\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/basic/basic2.c b/regression/cprover/basic/basic2.c new file mode 100644 index 00000000000..8becf0b4d00 --- /dev/null +++ b/regression/cprover/basic/basic2.c @@ -0,0 +1,9 @@ +int x; + +int main() +{ + x = 1; + ; + __CPROVER_assert(x == 1, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/basic2.desc b/regression/cprover/basic/basic2.desc new file mode 100644 index 00000000000..297a94c2fc5 --- /dev/null +++ b/regression/cprover/basic/basic2.desc @@ -0,0 +1,13 @@ +CORE +basic2.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝x❞:=0\]\)$ +^\(\d+\) S19 = S18$ +^\(\d+\) S20 = S19$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝x❞:=1\]\)$ +^\(\d+\) S22 = S21$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(❝x❞\) = 1\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/basic/basic3.c b/regression/cprover/basic/basic3.c new file mode 100644 index 00000000000..91731f94e78 --- /dev/null +++ b/regression/cprover/basic/basic3.c @@ -0,0 +1,9 @@ +int x, y; + +int main() +{ + x = 1; + y = 2; + __CPROVER_assert(x == 1, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/basic3.desc b/regression/cprover/basic/basic3.desc new file mode 100644 index 00000000000..eb2127fbc7b --- /dev/null +++ b/regression/cprover/basic/basic3.desc @@ -0,0 +1,12 @@ +CORE +basic3.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝x❞:=0\]\) +\(\d+\) ∀ ς : state \. S18\(ς\) ⇒ S19\(ς\[❝y❞:=0\]\) +\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝x❞:=1\]\) +\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝y❞:=2\]\) +\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(❝x❞\) = 1\) +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/basic/basic4.c b/regression/cprover/basic/basic4.c new file mode 100644 index 00000000000..555b0ac5eab --- /dev/null +++ b/regression/cprover/basic/basic4.c @@ -0,0 +1,9 @@ +int x; + +int main() +{ + x = 1; + x = 2; + __CPROVER_assert(x == 1, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/basic4.desc b/regression/cprover/basic/basic4.desc new file mode 100644 index 00000000000..1aaf8f0de0e --- /dev/null +++ b/regression/cprover/basic/basic4.desc @@ -0,0 +1,11 @@ +CORE +basic4.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝x❞:=0\]\)$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝x❞:=1\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝x❞:=2\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(❝x❞\) = 1\)$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/basic/basic5.c b/regression/cprover/basic/basic5.c new file mode 100644 index 00000000000..c5d2ba0200c --- /dev/null +++ b/regression/cprover/basic/basic5.c @@ -0,0 +1,11 @@ +int main() +{ + int x; + __CPROVER_bool c1 = (x >= 10); + __CPROVER_bool c2 = (x >= 5); + // clang-format off + __CPROVER_assert(c1 ==> c2, "property 1"); // passes + __CPROVER_assert(c2 ==> c1, "property 2"); // fails + // clang-format on + return 0; +} diff --git a/regression/cprover/basic/basic5.desc b/regression/cprover/basic/basic5.desc new file mode 100644 index 00000000000..d0f62ff8464 --- /dev/null +++ b/regression/cprover/basic/basic5.desc @@ -0,0 +1,14 @@ +CORE +basic5.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝main::1::c1❞:=ς\(❝main::1::x❞\) ≥ 10\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(enter_scope_state\(ς, ❝main::1::c2❞\)\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝main::1::c2❞:=ς\(❝main::1::x❞\) ≥ 5\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(ς\(❝main::1::c1❞\) ⇒ ς\(❝main::1::c2❞\)\)$ +^\(\d+\) S25 = S24$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(❝main::1::c2❞\) ⇒ ς\(❝main::1::c1❞\)\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/basic/basic6.c b/regression/cprover/basic/basic6.c new file mode 100644 index 00000000000..d17f7f4e3f2 --- /dev/null +++ b/regression/cprover/basic/basic6.c @@ -0,0 +1,12 @@ +int x; + +int main() +{ + x = 1; + x++; + x++; + x++; + x++; + __CPROVER_assert(x == 5, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/basic6.desc b/regression/cprover/basic/basic6.desc new file mode 100644 index 00000000000..7bbc6dd7c54 --- /dev/null +++ b/regression/cprover/basic/basic6.desc @@ -0,0 +1,13 @@ +CORE +basic6.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(23\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝x❞:=1\]\)$ +^\(24\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝x❞:=ς\(❝x❞\) \+ 1\]\)$ +^\(25\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝x❞:=ς\(❝x❞\) \+ 1\]\)$ +^\(26\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝x❞:=ς\(❝x❞\) \+ 1\]\)$ +^\(27\) ∀ ς : state \. S24\(ς\) ⇒ S25\(ς\[❝x❞:=ς\(❝x❞\) \+ 1\]\)$ +^\(28\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(❝x❞\) = 5\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/basic/extern1.c b/regression/cprover/basic/extern1.c new file mode 100644 index 00000000000..f0b6ac7ac39 --- /dev/null +++ b/regression/cprover/basic/extern1.c @@ -0,0 +1,13 @@ +struct S +{ + int x, y; +}; + +extern struct S some_struct; + +int main() +{ + // should fail + __CPROVER_assert(some_struct.x == some_struct.y, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/extern1.desc b/regression/cprover/basic/extern1.desc new file mode 100644 index 00000000000..185f9bed398 --- /dev/null +++ b/regression/cprover/basic/extern1.desc @@ -0,0 +1,7 @@ +CORE +extern1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/basic/no-assertions1.c b/regression/cprover/basic/no-assertions1.c new file mode 100644 index 00000000000..603b9efb645 --- /dev/null +++ b/regression/cprover/basic/no-assertions1.c @@ -0,0 +1,5 @@ +int main() +{ + __CPROVER_assert(0, "property 1"); + return 0; +} diff --git a/regression/cprover/basic/no-assertions1.desc b/regression/cprover/basic/no-assertions1.desc new file mode 100644 index 00000000000..77850c60273 --- /dev/null +++ b/regression/cprover/basic/no-assertions1.desc @@ -0,0 +1,7 @@ +CORE +basic1.c +--no-assertions +^EXIT=0$ +^SIGNAL=0$ +^There are no properties to show\.$ +-- diff --git a/regression/cprover/basic/nondet1.c b/regression/cprover/basic/nondet1.c new file mode 100644 index 00000000000..646308c12c2 --- /dev/null +++ b/regression/cprover/basic/nondet1.c @@ -0,0 +1,14 @@ +int nondet_int(); + +int main() +{ + int x, y; + + x = nondet_int(); + __CPROVER_assert(x == 20, "property 1"); // fails + + y = nondet_int(); + __CPROVER_assert(x == y, "property 2"); // fails + + return 0; +} diff --git a/regression/cprover/basic/nondet1.desc b/regression/cprover/basic/nondet1.desc new file mode 100644 index 00000000000..f902da15fbd --- /dev/null +++ b/regression/cprover/basic/nondet1.desc @@ -0,0 +1,15 @@ +CORE +nondet1.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state, nondet::S22-0 : signedbv\[32\] \. S21\(ς\) ⇒ S22\(ς\[❝main::1::x❞:=nondet::S22-0\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(❝main::1::x❞\) = 20\)$ +^\(\d+\) S23 = S22$ +^\(\d+\) ∀ ς : state, nondet::S24-0 : signedbv\[32\] \. S23\(ς\) ⇒ S24\(ς\[❝main::1::y❞:=nondet::S24-0\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(ς\(❝main::1::x❞\) = ς\(❝main::1::y❞\)\)$ +^\(\d+\) S25 = S24$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ S26\(ς\[❝return'❞:=0\]\)$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/branching/branching1.c b/regression/cprover/branching/branching1.c new file mode 100644 index 00000000000..68f1d0c9d5e --- /dev/null +++ b/regression/cprover/branching/branching1.c @@ -0,0 +1,14 @@ +int main() +{ + int x; + if(x >= 5) + { + __CPROVER_assert(x >= 5, "property 1"); // should pass + __CPROVER_assert(0, "property 2"); // should fail + } + else + { + __CPROVER_assert(x < 5, "property 3"); // should pass + __CPROVER_assert(0, "property 4"); // should fail + } +} diff --git a/regression/cprover/branching/branching1.desc b/regression/cprover/branching/branching1.desc new file mode 100644 index 00000000000..ff64a86eb44 --- /dev/null +++ b/regression/cprover/branching/branching1.desc @@ -0,0 +1,24 @@ +CORE +branching1.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. \(S20\(ς\) ∧ ¬\(ς\(❝main::1::x❞\) ≥ 5\)\) ⇒ S21T\(\ς\)$ +^\(\d+\) ∀ ς : state \. \(S20\(ς\) ∧ ς\(❝main::1::x❞\) ≥ 5\) ⇒ S21\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ \(ς\(❝main::1::x❞\) ≥ 5\)$ +^\(\d+\) S22 = S21$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(0 ≠ 0\)$ +^\(\d+\) S23 = S22$ +^\(\d+\) S24 = S23$ +^\(\d+\) ∀ ς : state \. S21T\(ς\) ⇒ \(ς\(❝main::1::x❞\) < 5\)$ +^\(\d+\) S25 = S21T$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(0 ≠ 0\)$ +^\(\d+\) S26 = S25$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S27in\(ς\)$ +^\(\d+\) ∀ ς : state \. S26\(ς\) ⇒ S27in\(ς\)$ +^\(\d+\) S27 = S27in$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +^\[main\.assertion\.4\] line \d+ property 4: REFUTED$ +-- diff --git a/regression/cprover/branching/branching2.c b/regression/cprover/branching/branching2.c new file mode 100644 index 00000000000..6d86e28233e --- /dev/null +++ b/regression/cprover/branching/branching2.c @@ -0,0 +1,14 @@ +int main() +{ + int a, b; + + if(a >= 5) + b = 1; + else + b = 0; + + __CPROVER_assert(b == (a >= 5), "property 1"); // should pass + __CPROVER_assert(b != (a >= 5), "property 2"); // should fail + + return 0; +} diff --git a/regression/cprover/branching/branching2.desc b/regression/cprover/branching/branching2.desc new file mode 100644 index 00000000000..53d98c6674a --- /dev/null +++ b/regression/cprover/branching/branching2.desc @@ -0,0 +1,16 @@ +CORE +branching2.c +--text --solve --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. \(S21\(ς\) ∧ ¬\(ς\(❝main::1::a❞\) ≥ 5\)\) ⇒ S22T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S21\(ς\) ∧ ς\(❝main::1::a❞\) ≥ 5\) ⇒ S22\(ς\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::b❞:=1\]\)$ +^\(\d+\) S24 = S23$ +^\(\d+\) ∀ ς : state \. S22T\(ς\) ⇒ S25\(ς\[❝main::1::b❞:=0\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S26in\(ς\)$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ S26in\(ς\)$ +^\(\d+\) S26 = S26in$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_assigns1.c b/regression/cprover/contracts/check_assigns1.c new file mode 100644 index 00000000000..a275c74608c --- /dev/null +++ b/regression/cprover/contracts/check_assigns1.c @@ -0,0 +1,36 @@ +int global; + +void my_function1(void) + // clang-format off + __CPROVER_assigns(global) // passes +// clang-format on +{ + global = 10; +} + +void my_function2(int parameter) + // clang-format off + __CPROVER_assigns() // fails + __CPROVER_ensures(1) +// clang-format on +{ + global = 10; +} + +void my_function3(int *pointer) + // clang-format off + __CPROVER_requires(__CPROVER_w_ok(pointer)) + __CPROVER_assigns(*pointer) // passes +// clang-format on +{ + *pointer = 10; +} + +void my_function4(void) + // clang-format off + __CPROVER_assigns() // passes +// clang-format on +{ + int local; + local = 10; +} diff --git a/regression/cprover/contracts/check_assigns1.desc b/regression/cprover/contracts/check_assigns1.desc new file mode 100644 index 00000000000..717f3da5e2e --- /dev/null +++ b/regression/cprover/contracts/check_assigns1.desc @@ -0,0 +1,9 @@ +CORE +check_assigns1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: REFUTED +^\[my_function3\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +-- diff --git a/regression/cprover/contracts/check_assigns2.c b/regression/cprover/contracts/check_assigns2.c new file mode 100644 index 00000000000..06939fc06ec --- /dev/null +++ b/regression/cprover/contracts/check_assigns2.c @@ -0,0 +1,20 @@ +struct some_struct +{ + int field; +}; + +void my_function1(int parameter) __CPROVER_assigns() // passes + __CPROVER_ensures(1) +{ + parameter = 1; +} + +void my_function2(void) __CPROVER_assigns() // passes + __CPROVER_ensures(1) +{ + struct some_struct s; + s.field = 1; + + int array[123]; + array[1] = 2; +} diff --git a/regression/cprover/contracts/check_assigns2.desc b/regression/cprover/contracts/check_assigns2.desc new file mode 100644 index 00000000000..df7ee50ef6b --- /dev/null +++ b/regression/cprover/contracts/check_assigns2.desc @@ -0,0 +1,6 @@ +CORE +check_assigns2.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/contracts/check_assigns3.c b/regression/cprover/contracts/check_assigns3.c new file mode 100644 index 00000000000..85a82f4c585 --- /dev/null +++ b/regression/cprover/contracts/check_assigns3.c @@ -0,0 +1,26 @@ +void my_function1(int *parameter) + // clang-format off + __CPROVER_requires(__CPROVER_w_ok(parameter)) + __CPROVER_assigns(*parameter) // passes + // clang-fromat on +{ + *parameter = 10; +} + +void my_function2(int *parameter) + // clang-format off + __CPROVER_requires(__CPROVER_w_ok(parameter)) + __CPROVER_assigns() // fails + // clang-fromat on +{ + *parameter = 10; +} + +void my_function3(int *parameter) + // clang-format off + __CPROVER_requires(__CPROVER_w_ok(parameter)) + // implicit assigns clause fails + // clang-fromat on +{ + *parameter = 10; +} diff --git a/regression/cprover/contracts/check_assigns3.desc b/regression/cprover/contracts/check_assigns3.desc new file mode 100644 index 00000000000..a58da480570 --- /dev/null +++ b/regression/cprover/contracts/check_assigns3.desc @@ -0,0 +1,9 @@ +CORE +check_assigns3.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: REFUTED +^\[my_function3\.assigns\.1\] line \d+ assigns clause: REFUTED +-- diff --git a/regression/cprover/contracts/check_assigns4.c b/regression/cprover/contracts/check_assigns4.c new file mode 100644 index 00000000000..cb955ef6a7c --- /dev/null +++ b/regression/cprover/contracts/check_assigns4.c @@ -0,0 +1,37 @@ +struct S +{ + int x, y; +} global; + +void my_function1(void) + // clang-format off + __CPROVER_assigns(global) // passes +// clang-format on +{ + global.x = 10; + global.y = 10; +} + +void my_function2(void) + // clang-format off + __CPROVER_assigns(global.x) // passes +// clang-format on +{ + global.x = 10; +} + +void my_function3(void) + // clang-format off + __CPROVER_assigns(global.y) // passes +// clang-format on +{ + global.y = 10; +} + +void my_function4(void) + // clang-format off + __CPROVER_assigns(global.y) // fails +// clang-format on +{ + global.x = 10; +} diff --git a/regression/cprover/contracts/check_assigns4.desc b/regression/cprover/contracts/check_assigns4.desc new file mode 100644 index 00000000000..60b3c394c59 --- /dev/null +++ b/regression/cprover/contracts/check_assigns4.desc @@ -0,0 +1,11 @@ +CORE +check_assigns4.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function1\.assigns\.2\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function3\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function4\.assigns\.1\] line \d+ assigns clause: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_assigns5.c b/regression/cprover/contracts/check_assigns5.c new file mode 100644 index 00000000000..847b8e61986 --- /dev/null +++ b/regression/cprover/contracts/check_assigns5.c @@ -0,0 +1,29 @@ +struct S +{ + int x, y; +}; + +void my_function1(struct S *s) __CPROVER_requires(__CPROVER_rw_ok(s)) + __CPROVER_assigns(*s) // passes +{ + s->x = 10; + s->y = 10; +} + +void my_function2(struct S *s) __CPROVER_requires(__CPROVER_rw_ok(s)) + __CPROVER_assigns(s->x) // passes +{ + s->x = 10; +} + +void my_function3(struct S *s) __CPROVER_requires(__CPROVER_rw_ok(s)) + __CPROVER_assigns(s->y) // passes +{ + s->y = 10; +} + +void my_function4(struct S *s) __CPROVER_requires(__CPROVER_rw_ok(s)) + __CPROVER_assigns(s->y) // fails +{ + s->x = 10; +} diff --git a/regression/cprover/contracts/check_assigns5.desc b/regression/cprover/contracts/check_assigns5.desc new file mode 100644 index 00000000000..263f67b98b8 --- /dev/null +++ b/regression/cprover/contracts/check_assigns5.desc @@ -0,0 +1,11 @@ +CORE +check_assigns5.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function1\.assigns\.2\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function3\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function4\.assigns\.1\] line \d+ assigns clause: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_assigns6.c b/regression/cprover/contracts/check_assigns6.c new file mode 100644 index 00000000000..f3c076709f5 --- /dev/null +++ b/regression/cprover/contracts/check_assigns6.c @@ -0,0 +1,19 @@ +int global; +_Bool flag; + +void my_function1() + // clang-format off + __CPROVER_assigns(flag: global) // passes +// clang-format on +{ + if(flag) + global = 10; +} + +void my_function2() + // clang-format off + __CPROVER_assigns(flag: global) // fails +// clang-format on +{ + global = 10; +} diff --git a/regression/cprover/contracts/check_assigns6.desc b/regression/cprover/contracts/check_assigns6.desc new file mode 100644 index 00000000000..5541bf82c1a --- /dev/null +++ b/regression/cprover/contracts/check_assigns6.desc @@ -0,0 +1,8 @@ +CORE +check_assigns6.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_postcondition1.c b/regression/cprover/contracts/check_postcondition1.c new file mode 100644 index 00000000000..8b800c00c3e --- /dev/null +++ b/regression/cprover/contracts/check_postcondition1.c @@ -0,0 +1,16 @@ +int global; + +void my_function1(int parameter) + // clang-format off + __CPROVER_ensures(global >= 0) // passes +// clang-format on +{ + global = 10; +} + +void my_function2(int parameter) + // clang-format off + __CPROVER_ensures(global >= 0) // fails +// clang-format on +{ +} diff --git a/regression/cprover/contracts/check_postcondition1.desc b/regression/cprover/contracts/check_postcondition1.desc new file mode 100644 index 00000000000..7a7b26d4591 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition1.desc @@ -0,0 +1,8 @@ +CORE +check_postcondition1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[my_function2\.postcondition\.1\] line \d+ postcondition: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_postcondition2.c b/regression/cprover/contracts/check_postcondition2.c new file mode 100644 index 00000000000..a40b34fb77d --- /dev/null +++ b/regression/cprover/contracts/check_postcondition2.c @@ -0,0 +1,21 @@ +int global; + +void my_function1(int parameter) + // clang-format off + __CPROVER_ensures(global == parameter) // passes + __CPROVER_assigns(global) +// clang-format on +{ + parameter++; + global = parameter; +} + +void my_function2(int parameter) + // clang-format off + __CPROVER_ensures(global == parameter) // fails + __CPROVER_assigns(global) +// clang-format on +{ + global = parameter; + parameter++; +} diff --git a/regression/cprover/contracts/check_postcondition2.desc b/regression/cprover/contracts/check_postcondition2.desc new file mode 100644 index 00000000000..51d52105b18 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition2.desc @@ -0,0 +1,8 @@ +CORE +check_postcondition2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[my_function2\.postcondition\.1\] line \d+ postcondition: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_postcondition3.c b/regression/cprover/contracts/check_postcondition3.c new file mode 100644 index 00000000000..79d863a8d9a --- /dev/null +++ b/regression/cprover/contracts/check_postcondition3.c @@ -0,0 +1,17 @@ +int global; + +void my_function1(int parameter) + __CPROVER_ensures(global == __CPROVER_old(parameter)) // passes + __CPROVER_assigns(global) +{ + global = parameter; + parameter++; +} + +void my_function2(int parameter) + __CPROVER_ensures(global == __CPROVER_old(parameter)) // fails + __CPROVER_assigns(global) +{ + parameter++; + global = parameter; +} diff --git a/regression/cprover/contracts/check_postcondition3.desc b/regression/cprover/contracts/check_postcondition3.desc new file mode 100644 index 00000000000..b8a253af23e --- /dev/null +++ b/regression/cprover/contracts/check_postcondition3.desc @@ -0,0 +1,10 @@ +CORE +check_postcondition3.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function1\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[my_function2\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[my_function2\.postcondition\.1\] line \d+ postcondition: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_postcondition4.c b/regression/cprover/contracts/check_postcondition4.c new file mode 100644 index 00000000000..42a661f138c --- /dev/null +++ b/regression/cprover/contracts/check_postcondition4.c @@ -0,0 +1,12 @@ +int x, y; + +void my_function() + // clang-format off + __CPROVER_ensures(x == 10) // passes + __CPROVER_ensures(y == 20) // fails + __CPROVER_assigns(x, y) +// clang-format on +{ + x = 10; + y = 10; +} diff --git a/regression/cprover/contracts/check_postcondition4.desc b/regression/cprover/contracts/check_postcondition4.desc new file mode 100644 index 00000000000..fb23bd58e68 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition4.desc @@ -0,0 +1,8 @@ +CORE +check_postcondition4.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function\.postcondition\.2\] line \d+ postcondition x == 10: SUCCESS$ +^\[my_function\.postcondition\.1\] line \d+ postcondition y == 20: REFUTED$ +-- diff --git a/regression/cprover/contracts/check_postcondition5.c b/regression/cprover/contracts/check_postcondition5.c new file mode 100644 index 00000000000..d0aeb2329c2 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition5.c @@ -0,0 +1,8 @@ +void my_function(int *in, int *out) __CPROVER_requires(__CPROVER_rw_ok(in)) + __CPROVER_requires(__CPROVER_rw_ok(out)) + __CPROVER_requires(!__CPROVER_same_object(in, out)) + __CPROVER_assigns(*in, *out) __CPROVER_ensures(*out == __CPROVER_old(*in)) +{ + *out = *in; + *in = 0; // overwrite +} diff --git a/regression/cprover/contracts/check_postcondition5.desc b/regression/cprover/contracts/check_postcondition5.desc new file mode 100644 index 00000000000..c35a47d73c9 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition5.desc @@ -0,0 +1,7 @@ +CORE +check_postcondition5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[my_function\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +-- diff --git a/regression/cprover/contracts/check_postcondition6.c b/regression/cprover/contracts/check_postcondition6.c new file mode 100644 index 00000000000..4593e5ac1b9 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition6.c @@ -0,0 +1,10 @@ +int my_function(int x) + __CPROVER_ensures(__CPROVER_return_value == __CPROVER_old(x)) +{ + for(int i = 0; i < 100; i++) + { + // does not modify x + } + + return x; +} diff --git a/regression/cprover/contracts/check_postcondition6.desc b/regression/cprover/contracts/check_postcondition6.desc new file mode 100644 index 00000000000..fc11b9f4e75 --- /dev/null +++ b/regression/cprover/contracts/check_postcondition6.desc @@ -0,0 +1,7 @@ +KNOWNBUG +check_postcondition6.c + +^EXIT=0$ +^SIGNAL=0$ +^\[my_function\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +-- diff --git a/regression/cprover/contracts/check_precondition1.c b/regression/cprover/contracts/check_precondition1.c new file mode 100644 index 00000000000..e2baa844323 --- /dev/null +++ b/regression/cprover/contracts/check_precondition1.c @@ -0,0 +1,13 @@ +void my_function(int parameter) + // clang-format off + __CPROVER_requires(parameter >= 10) +// clang-format on +{ +} + +int main() +{ + my_function(-123); // fails + my_function(123); // passes + return 0; +} diff --git a/regression/cprover/contracts/check_precondition1.desc b/regression/cprover/contracts/check_precondition1.desc new file mode 100644 index 00000000000..dc79c98981e --- /dev/null +++ b/regression/cprover/contracts/check_precondition1.desc @@ -0,0 +1,8 @@ +CORE +check_precondition1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.precondition\.1\] line \d+ my_function precondition .*: REFUTED$ +^\[main\.precondition\.2\] line \d+ my_function precondition .*: SUCCESS$ +-- diff --git a/regression/cprover/contracts/check_precondition2.c b/regression/cprover/contracts/check_precondition2.c new file mode 100644 index 00000000000..c919dd08be4 --- /dev/null +++ b/regression/cprover/contracts/check_precondition2.c @@ -0,0 +1,15 @@ +void callee(int parameter) + // clang-format off + __CPROVER_requires(parameter >= 10) +// clang-format on +{ +} + +void caller(int some_value) + // clang-format off + __CPROVER_requires(1) +// clang-format on +{ + if(some_value >= 10) + callee(some_value); // precondition passes +} diff --git a/regression/cprover/contracts/check_precondition2.desc b/regression/cprover/contracts/check_precondition2.desc new file mode 100644 index 00000000000..1dc91b5e22b --- /dev/null +++ b/regression/cprover/contracts/check_precondition2.desc @@ -0,0 +1,7 @@ +CORE +check_precondition2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[caller\.precondition\.1\] line \d+ callee precondition parameter >= 10: SUCCESS$ +-- diff --git a/regression/cprover/contracts/check_return_value1.c b/regression/cprover/contracts/check_return_value1.c new file mode 100644 index 00000000000..c5047444ac0 --- /dev/null +++ b/regression/cprover/contracts/check_return_value1.c @@ -0,0 +1,15 @@ +int my_function1(void) + // clang-format off + __CPROVER_ensures(__CPROVER_return_value >= 0) // passes +// clang-format on +{ + return 10; +} + +int my_function2(void) + // clang-format off + __CPROVER_ensures(__CPROVER_return_value >= 0) // fails +// clang-format on +{ + return -10; +} diff --git a/regression/cprover/contracts/check_return_value1.desc b/regression/cprover/contracts/check_return_value1.desc new file mode 100644 index 00000000000..e7252fe054f --- /dev/null +++ b/regression/cprover/contracts/check_return_value1.desc @@ -0,0 +1,8 @@ +CORE +check_return_value1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function1\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[my_function2\.postcondition\.1\] line \d+ postcondition: REFUTED$ +-- diff --git a/regression/cprover/contracts/configured_contract1.c b/regression/cprover/contracts/configured_contract1.c new file mode 100644 index 00000000000..efe45304779 --- /dev/null +++ b/regression/cprover/contracts/configured_contract1.c @@ -0,0 +1,12 @@ +int global; + +// no contract given at all +void my_function1(int parameter) +{ + global = 10; +} + +// no contact given at all +void my_function2(int parameter) +{ +} diff --git a/regression/cprover/contracts/configured_contract1.desc b/regression/cprover/contracts/configured_contract1.desc new file mode 100644 index 00000000000..0d32e00d427 --- /dev/null +++ b/regression/cprover/contracts/configured_contract1.desc @@ -0,0 +1,8 @@ +KNOWNBUG +configured_contract1.yaml + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function my_function1 is safe / postcondition: SUCCESS$ +^program is safe / function my_function2 is safe / postcondition: REFUTED$ +-- diff --git a/regression/cprover/contracts/configured_contract2.c b/regression/cprover/contracts/configured_contract2.c new file mode 100644 index 00000000000..b6c2880e774 --- /dev/null +++ b/regression/cprover/contracts/configured_contract2.c @@ -0,0 +1,9 @@ +int global; + +#define MY_MAGIC_VALUE 10 + +// no contract given at all +void my_function1(int parameter) +{ + global = MY_MAGIC_VALUE; +} diff --git a/regression/cprover/contracts/configured_contract2.desc b/regression/cprover/contracts/configured_contract2.desc new file mode 100644 index 00000000000..892e11d3604 --- /dev/null +++ b/regression/cprover/contracts/configured_contract2.desc @@ -0,0 +1,7 @@ +KNOWNBUG +configured_contract2.yaml + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function my_function1 is safe / postcondition: SUCCESS$ +-- diff --git a/regression/cprover/contracts/integer_range1.c b/regression/cprover/contracts/integer_range1.c new file mode 100644 index 00000000000..27be359e147 --- /dev/null +++ b/regression/cprover/contracts/integer_range1.c @@ -0,0 +1,25 @@ +typedef __CPROVER_size_t size_t; + +void returns_a_range(const char *base, size_t size, size_t *pos) + // clang-format off + __CPROVER_requires(__CPROVER_r_ok(base, size)) + __CPROVER_requires(__CPROVER_rw_ok(pos)) + __CPROVER_requires(*pos <= size) + __CPROVER_assigns(*pos) + __CPROVER_ensures(*pos <= size) +// clang-format on +{ + size_t p = *pos; + + // skip a pair of double quotes + if(p < size && base[p] == '"') + { + p++; + + if(p < size && base[p] == '"') + { + p++; + *pos = p; + } + } +} diff --git a/regression/cprover/contracts/integer_range1.desc b/regression/cprover/contracts/integer_range1.desc new file mode 100644 index 00000000000..24e0c040b10 --- /dev/null +++ b/regression/cprover/contracts/integer_range1.desc @@ -0,0 +1,6 @@ +CORE +integer_range1.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/contracts/integer_range2.c b/regression/cprover/contracts/integer_range2.c new file mode 100644 index 00000000000..63d5fb22ac6 --- /dev/null +++ b/regression/cprover/contracts/integer_range2.c @@ -0,0 +1,24 @@ +typedef __CPROVER_size_t size_t; + +void returns_a_range(const char *base, size_t size, size_t pos, size_t *length) + // clang-format off + __CPROVER_requires(__CPROVER_r_ok(base, size)) + __CPROVER_requires(pos <= size) + __CPROVER_requires(__CPROVER_rw_ok(length)) + __CPROVER_assigns(*length) + __CPROVER_ensures(__CPROVER_old(pos) + *length <= size) +// clang-format on +{ + // skip a pair of double quotes + size_t start = pos; + + if(pos < size && base[pos] == '"') + { + pos++; + + if(pos < size && base[pos] == '"') + pos++; + } + + *length = pos - start; +} diff --git a/regression/cprover/contracts/integer_range2.desc b/regression/cprover/contracts/integer_range2.desc new file mode 100644 index 00000000000..153353437ab --- /dev/null +++ b/regression/cprover/contracts/integer_range2.desc @@ -0,0 +1,6 @@ +CORE +integer_range2.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/contracts/integer_range3.c b/regression/cprover/contracts/integer_range3.c new file mode 100644 index 00000000000..7442de8c612 --- /dev/null +++ b/regression/cprover/contracts/integer_range3.c @@ -0,0 +1,28 @@ +typedef __CPROVER_size_t size_t; + +_Bool returns_a_range(const char *base, size_t size, size_t pos, size_t *length) + // clang-format off + __CPROVER_requires(__CPROVER_r_ok(base, size)) __CPROVER_requires(pos <= size) + __CPROVER_requires(__CPROVER_rw_ok(length)) __CPROVER_assigns(*length) + __CPROVER_ensures( + __CPROVER_return_value ==> __CPROVER_old(pos) + *length <= size) + // clang-format off +{ + // skip a pair of double quotes + size_t start = pos; + _Bool result = 0; + + if(pos < size && base[pos] == '"') + { + pos++; + + if(pos < size && base[pos] == '"') + { + pos++; + *length = pos - start; + result = 1; + } + } + + return result; +} diff --git a/regression/cprover/contracts/integer_range3.desc b/regression/cprover/contracts/integer_range3.desc new file mode 100644 index 00000000000..831250508ae --- /dev/null +++ b/regression/cprover/contracts/integer_range3.desc @@ -0,0 +1,6 @@ +CORE +integer_range3.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/contracts/integer_range4.c b/regression/cprover/contracts/integer_range4.c new file mode 100644 index 00000000000..d4075f69307 --- /dev/null +++ b/regression/cprover/contracts/integer_range4.c @@ -0,0 +1,35 @@ +typedef __CPROVER_size_t size_t; + +_Bool returns_a_range(const char *base, size_t size, size_t pos, size_t *length) + // clang-format off + __CPROVER_requires(__CPROVER_r_ok(base, size)) + __CPROVER_requires(pos <= size) + __CPROVER_requires(__CPROVER_rw_ok(length)) + __CPROVER_assigns(*length) + __CPROVER_ensures(__CPROVER_return_value ==> __CPROVER_old(pos) + *length <= size) +// clang-format on +{ + // skip any number of double quotes + size_t start = pos; + _Bool result = 0; + + while(pos < size && base[pos] == '"') + // clang-format off + __CPROVER_loop_invariant(__CPROVER_r_ok(base, size)) + __CPROVER_loop_invariant(__CPROVER_rw_ok(length)) + __CPROVER_loop_invariant(!result) + // clang-format on + { + // out to be loop invariant + __CPROVER_assert(__CPROVER_old(pos) == start, "property 1"); + pos++; + } + + if(pos > start) + { + *length = pos - start; + result = 1; + } + + return result; +} diff --git a/regression/cprover/contracts/integer_range4.desc b/regression/cprover/contracts/integer_range4.desc new file mode 100644 index 00000000000..78128b8f5d3 --- /dev/null +++ b/regression/cprover/contracts/integer_range4.desc @@ -0,0 +1,6 @@ +CORE +integer_range4.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/contracts/memory_safety1.c b/regression/cprover/contracts/memory_safety1.c new file mode 100644 index 00000000000..c8f6b3b800a --- /dev/null +++ b/regression/cprover/contracts/memory_safety1.c @@ -0,0 +1,5 @@ +// no contract given at all +void my_function(int *pointer) +{ + *pointer = 123; +} diff --git a/regression/cprover/contracts/memory_safety1.desc b/regression/cprover/contracts/memory_safety1.desc new file mode 100644 index 00000000000..24307e12e11 --- /dev/null +++ b/regression/cprover/contracts/memory_safety1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +memory_safety1.yaml + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function my_function is safe / dereference line .*: SUCCESS$ +-- diff --git a/regression/cprover/contracts/nested1.c b/regression/cprover/contracts/nested1.c new file mode 100644 index 00000000000..2d392b59afc --- /dev/null +++ b/regression/cprover/contracts/nested1.c @@ -0,0 +1,16 @@ +void callee(int *p) + // clang-format off + __CPROVER_requires(__CPROVER_rw_ok(p)) +// clang-format on +{ +} + +void caller(int *p) + // clang-format off + __CPROVER_requires(__CPROVER_rw_ok(p)) +// clang-format on +{ + int i; + __CPROVER_assert(__CPROVER_rw_ok(p), "property 1"); + callee(&i); +} diff --git a/regression/cprover/contracts/nested1.desc b/regression/cprover/contracts/nested1.desc new file mode 100644 index 00000000000..7d95d6650f0 --- /dev/null +++ b/regression/cprover/contracts/nested1.desc @@ -0,0 +1,8 @@ +CORE +nested1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[caller\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[caller\.precondition\.1\] line \d+ callee precondition .*: SUCCESS$ +-- diff --git a/regression/cprover/contracts/one_function1.c b/regression/cprover/contracts/one_function1.c new file mode 100644 index 00000000000..e48cc77ec38 --- /dev/null +++ b/regression/cprover/contracts/one_function1.c @@ -0,0 +1,7 @@ +void my_function1() __CPROVER_ensures(0) // fails +{ +} + +void my_function2() __CPROVER_ensures(1) // passes +{ +} diff --git a/regression/cprover/contracts/one_function1.desc b/regression/cprover/contracts/one_function1.desc new file mode 100644 index 00000000000..a42d505aa4a --- /dev/null +++ b/regression/cprover/contracts/one_function1.desc @@ -0,0 +1,8 @@ +CORE +one_function1.c +--contract my_function2 +^EXIT=0$ +^SIGNAL=0$ +^\[my_function2\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +-- +^\[my_function1\.postcondition\.1\] line \d+ postcondition: REFUTED$ diff --git a/regression/cprover/contracts/recursive_function1.c b/regression/cprover/contracts/recursive_function1.c new file mode 100644 index 00000000000..111471fb986 --- /dev/null +++ b/regression/cprover/contracts/recursive_function1.c @@ -0,0 +1,8 @@ +int recursive_function(int parameter) + __CPROVER_requires(parameter >= 0) // passes +{ + if(parameter == 0) + return 0; + else + return recursive_function(parameter - 1) + 1; +} diff --git a/regression/cprover/contracts/recursive_function1.desc b/regression/cprover/contracts/recursive_function1.desc new file mode 100644 index 00000000000..4b203ae12ff --- /dev/null +++ b/regression/cprover/contracts/recursive_function1.desc @@ -0,0 +1,7 @@ +CORE +recursive_function1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[recursive_function\.precondition\.1] line \d+ recursive_function precondition.*: SUCCESS$ +-- diff --git a/regression/cprover/contracts/recursive_function2.c b/regression/cprover/contracts/recursive_function2.c new file mode 100644 index 00000000000..853afd6cd2a --- /dev/null +++ b/regression/cprover/contracts/recursive_function2.c @@ -0,0 +1,5 @@ +int recursive_function() + __CPROVER_ensures(__CPROVER_return_value == 10) // passes +{ + return recursive_function(); +} diff --git a/regression/cprover/contracts/recursive_function2.desc b/regression/cprover/contracts/recursive_function2.desc new file mode 100644 index 00000000000..2c37817d6a0 --- /dev/null +++ b/regression/cprover/contracts/recursive_function2.desc @@ -0,0 +1,7 @@ +CORE +recursive_function2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[recursive_function\.postcondition\.1] line \d+ postcondition: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_assigns1.c b/regression/cprover/contracts/use_assigns1.c new file mode 100644 index 00000000000..61f8949f4e1 --- /dev/null +++ b/regression/cprover/contracts/use_assigns1.c @@ -0,0 +1,16 @@ +int global; + +void my_function1(void) __CPROVER_assigns() +{ +} + +int main() +{ + global = 1; + + my_function1(); // does not assign 'global' + + __CPROVER_assert(global == 1, "property 1"); + + return 0; +} diff --git a/regression/cprover/contracts/use_assigns1.desc b/regression/cprover/contracts/use_assigns1.desc new file mode 100644 index 00000000000..58379a2849b --- /dev/null +++ b/regression/cprover/contracts/use_assigns1.desc @@ -0,0 +1,7 @@ +CORE +use_assigns1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_assigns2.c b/regression/cprover/contracts/use_assigns2.c new file mode 100644 index 00000000000..418e3480e76 --- /dev/null +++ b/regression/cprover/contracts/use_assigns2.c @@ -0,0 +1,16 @@ +int global; + +void my_function1(void) __CPROVER_assigns(global) +{ +} + +int main() +{ + global = 1; + + my_function1(); // does assign 'global' + + __CPROVER_assert(global == 1, "property 1"); // fails + + return 0; +} diff --git a/regression/cprover/contracts/use_assigns2.desc b/regression/cprover/contracts/use_assigns2.desc new file mode 100644 index 00000000000..bce4f56f182 --- /dev/null +++ b/regression/cprover/contracts/use_assigns2.desc @@ -0,0 +1,7 @@ +CORE +use_assigns2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/contracts/use_old1.c b/regression/cprover/contracts/use_old1.c new file mode 100644 index 00000000000..bb57b4c57da --- /dev/null +++ b/regression/cprover/contracts/use_old1.c @@ -0,0 +1,6 @@ +void my_function(int parameter) __CPROVER_requires(1) +{ + int copy = parameter; + parameter = 456; + __CPROVER_assert(copy == __CPROVER_old(parameter), "property 1"); +} diff --git a/regression/cprover/contracts/use_old1.desc b/regression/cprover/contracts/use_old1.desc new file mode 100644 index 00000000000..2630bdd1987 --- /dev/null +++ b/regression/cprover/contracts/use_old1.desc @@ -0,0 +1,7 @@ +CORE +use_old1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[my_function\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_old2.c b/regression/cprover/contracts/use_old2.c new file mode 100644 index 00000000000..1e1e96cbe6b --- /dev/null +++ b/regression/cprover/contracts/use_old2.c @@ -0,0 +1,12 @@ +void my_function(unsigned parameter) + // clang-format off + __CPROVER_requires(1) +// clang-format on +{ + for(unsigned counter = 0; counter != 100; counter++) + { + __CPROVER_assert(parameter == __CPROVER_old(parameter), "property 1"); + } + + __CPROVER_assert(parameter == __CPROVER_old(parameter), "property 2"); +} diff --git a/regression/cprover/contracts/use_old2.desc b/regression/cprover/contracts/use_old2.desc new file mode 100644 index 00000000000..dc7286b7daa --- /dev/null +++ b/regression/cprover/contracts/use_old2.desc @@ -0,0 +1,8 @@ +CORE +use_old2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[my_function\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[my_function\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_postcondition1.c b/regression/cprover/contracts/use_postcondition1.c new file mode 100644 index 00000000000..fba09bf368f --- /dev/null +++ b/regression/cprover/contracts/use_postcondition1.c @@ -0,0 +1,13 @@ +int global; + +void callee() __CPROVER_ensures(global >= 10) __CPROVER_assigns(global) +{ + global = 20; +} + +int main() +{ + callee(); + __CPROVER_assert(global >= 10, "property 1"); + return 0; +} diff --git a/regression/cprover/contracts/use_postcondition1.desc b/regression/cprover/contracts/use_postcondition1.desc new file mode 100644 index 00000000000..8164fa8ec70 --- /dev/null +++ b/regression/cprover/contracts/use_postcondition1.desc @@ -0,0 +1,9 @@ +CORE +use_postcondition1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[callee\.assigns\.1\] line \d+ assigns clause: SUCCESS$ +^\[callee\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_postcondition2.c b/regression/cprover/contracts/use_postcondition2.c new file mode 100644 index 00000000000..c7133397568 --- /dev/null +++ b/regression/cprover/contracts/use_postcondition2.c @@ -0,0 +1,14 @@ +int global; + +void callee() __CPROVER_ensures(global >= 10) __CPROVER_assigns(global) +{ + global = 20; +} + +int main() +{ + global = 1; + callee(); + __CPROVER_assert(0, "property 1"); // should fail + return 0; +} diff --git a/regression/cprover/contracts/use_postcondition4.c b/regression/cprover/contracts/use_postcondition4.c new file mode 100644 index 00000000000..5fbe7fb8076 --- /dev/null +++ b/regression/cprover/contracts/use_postcondition4.c @@ -0,0 +1,19 @@ +int global1, global2; + +void callee() + // clang-format off + __CPROVER_ensures(global2 == __CPROVER_old(global1)) + __CPROVER_assigns(global1, global2) +// clang-format on +{ + global2 = global1; + global1 = 20; +} + +int main() +{ + global1 = 123; + callee(); + __CPROVER_assert(global2 == 123, "property 1"); + return 0; +} diff --git a/regression/cprover/contracts/use_postcondition4.desc b/regression/cprover/contracts/use_postcondition4.desc new file mode 100644 index 00000000000..a24466214ef --- /dev/null +++ b/regression/cprover/contracts/use_postcondition4.desc @@ -0,0 +1,8 @@ +CORE +use_postcondition4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[callee\.postcondition\.1\] line \d+ postcondition: SUCCESS$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/contracts/use_precondition1.c b/regression/cprover/contracts/use_precondition1.c new file mode 100644 index 00000000000..b2cd0c3f19c --- /dev/null +++ b/regression/cprover/contracts/use_precondition1.c @@ -0,0 +1,8 @@ +void my_function(int parameter) + // clang-format off + __CPROVER_requires(parameter >= 10) +// clang-format on +{ + __CPROVER_assert(parameter != 0, "property 1"); // passes + __CPROVER_assert(parameter != 20, "property 2"); // fails +} diff --git a/regression/cprover/contracts/use_precondition1.desc b/regression/cprover/contracts/use_precondition1.desc new file mode 100644 index 00000000000..b74d6c38ab8 --- /dev/null +++ b/regression/cprover/contracts/use_precondition1.desc @@ -0,0 +1,8 @@ +CORE +use_precondition1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[my_function\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[my_function\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/contracts/use_precondition2.c b/regression/cprover/contracts/use_precondition2.c new file mode 100644 index 00000000000..84d963d27db --- /dev/null +++ b/regression/cprover/contracts/use_precondition2.c @@ -0,0 +1,8 @@ +void my_function(const int *p) + // clang-format off + __CPROVER_requires(__CPROVER_r_ok(p)) + __CPROVER_requires(*p >= 20) +// clang-format on +{ + __CPROVER_assert(*p != 0, "property 1"); // passes +} diff --git a/regression/cprover/contracts/use_precondition2.desc b/regression/cprover/contracts/use_precondition2.desc new file mode 100644 index 00000000000..964ca1c55f2 --- /dev/null +++ b/regression/cprover/contracts/use_precondition2.desc @@ -0,0 +1,9 @@ +CORE +use_precondition2.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[my_function\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[my_function\.pointer\.2\] line \d+ pointer p safe: SUCCESS$ +^\[my_function\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring1.c b/regression/cprover/cstrings/cstring1.c new file mode 100644 index 00000000000..9dccf42e8f6 --- /dev/null +++ b/regression/cprover/cstrings/cstring1.c @@ -0,0 +1,7 @@ +int main() +{ + char buffer[100]; + buffer[10] = 0; + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // passes + return 0; +} diff --git a/regression/cprover/cstrings/cstring1.desc b/regression/cprover/cstrings/cstring1.desc new file mode 100644 index 00000000000..e4bf9ccd9fd --- /dev/null +++ b/regression/cprover/cstrings/cstring1.desc @@ -0,0 +1,7 @@ +CORE +cstring1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring10.c b/regression/cprover/cstrings/cstring10.c new file mode 100644 index 00000000000..3d659fe8dc0 --- /dev/null +++ b/regression/cprover/cstrings/cstring10.c @@ -0,0 +1,9 @@ +int main() +{ + char buffer[100], *p, *q; + q = buffer; // take address + buffer[10] = 0; + *p = 123; // might point into buffer + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/cstring10.desc b/regression/cprover/cstrings/cstring10.desc new file mode 100644 index 00000000000..d42368d3929 --- /dev/null +++ b/regression/cprover/cstrings/cstring10.desc @@ -0,0 +1,7 @@ +CORE +cstring10.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring2.c b/regression/cprover/cstrings/cstring2.c new file mode 100644 index 00000000000..926e29bc707 --- /dev/null +++ b/regression/cprover/cstrings/cstring2.c @@ -0,0 +1,10 @@ +int main() +{ + char buffer[100]; + + // simply state equality + uninterpreted function + if(__CPROVER_is_cstring(buffer)) + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring2.desc b/regression/cprover/cstrings/cstring2.desc new file mode 100644 index 00000000000..c6dd3f06e9f --- /dev/null +++ b/regression/cprover/cstrings/cstring2.desc @@ -0,0 +1,7 @@ +CORE +cstring2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring3.c b/regression/cprover/cstrings/cstring3.c new file mode 100644 index 00000000000..987ba47bf9c --- /dev/null +++ b/regression/cprover/cstrings/cstring3.c @@ -0,0 +1,8 @@ +int main() +{ + char buffer[100]; + buffer[10] = 0; // establish + buffer[10] = 1; // wreck + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/cstring3.desc b/regression/cprover/cstrings/cstring3.desc new file mode 100644 index 00000000000..dc369db9b58 --- /dev/null +++ b/regression/cprover/cstrings/cstring3.desc @@ -0,0 +1,7 @@ +CORE +cstring3.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring4.c b/regression/cprover/cstrings/cstring4.c new file mode 100644 index 00000000000..6831f74142c --- /dev/null +++ b/regression/cprover/cstrings/cstring4.c @@ -0,0 +1,8 @@ +int main() +{ + char buffer[100], ch; + __CPROVER_assume(__CPROVER_is_cstring(buffer)); + ch = 'a'; // does not affect 'buffer' + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // passes + return 0; +} diff --git a/regression/cprover/cstrings/cstring4.desc b/regression/cprover/cstrings/cstring4.desc new file mode 100644 index 00000000000..fb7c231131c --- /dev/null +++ b/regression/cprover/cstrings/cstring4.desc @@ -0,0 +1,7 @@ +CORE +cstring4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring5.c b/regression/cprover/cstrings/cstring5.c new file mode 100644 index 00000000000..54dd60c2465 --- /dev/null +++ b/regression/cprover/cstrings/cstring5.c @@ -0,0 +1,9 @@ +int main() +{ + char buffer[100], *p, ch; + p = &ch; + __CPROVER_assume(__CPROVER_is_cstring(buffer)); + *p = 'a'; // possibly wreck, but doesn't happen + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // passes + return 0; +} diff --git a/regression/cprover/cstrings/cstring5.desc b/regression/cprover/cstrings/cstring5.desc new file mode 100644 index 00000000000..80d46cbd6f2 --- /dev/null +++ b/regression/cprover/cstrings/cstring5.desc @@ -0,0 +1,7 @@ +CORE +cstring5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring6.c b/regression/cprover/cstrings/cstring6.c new file mode 100644 index 00000000000..6a8a717116d --- /dev/null +++ b/regression/cprover/cstrings/cstring6.c @@ -0,0 +1,9 @@ +int main() +{ + char buffer[100], *p, *q; + buffer[10] = 0; + p = buffer; + __CPROVER_assert(__CPROVER_is_cstring(p), "property 1"); // passes + __CPROVER_assert(__CPROVER_is_cstring(q), "property 2"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/cstring6.desc b/regression/cprover/cstrings/cstring6.desc new file mode 100644 index 00000000000..0d84ad6e118 --- /dev/null +++ b/regression/cprover/cstrings/cstring6.desc @@ -0,0 +1,8 @@ +CORE +cstring6.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring7.c b/regression/cprover/cstrings/cstring7.c new file mode 100644 index 00000000000..53a344607d7 --- /dev/null +++ b/regression/cprover/cstrings/cstring7.c @@ -0,0 +1,8 @@ +int main() +{ + char buffer[100], *p; + buffer[10] = 0; + *p = 123; // might point into buffer + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/cstring7.desc b/regression/cprover/cstrings/cstring7.desc new file mode 100644 index 00000000000..bb05f60ecba --- /dev/null +++ b/regression/cprover/cstrings/cstring7.desc @@ -0,0 +1,7 @@ +CORE +cstring7.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring8.c b/regression/cprover/cstrings/cstring8.c new file mode 100644 index 00000000000..b2f10b8b9a1 --- /dev/null +++ b/regression/cprover/cstrings/cstring8.c @@ -0,0 +1,9 @@ +int main() +{ + char buffer[100]; + int i; + buffer[10] = 0; + buffer[i] = 123; // wrecks it + __CPROVER_assert(__CPROVER_is_cstring(buffer), "property 1"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/cstring8.desc b/regression/cprover/cstrings/cstring8.desc new file mode 100644 index 00000000000..7d61f37b2d3 --- /dev/null +++ b/regression/cprover/cstrings/cstring8.desc @@ -0,0 +1,7 @@ +CORE +cstring8.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring9.c b/regression/cprover/cstrings/cstring9.c new file mode 100644 index 00000000000..fb42ecafc8a --- /dev/null +++ b/regression/cprover/cstrings/cstring9.c @@ -0,0 +1,12 @@ +int main() +{ + const char *string; + + __CPROVER_assume(__CPROVER_is_cstring(string)); + + const char *p = string; + + __CPROVER_assert(__CPROVER_is_cstring(p), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring9.desc b/regression/cprover/cstrings/cstring9.desc new file mode 100644 index 00000000000..92b28dcfa77 --- /dev/null +++ b/regression/cprover/cstrings/cstring9.desc @@ -0,0 +1,7 @@ +CORE +cstring9.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_is_function1.c b/regression/cprover/cstrings/cstring_is_function1.c new file mode 100644 index 00000000000..a9052900c93 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_function1.c @@ -0,0 +1,9 @@ +int main() +{ + const char *p, *q; + __CPROVER_assume(p == q); + __CPROVER_assert( + __CPROVER_is_cstring(p) == __CPROVER_is_cstring(q), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_is_function1.desc b/regression/cprover/cstrings/cstring_is_function1.desc new file mode 100644 index 00000000000..7604dbe6545 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_function1.desc @@ -0,0 +1,7 @@ +CORE +cstring_is_function1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_is_memory_safe1.c b/regression/cprover/cstrings/cstring_is_memory_safe1.c new file mode 100644 index 00000000000..8080575056a --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe1.c @@ -0,0 +1,12 @@ +int main() +{ + const char *p; + __CPROVER_assume(__CPROVER_is_cstring(p)); + + // this is now safe + *p; + + p[1]; // but not that one + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_is_memory_safe1.desc b/regression/cprover/cstrings/cstring_is_memory_safe1.desc new file mode 100644 index 00000000000..685679a040e --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe1.desc @@ -0,0 +1,8 @@ +CORE +cstring_is_memory_safe1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer .* safe: REFUTED$ +-- diff --git a/regression/cprover/cstrings/cstring_is_memory_safe2.c b/regression/cprover/cstrings/cstring_is_memory_safe2.c new file mode 100644 index 00000000000..bd03491e053 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe2.c @@ -0,0 +1,10 @@ +int main() +{ + const char *p; + __CPROVER_size_t i; + __CPROVER_assume(__CPROVER_is_cstring(p + i)); + // this is now safe + p[i]; + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_is_memory_safe2.desc b/regression/cprover/cstrings/cstring_is_memory_safe2.desc new file mode 100644 index 00000000000..81c68eb5ba0 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe2.desc @@ -0,0 +1,7 @@ +CORE +cstring_is_memory_safe2.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_is_memory_safe3.c b/regression/cprover/cstrings/cstring_is_memory_safe3.c new file mode 100644 index 00000000000..b58c333d77c --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe3.c @@ -0,0 +1,12 @@ +int main() +{ + const char *p; + __CPROVER_size_t i; + __CPROVER_assume(__CPROVER_is_cstring(p + i)); + + // this is now safe + const unsigned char *unsigned_p = (const unsigned char *)p; + unsigned_p[i]; + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_is_memory_safe3.desc b/regression/cprover/cstrings/cstring_is_memory_safe3.desc new file mode 100644 index 00000000000..ee45ae42ce3 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_memory_safe3.desc @@ -0,0 +1,7 @@ +CORE +cstring_is_memory_safe3.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_is_not_null.c b/regression/cprover/cstrings/cstring_is_not_null.c new file mode 100644 index 00000000000..eed97894065 --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_not_null.c @@ -0,0 +1,10 @@ +int main() +{ + const char *p; + __CPROVER_assume(__CPROVER_is_cstring(p)); + + // p can't be null + __CPROVER_assert(p != 0, "property"); + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_is_not_null.desc b/regression/cprover/cstrings/cstring_is_not_null.desc new file mode 100644 index 00000000000..21a959777df --- /dev/null +++ b/regression/cprover/cstrings/cstring_is_not_null.desc @@ -0,0 +1,7 @@ +CORE +cstring_is_not_null.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1] line \d+ property: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_plus_one1.c b/regression/cprover/cstrings/cstring_plus_one1.c new file mode 100644 index 00000000000..dc7cad96f57 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one1.c @@ -0,0 +1,10 @@ +int main() +{ + const char *p; + __CPROVER_assume(__CPROVER_is_cstring(p)); + + if(*p != 0) + __CPROVER_assert(__CPROVER_is_cstring(p + 1), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_plus_one1.desc b/regression/cprover/cstrings/cstring_plus_one1.desc new file mode 100644 index 00000000000..150b2dad26d --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one1.desc @@ -0,0 +1,7 @@ +CORE +cstring_plus_one1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_plus_one2.c b/regression/cprover/cstrings/cstring_plus_one2.c new file mode 100644 index 00000000000..a7e41ea2b32 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one2.c @@ -0,0 +1,13 @@ +int main() +{ + const char *p; + __CPROVER_assume(__CPROVER_is_cstring(p)); + + if(*p != 0) + { + p++; // might modify the string + __CPROVER_assert(__CPROVER_is_cstring(p), "property 1"); // passes + } + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_plus_one2.desc b/regression/cprover/cstrings/cstring_plus_one2.desc new file mode 100644 index 00000000000..6fe932b95ca --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one2.desc @@ -0,0 +1,7 @@ +CORE +cstring_plus_one2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_plus_one3.c b/regression/cprover/cstrings/cstring_plus_one3.c new file mode 100644 index 00000000000..c16ca65ad24 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one3.c @@ -0,0 +1,10 @@ +int main() +{ + const char *p; + __CPROVER_size_t i; + __CPROVER_assume(__CPROVER_is_cstring(p + i)); + __CPROVER_assume(p[i] != 0); + __CPROVER_assert(__CPROVER_is_cstring(p + i + 1), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_plus_one3.desc b/regression/cprover/cstrings/cstring_plus_one3.desc new file mode 100644 index 00000000000..07454ecbbe2 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one3.desc @@ -0,0 +1,7 @@ +CORE +cstring_plus_one3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_plus_one4.c b/regression/cprover/cstrings/cstring_plus_one4.c new file mode 100644 index 00000000000..e49d600da19 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one4.c @@ -0,0 +1,11 @@ +int main() +{ + const char *p; + __CPROVER_size_t i; + __CPROVER_assume(__CPROVER_is_cstring(p + i)); + __CPROVER_assume(p[i] != 0); + i++; + __CPROVER_assert(__CPROVER_is_cstring(p + i), "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_plus_one4.desc b/regression/cprover/cstrings/cstring_plus_one4.desc new file mode 100644 index 00000000000..6e3a9b0b737 --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one4.desc @@ -0,0 +1,7 @@ +CORE +cstring_plus_one4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_plus_one5.c b/regression/cprover/cstrings/cstring_plus_one5.c new file mode 100644 index 00000000000..0ac3448bd5f --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one5.c @@ -0,0 +1,14 @@ +int main() +{ + const char *p; + + __CPROVER_assume(__CPROVER_is_cstring(p)); + __CPROVER_assume(*p != 0); + + p++; + + __CPROVER_assert(__CPROVER_is_cstring(p), "property 1"); + __CPROVER_assert(__CPROVER_r_ok(p), "property 2"); + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_plus_one5.desc b/regression/cprover/cstrings/cstring_plus_one5.desc new file mode 100644 index 00000000000..52511e22c5b --- /dev/null +++ b/regression/cprover/cstrings/cstring_plus_one5.desc @@ -0,0 +1,8 @@ +CORE +cstring_plus_one5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_preserved1.c b/regression/cprover/cstrings/cstring_preserved1.c new file mode 100644 index 00000000000..f50eb069268 --- /dev/null +++ b/regression/cprover/cstrings/cstring_preserved1.c @@ -0,0 +1,13 @@ +int main() +{ + const char *p; + + __CPROVER_assume(__CPROVER_is_cstring(p)); + + // unrelated + const char *q = 0; + + __CPROVER_assert(__CPROVER_is_cstring(p), "p is still a cstring"); + + return 0; +} diff --git a/regression/cprover/cstrings/cstring_preserved1.desc b/regression/cprover/cstrings/cstring_preserved1.desc new file mode 100644 index 00000000000..ab461ddc658 --- /dev/null +++ b/regression/cprover/cstrings/cstring_preserved1.desc @@ -0,0 +1,7 @@ +CORE +cstring_preserved1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ p is still a cstring: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstring_preserved2.c b/regression/cprover/cstrings/cstring_preserved2.c new file mode 100644 index 00000000000..8d4dd3d7eaa --- /dev/null +++ b/regression/cprover/cstrings/cstring_preserved2.c @@ -0,0 +1,8 @@ +void cstring_preserved(const char *p) + __CPROVER_requires(__CPROVER_is_cstring(p)) +{ + // unrelated + __CPROVER_size_t i = 0; + + __CPROVER_assert(__CPROVER_is_cstring(p), "p is still a cstring"); +} diff --git a/regression/cprover/cstrings/cstring_preserved2.desc b/regression/cprover/cstrings/cstring_preserved2.desc new file mode 100644 index 00000000000..270cb706f7a --- /dev/null +++ b/regression/cprover/cstrings/cstring_preserved2.desc @@ -0,0 +1,7 @@ +CORE +cstring_preserved2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[cstring_preserved\.assertion\.1\] line \d+ p is still a cstring: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstrlen1.c b/regression/cprover/cstrings/cstrlen1.c new file mode 100644 index 00000000000..f67f9a7bbd6 --- /dev/null +++ b/regression/cprover/cstrings/cstrlen1.c @@ -0,0 +1,7 @@ +int main() +{ + char buffer[100]; + buffer[0] = 0; + __CPROVER_assert(__CPROVER_cstrlen(buffer) == 0, "property 1"); // passes + return 0; +} diff --git a/regression/cprover/cstrings/cstrlen1.desc b/regression/cprover/cstrings/cstrlen1.desc new file mode 100644 index 00000000000..3b4ca29158e --- /dev/null +++ b/regression/cprover/cstrings/cstrlen1.desc @@ -0,0 +1,7 @@ +CORE +cstrlen1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/cstrlen2.c b/regression/cprover/cstrings/cstrlen2.c new file mode 100644 index 00000000000..2f8dca1e269 --- /dev/null +++ b/regression/cprover/cstrings/cstrlen2.c @@ -0,0 +1,5 @@ +void string_access(__CPROVER_size_t length, const char *p) + __CPROVER_requires(__CPROVER_is_cstring(p) && __CPROVER_cstrlen(p) == length) +{ + __CPROVER_assert(p[length] == 0, "property 1"); // passes +} diff --git a/regression/cprover/cstrings/cstrlen2.desc b/regression/cprover/cstrings/cstrlen2.desc new file mode 100644 index 00000000000..776afeaaafc --- /dev/null +++ b/regression/cprover/cstrings/cstrlen2.desc @@ -0,0 +1,7 @@ +KNOWNBUG +cstrlen2.c + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function string_access is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/iterate_over_string1.c b/regression/cprover/cstrings/iterate_over_string1.c new file mode 100644 index 00000000000..4d86a3afe96 --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string1.c @@ -0,0 +1,16 @@ +int main() +{ + const char *p; + + __CPROVER_assume(__CPROVER_is_cstring(p)); + + while(*p != 0) + // clang-format off + __CPROVER_loop_invariant(__CPROVER_is_cstring(p)) + { + p++; + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/cstrings/iterate_over_string1.desc b/regression/cprover/cstrings/iterate_over_string1.desc new file mode 100644 index 00000000000..262af2543eb --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string1.desc @@ -0,0 +1,7 @@ +CORE +iterate_over_string1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/iterate_over_string2.c b/regression/cprover/cstrings/iterate_over_string2.c new file mode 100644 index 00000000000..3cd9ab95d11 --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string2.c @@ -0,0 +1,18 @@ +int main() +{ + const char *p; + + __CPROVER_assume(__CPROVER_is_cstring(p)); + + __CPROVER_size_t i = 0; + + while(p[i] != 0) + // clang-format off + __CPROVER_loop_invariant(__CPROVER_is_cstring(p + i)) + { + i++; + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/cstrings/iterate_over_string2.desc b/regression/cprover/cstrings/iterate_over_string2.desc new file mode 100644 index 00000000000..49ca08f539b --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string2.desc @@ -0,0 +1,7 @@ +CORE +iterate_over_string2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/iterate_over_string3.c b/regression/cprover/cstrings/iterate_over_string3.c new file mode 100644 index 00000000000..88bcf043bd1 --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string3.c @@ -0,0 +1,37 @@ +#define size_t __CPROVER_size_t +#define false 0 + +_Bool compare( + const void *const array, + const size_t array_len, + const char *const c_str) +{ + // const unsigned char *array_bytes = array; + const unsigned char *str_bytes = (const unsigned char *)c_str; + + // jointly iterate over string and array + for(size_t i = 0; i < array_len; ++i) + __CPROVER_loop_invariant(__CPROVER_is_cstring(str_bytes + i)) + { + unsigned char s = str_bytes[i]; + if(s == '\0') + { + return false; + } + + // if (array_bytes[i] != s) { + // return false; + // } + } + + //return str_bytes[array_len] == '\0'; +} + +int main() +{ + unsigned char array[10]; + size_t array_len = 10; + const char *c_str; + __CPROVER_assume(__CPROVER_is_cstring(c_str)); + compare(array, array_len, c_str); +} diff --git a/regression/cprover/cstrings/iterate_over_string3.desc b/regression/cprover/cstrings/iterate_over_string3.desc new file mode 100644 index 00000000000..108dc12aea5 --- /dev/null +++ b/regression/cprover/cstrings/iterate_over_string3.desc @@ -0,0 +1,7 @@ +CORE +iterate_over_string3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[compare\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/string_literal1.c b/regression/cprover/cstrings/string_literal1.c new file mode 100644 index 00000000000..f2cadc39519 --- /dev/null +++ b/regression/cprover/cstrings/string_literal1.c @@ -0,0 +1,8 @@ +int main() +{ + char *p = "0123"; + __CPROVER_assert(__CPROVER_is_cstring(p), "property 1"); // passes + __CPROVER_assert(__CPROVER_is_cstring(p + 4), "property 2"); // passes + // __CPROVER_assert(__CPROVER_is_cstring(p+5), "property 3"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/string_literal1.desc b/regression/cprover/cstrings/string_literal1.desc new file mode 100644 index 00000000000..665e2502eb9 --- /dev/null +++ b/regression/cprover/cstrings/string_literal1.desc @@ -0,0 +1,8 @@ +CORE +string_literal1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/cstrings/string_literal2.c b/regression/cprover/cstrings/string_literal2.c new file mode 100644 index 00000000000..e383a12e30a --- /dev/null +++ b/regression/cprover/cstrings/string_literal2.c @@ -0,0 +1,11 @@ +const char *HEX_CHARS = "0123456789abcdef"; + +int main() +{ + unsigned char input; + char output; + + output = HEX_CHARS[input & 0xf]; + + return 0; +} diff --git a/regression/cprover/cstrings/string_literal2.desc b/regression/cprover/cstrings/string_literal2.desc new file mode 100644 index 00000000000..3a90ab877d3 --- /dev/null +++ b/regression/cprover/cstrings/string_literal2.desc @@ -0,0 +1,6 @@ +CORE +string_literal2.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/cstrings/string_literal3.c b/regression/cprover/cstrings/string_literal3.c new file mode 100644 index 00000000000..7826301b224 --- /dev/null +++ b/regression/cprover/cstrings/string_literal3.c @@ -0,0 +1,7 @@ +int main() +{ + char *p = "0123"; + __CPROVER_assert(__CPROVER_r_ok(p), "property 1"); // passes + __CPROVER_assert(__CPROVER_w_ok(p), "property 2"); // fails + return 0; +} diff --git a/regression/cprover/cstrings/string_literal3.desc b/regression/cprover/cstrings/string_literal3.desc new file mode 100644 index 00000000000..a690e7ac3fb --- /dev/null +++ b/regression/cprover/cstrings/string_literal3.desc @@ -0,0 +1,8 @@ +CORE +string_literal3.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/cstrings/string_literal4.c b/regression/cprover/cstrings/string_literal4.c new file mode 100644 index 00000000000..13694836834 --- /dev/null +++ b/regression/cprover/cstrings/string_literal4.c @@ -0,0 +1,7 @@ +int main() +{ + char *p = "0123"; + __CPROVER_assert(__CPROVER_OBJECT_SIZE(p) == 5, "property 1"); // passes + __CPROVER_assert(__CPROVER_LIVE_OBJECT(p), "property 2"); // passes + return 0; +} diff --git a/regression/cprover/cstrings/string_literal4.desc b/regression/cprover/cstrings/string_literal4.desc new file mode 100644 index 00000000000..16ecee0023a --- /dev/null +++ b/regression/cprover/cstrings/string_literal4.desc @@ -0,0 +1,6 @@ +CORE +string_literal4.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/cstrings/strlen1.c b/regression/cprover/cstrings/strlen1.c new file mode 100644 index 00000000000..2260045fb51 --- /dev/null +++ b/regression/cprover/cstrings/strlen1.c @@ -0,0 +1,21 @@ +__CPROVER_size_t my_strlen(const char *p) + // clang-format off + __CPROVER_requires(__CPROVER_is_cstring(p)) + __CPROVER_ensures(__CPROVER_return_value == __CPROVER_cstrlen(p)) +// clang-format on +{ + __CPROVER_size_t result = 0; + + while(*p != 0) + // clang-format off + __CPROVER_loop_invariant(__CPROVER_is_cstring(p)) + __CPROVER_loop_invariant(p - __CPROVER_old(p) == result) + __CPROVER_loop_invariant(result <= __CPROVER_cstrlen(__CPROVER_old(p))) + { + p++; + result++; + } + // clang-format on + + return result; +} diff --git a/regression/cprover/cstrings/strlen1.desc b/regression/cprover/cstrings/strlen1.desc new file mode 100644 index 00000000000..c24acde0e35 --- /dev/null +++ b/regression/cprover/cstrings/strlen1.desc @@ -0,0 +1,6 @@ +KNOWNBUG +strlen1.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/cstrings/strlen2.c b/regression/cprover/cstrings/strlen2.c new file mode 100644 index 00000000000..97b61d38f9a --- /dev/null +++ b/regression/cprover/cstrings/strlen2.c @@ -0,0 +1,7 @@ +__CPROVER_size_t strlen(const char *); + +int main() +{ + strlen("foo"); // safe + strlen(0); // unsafe +} diff --git a/regression/cprover/cstrings/strlen2.desc b/regression/cprover/cstrings/strlen2.desc new file mode 100644 index 00000000000..768b395adb3 --- /dev/null +++ b/regression/cprover/cstrings/strlen2.desc @@ -0,0 +1,8 @@ +CORE +strlen2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.strlen\.1\] line \d+ strlen argument must be C string: SUCCESS$ +^\[main\.strlen\.2\] line \d+ strlen argument must be C string: REFUTED$ +-- diff --git a/regression/cprover/float/basic-float1.c b/regression/cprover/float/basic-float1.c new file mode 100644 index 00000000000..90a781ffcdb --- /dev/null +++ b/regression/cprover/float/basic-float1.c @@ -0,0 +1,6 @@ +int main() +{ + float f = 1.0; + __CPROVER_assert(f + 0.5f == 1.5f, "addition"); + return 0; +} diff --git a/regression/cprover/float/basic-float1.desc b/regression/cprover/float/basic-float1.desc new file mode 100644 index 00000000000..f166c3d1380 --- /dev/null +++ b/regression/cprover/float/basic-float1.desc @@ -0,0 +1,9 @@ +CORE +basic-float1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝main::1::f❞:=floatbv_typecast\(1, floatbv\[32\], ς\(❝__CPROVER_rounding_mode❞\)\)\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ ieee_float_equal\(floatbv_plus\(ς\(❝main::1::f❞\), 0\.5, ς\(❝__CPROVER_rounding_mode❞\)\), 1\.5\)$ +^\[main\.assertion\.1\] line \d+ addition: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call1.c b/regression/cprover/function_calls/call1.c new file mode 100644 index 00000000000..b6cad2b58c6 --- /dev/null +++ b/regression/cprover/function_calls/call1.c @@ -0,0 +1,11 @@ +int my_function() +{ + return 10; +} + +int main() +{ + int x; + x = my_function(); + __CPROVER_assert(x == 10, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call1.desc b/regression/cprover/function_calls/call1.desc new file mode 100644 index 00000000000..3683f897a68 --- /dev/null +++ b/regression/cprover/function_calls/call1.desc @@ -0,0 +1,7 @@ +CORE +call1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call2.c b/regression/cprover/function_calls/call2.c new file mode 100644 index 00000000000..c477d3914d6 --- /dev/null +++ b/regression/cprover/function_calls/call2.c @@ -0,0 +1,9 @@ +// no body +int my_function(); + +int main() +{ + int x; + x = my_function(); + __CPROVER_assert(x == 10, "property 1"); // should fail +} diff --git a/regression/cprover/function_calls/call2.desc b/regression/cprover/function_calls/call2.desc new file mode 100644 index 00000000000..0dc0e41bb56 --- /dev/null +++ b/regression/cprover/function_calls/call2.desc @@ -0,0 +1,7 @@ +CORE +call2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/function_calls/call3.c b/regression/cprover/function_calls/call3.c new file mode 100644 index 00000000000..e482b27685d --- /dev/null +++ b/regression/cprover/function_calls/call3.c @@ -0,0 +1,16 @@ +int bar() +{ + return 10; +} + +int foo() +{ + return bar(); +} + +int main() +{ + int x; + x = foo(); + __CPROVER_assert(x == 10, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call3.desc b/regression/cprover/function_calls/call3.desc new file mode 100644 index 00000000000..3d229e7137a --- /dev/null +++ b/regression/cprover/function_calls/call3.desc @@ -0,0 +1,7 @@ +CORE +call3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call4.c b/regression/cprover/function_calls/call4.c new file mode 100644 index 00000000000..4f5417b5709 --- /dev/null +++ b/regression/cprover/function_calls/call4.c @@ -0,0 +1,12 @@ +int global; + +void my_function() +{ + global = 10; // a side effect +} + +int main() +{ + my_function(); + __CPROVER_assert(global == 10, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call4.desc b/regression/cprover/function_calls/call4.desc new file mode 100644 index 00000000000..2d12066f21b --- /dev/null +++ b/regression/cprover/function_calls/call4.desc @@ -0,0 +1,7 @@ +CORE +call4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call5.c b/regression/cprover/function_calls/call5.c new file mode 100644 index 00000000000..b0874037a93 --- /dev/null +++ b/regression/cprover/function_calls/call5.c @@ -0,0 +1,11 @@ +int my_function(int parameter) +{ + return parameter; +} + +int main() +{ + int x; + x = my_function(123); + __CPROVER_assert(x == 123, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call5.desc b/regression/cprover/function_calls/call5.desc new file mode 100644 index 00000000000..21dc2b161c8 --- /dev/null +++ b/regression/cprover/function_calls/call5.desc @@ -0,0 +1,7 @@ +CORE +call5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call6.c b/regression/cprover/function_calls/call6.c new file mode 100644 index 00000000000..916117db462 --- /dev/null +++ b/regression/cprover/function_calls/call6.c @@ -0,0 +1,11 @@ +int my_function(int parameter1, int parameter2) +{ + return parameter1 + parameter2; +} + +int main() +{ + int x; + x = my_function(123, 1); + __CPROVER_assert(x == 124, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call6.desc b/regression/cprover/function_calls/call6.desc new file mode 100644 index 00000000000..63e9eb2997f --- /dev/null +++ b/regression/cprover/function_calls/call6.desc @@ -0,0 +1,7 @@ +CORE +call6.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call7.c b/regression/cprover/function_calls/call7.c new file mode 100644 index 00000000000..8a4fd65b1f8 --- /dev/null +++ b/regression/cprover/function_calls/call7.c @@ -0,0 +1,11 @@ +int my_function(int *p) +{ + return *p; +} + +int main() +{ + int x, y; + x = my_function(&y); + __CPROVER_assert(x == y, "property 1"); // should pass +} diff --git a/regression/cprover/function_calls/call7.desc b/regression/cprover/function_calls/call7.desc new file mode 100644 index 00000000000..5ba7ff2b1b5 --- /dev/null +++ b/regression/cprover/function_calls/call7.desc @@ -0,0 +1,7 @@ +CORE +call7.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/call8.c b/regression/cprover/function_calls/call8.c new file mode 100644 index 00000000000..f37586f51dc --- /dev/null +++ b/regression/cprover/function_calls/call8.c @@ -0,0 +1,10 @@ +void my_function(int *p) +{ + *p; +} + +int main() +{ + my_function(0); // not safe + return 0; +} diff --git a/regression/cprover/function_calls/call8.desc b/regression/cprover/function_calls/call8.desc new file mode 100644 index 00000000000..fc4b676901f --- /dev/null +++ b/regression/cprover/function_calls/call8.desc @@ -0,0 +1,7 @@ +CORE +call8.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[my_function\.pointer\.1\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/function_calls/call_no_body1.c b/regression/cprover/function_calls/call_no_body1.c new file mode 100644 index 00000000000..8f3d55e3b36 --- /dev/null +++ b/regression/cprover/function_calls/call_no_body1.c @@ -0,0 +1,7 @@ +int function_without_body(); + +int main() +{ + int x = function_without_body(); + return 0; +} diff --git a/regression/cprover/function_calls/call_no_body1.desc b/regression/cprover/function_calls/call_no_body1.desc new file mode 100644 index 00000000000..fe1c8685695 --- /dev/null +++ b/regression/cprover/function_calls/call_no_body1.desc @@ -0,0 +1,9 @@ +CORE +call_no_body1.c +--text +^EXIT=0$ +^SIGNAL=0$ +^\*\*\*\* WARNING: no body for function function_without_body$ +^\(\d+\) ∀ ς : state, nondet::S28\.2-0 : signedbv\[32\] \. S28\.1\(ς\) ⇒ S28\.2\(ς\[❝main::\$tmp::return_value_function_without_body❞:=nondet::S28\.2-0\]\)$ +^\(\d+\) ∀ ς : state \. S28\.2\(ς\) ⇒ S28\.3\(ς\[❝main::1::x❞:=ς\(❝main::\$tmp::return_value_function_without_body❞\)\]\)$ +-- diff --git a/regression/cprover/function_calls/call_no_body2.c b/regression/cprover/function_calls/call_no_body2.c new file mode 100644 index 00000000000..e00056c3245 --- /dev/null +++ b/regression/cprover/function_calls/call_no_body2.c @@ -0,0 +1,12 @@ +struct some_struct +{ + int x; +}; + +struct some_struct function_without_body(); + +int main() +{ + struct some_struct s = function_without_body(); + return 0; +} diff --git a/regression/cprover/function_calls/call_no_body2.desc b/regression/cprover/function_calls/call_no_body2.desc new file mode 100644 index 00000000000..05093778b48 --- /dev/null +++ b/regression/cprover/function_calls/call_no_body2.desc @@ -0,0 +1,7 @@ +CORE +call_no_body2.c +--text +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state, nondet::S28\.2-0 : signedbv\[32\] \. S28\.1\(ς\) ⇒ S28\.2\(ς\[❝main::\$tmp::return_value_function_without_body❞\.❝x❞:=nondet::S28\.2-0\]\)$ +-- diff --git a/regression/cprover/function_calls/call_twice1.c b/regression/cprover/function_calls/call_twice1.c new file mode 100644 index 00000000000..41beeb4ec6e --- /dev/null +++ b/regression/cprover/function_calls/call_twice1.c @@ -0,0 +1,16 @@ +int global; + +void my_function() +{ + global++; // a side effect +} + +int main() +{ + global = 1; + my_function(); + __CPROVER_assert(global == 2, "property 1"); // should pass + + my_function(); + __CPROVER_assert(global == 3, "property 2"); // should pass +} diff --git a/regression/cprover/function_calls/call_twice1.desc b/regression/cprover/function_calls/call_twice1.desc new file mode 100644 index 00000000000..22df428dac4 --- /dev/null +++ b/regression/cprover/function_calls/call_twice1.desc @@ -0,0 +1,8 @@ +CORE +call_twice1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/function_pointer1.c b/regression/cprover/function_calls/function_pointer1.c new file mode 100644 index 00000000000..83492844db5 --- /dev/null +++ b/regression/cprover/function_calls/function_pointer1.c @@ -0,0 +1,17 @@ +int my_function(void) +{ + return 10; +} + +typedef int (*fptrt)(void); + +int main() +{ + fptrt fptr1 = my_function; + fptrt fptr2 = 0; + + fptr1(); // safe + fptr2(); // unsafe + + return 0; +} diff --git a/regression/cprover/function_calls/function_pointer1.desc b/regression/cprover/function_calls/function_pointer1.desc new file mode 100644 index 00000000000..2ec13934978 --- /dev/null +++ b/regression/cprover/function_calls/function_pointer1.desc @@ -0,0 +1,8 @@ +KNOWNBUG +function_pointer1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer_dereference\.1\] line \d+ dereferenced function pointer must be my_function: SUCCESS$ +^\[main\.pointer_dereference\.2\] line \d+ dereferenced function pointer must be my_function: REFUTED$ +-- diff --git a/regression/cprover/function_calls/no_main1.c b/regression/cprover/function_calls/no_main1.c new file mode 100644 index 00000000000..7ca7977eaeb --- /dev/null +++ b/regression/cprover/function_calls/no_main1.c @@ -0,0 +1,4 @@ +void my_function(int parameter) +{ + // nothing here +} diff --git a/regression/cprover/function_calls/no_main1.desc b/regression/cprover/function_calls/no_main1.desc new file mode 100644 index 00000000000..40e6abdd858 --- /dev/null +++ b/regression/cprover/function_calls/no_main1.desc @@ -0,0 +1,7 @@ +CORE +no_main1.c + +^EXIT=6$ +^SIGNAL=0$ +^error: The program has no entry point$ +-- diff --git a/regression/cprover/function_calls/recursion1.c b/regression/cprover/function_calls/recursion1.c new file mode 100644 index 00000000000..14db6eba800 --- /dev/null +++ b/regression/cprover/function_calls/recursion1.c @@ -0,0 +1,9 @@ +void foo() +{ + foo(); +} + +int main() +{ + foo(); +} diff --git a/regression/cprover/function_calls/recursion1.desc b/regression/cprover/function_calls/recursion1.desc new file mode 100644 index 00000000000..82f4a6600f3 --- /dev/null +++ b/regression/cprover/function_calls/recursion1.desc @@ -0,0 +1,6 @@ +CORE +recursion1.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/function_calls/va_args1.c b/regression/cprover/function_calls/va_args1.c new file mode 100644 index 00000000000..de6aad7af37 --- /dev/null +++ b/regression/cprover/function_calls/va_args1.c @@ -0,0 +1,19 @@ +#include + +int sum(int first, ...) +{ + va_list ap; + va_start(ap, first); + + int result = first; + result += va_arg(ap, int); + result += va_arg(ap, int); + return result; +} + +int main() +{ + int total; + total = sum(1, 2, 3); + __CPROVER_assert(total == 1 + 2 + 3, "property 1"); +} diff --git a/regression/cprover/function_calls/va_args1.desc b/regression/cprover/function_calls/va_args1.desc new file mode 100644 index 00000000000..d85bd0bd489 --- /dev/null +++ b/regression/cprover/function_calls/va_args1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +va_args1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/va_args2.c b/regression/cprover/function_calls/va_args2.c new file mode 100644 index 00000000000..be3b42fe29b --- /dev/null +++ b/regression/cprover/function_calls/va_args2.c @@ -0,0 +1,18 @@ +#include + +void f(int first, ...) +{ + va_list ap; + va_start(ap, first); + + int second; + second = va_arg(ap, int); + + __CPROVER_assert(first == 1, "property 1"); + __CPROVER_assert(second == 2, "property 2"); +} + +int main() +{ + f(1, 2); +} diff --git a/regression/cprover/function_calls/va_args2.desc b/regression/cprover/function_calls/va_args2.desc new file mode 100644 index 00000000000..0874117d3cb --- /dev/null +++ b/regression/cprover/function_calls/va_args2.desc @@ -0,0 +1,8 @@ +KNOWNBUG +va_args2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[f\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[f\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/function_calls/va_args3.c b/regression/cprover/function_calls/va_args3.c new file mode 100644 index 00000000000..c09a4c78752 --- /dev/null +++ b/regression/cprover/function_calls/va_args3.c @@ -0,0 +1,22 @@ +#include + +void f(int first, ...) +{ + va_list ap; + va_start(ap, first); + + int second; + second = va_arg(ap, int); + + int third; + third = va_arg(ap, int); + + __CPROVER_assert(first == 1, "property 1"); + __CPROVER_assert(second == 2, "property 2"); + __CPROVER_assert(third == 3, "property 3"); +} + +int main() +{ + f(1, 2, 3); +} diff --git a/regression/cprover/function_calls/va_args3.desc b/regression/cprover/function_calls/va_args3.desc new file mode 100644 index 00000000000..3face4e5475 --- /dev/null +++ b/regression/cprover/function_calls/va_args3.desc @@ -0,0 +1,9 @@ +KNOWNBUG +va_args3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[f\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[f\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[f\.assertion\.3\] line \d+ property 3: SUCCESS$ +-- diff --git a/regression/cprover/function_pointers/malloc_wrapper.c b/regression/cprover/function_pointers/malloc_wrapper.c new file mode 100644 index 00000000000..f0b96c94607 --- /dev/null +++ b/regression/cprover/function_pointers/malloc_wrapper.c @@ -0,0 +1,13 @@ +void *malloc(__CPROVER_size_t); + +struct my_allocatort +{ + void *(*allocate)(__CPROVER_size_t); +}; + +int main() +{ + struct my_allocatort allocator = {malloc}; + void *p = allocator.allocate(1); + __CPROVER_assert(__CPROVER_rw_ok(p, 1), "property 1"); +} diff --git a/regression/cprover/function_pointers/malloc_wrapper.desc b/regression/cprover/function_pointers/malloc_wrapper.desc new file mode 100644 index 00000000000..e266aac920c --- /dev/null +++ b/regression/cprover/function_pointers/malloc_wrapper.desc @@ -0,0 +1,7 @@ +KNOWNBUG +malloc_wrapper.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/lists/sentinel_dll1.c b/regression/cprover/lists/sentinel_dll1.c new file mode 100644 index 00000000000..34960415f46 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll1.c @@ -0,0 +1,28 @@ +// Modeled after the AWS C common doubly linked list. +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.h +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.inl + +// The 'head' and 'tail' nodes are sentinel nodes, indicating the +// beginning and end of the list. + +#include + +struct List +{ + struct List *n, *p; +}; + +int main() +{ + struct List head, tail; + + // setup the empty list + head.n = &tail; + head.p = 0; + tail.n = 0; + tail.p = &head; + + __CPROVER_assert(__CPROVER_is_sentinel_dll(&head, &tail), "property 1"); + + return 0; +} diff --git a/regression/cprover/lists/sentinel_dll1.desc b/regression/cprover/lists/sentinel_dll1.desc new file mode 100644 index 00000000000..ca8d3e3d2b2 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll1.desc @@ -0,0 +1,7 @@ +CORE +sentinel_dll1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/lists/sentinel_dll2.c b/regression/cprover/lists/sentinel_dll2.c new file mode 100644 index 00000000000..526b2705f44 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll2.c @@ -0,0 +1,34 @@ +// Modeled after the AWS C common doubly linked list. +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.h +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.inl + +// The 'head' and 'tail' nodes are sentinel nodes, indicating the +// beginning and end of the list. + +#include + +struct List +{ + struct List *n, *p; +}; + +int main() +{ + struct List head, tail; + + // setup the empty list + head.n = &tail; + tail.p = &head; + + // now add one node at the end + struct List new_node; + new_node.n = &tail; + new_node.p = tail.p; + tail.p->n = &new_node; + + // check it's a node + __CPROVER_assert( + __CPROVER_is_sentinel_dll(&head, &tail, &new_node), "property 1"); + + return 0; +} diff --git a/regression/cprover/lists/sentinel_dll2.desc b/regression/cprover/lists/sentinel_dll2.desc new file mode 100644 index 00000000000..2aad648b5a5 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll2.desc @@ -0,0 +1,7 @@ +KNOWNBUG +sentinel_dll2.c + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function main is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/lists/sentinel_dll3.c b/regression/cprover/lists/sentinel_dll3.c new file mode 100644 index 00000000000..e54d37a1af3 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll3.c @@ -0,0 +1,35 @@ +// Modeled after the AWS C common doubly linked list. +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.h +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.inl + +// The 'head' and 'tail' nodes are sentinel nodes, indicating the +// beginning and end of the list. + +#include + +struct List +{ + struct List *n, *p; +}; + +int main() +{ + struct List head, tail; + + // Assume we've got a node in this list! + struct List *node; + __CPROVER_assume(__CPROVER_is_sentinel_dll(&head, &tail, node)); + + // we are either at the end or have another node + if(node->n == &tail) + { + } + else + { + // check it's a node + __CPROVER_assert( + __CPROVER_is_sentinel_dll(&head, &tail, node->n), "property 1"); + } + + return 0; +} diff --git a/regression/cprover/lists/sentinel_dll3.desc b/regression/cprover/lists/sentinel_dll3.desc new file mode 100644 index 00000000000..d4f3753f661 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll3.desc @@ -0,0 +1,7 @@ +KNOWNBUG +sentinel_dll3.c +--no-memory-check +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function main is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/lists/sentinel_dll4.c b/regression/cprover/lists/sentinel_dll4.c new file mode 100644 index 00000000000..4649a80c63c --- /dev/null +++ b/regression/cprover/lists/sentinel_dll4.c @@ -0,0 +1,32 @@ +// Modeled after the AWS C common doubly linked list. +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.h +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.inl + +// The 'head' and 'tail' nodes are sentinel nodes, indicating the +// beginning and end of the list. + +#include + +struct List +{ + struct List *n, *p; +}; + +int main() +{ + struct List head, tail; + + // Assume we've got a node in this list! + struct List *node; + __CPROVER_assume(__CPROVER_is_sentinel_dll(&head, &tail, node)); + + // do unrelated assignments + int i = 123; + int *q = &i; + *q = 456; + + // check it's still a node + __CPROVER_assert(__CPROVER_is_sentinel_dll(&head, &tail, node), "property 1"); + + return 0; +} diff --git a/regression/cprover/lists/sentinel_dll4.desc b/regression/cprover/lists/sentinel_dll4.desc new file mode 100644 index 00000000000..a8f06001473 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll4.desc @@ -0,0 +1,7 @@ +CORE +sentinel_dll4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/lists/sentinel_dll5.c b/regression/cprover/lists/sentinel_dll5.c new file mode 100644 index 00000000000..3292ca8214c --- /dev/null +++ b/regression/cprover/lists/sentinel_dll5.c @@ -0,0 +1,30 @@ +// Modeled after the AWS C common doubly linked list. +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.h +// https://github.com/awslabs/aws-c-common/blob/main/include/aws/common/linked_list.inl + +// The 'head' and 'tail' nodes are sentinel nodes, indicating the +// beginning and end of the list. + +#include + +struct List +{ + struct List *n, *p; +}; + +int main() +{ + struct List head, tail; + + // Assume we've got a node in this list! + struct List *node; + __CPROVER_assume(__CPROVER_is_sentinel_dll(&head, &tail, node)); + + // do unrelated assignment, but to a node type + struct List *other_node = 0; + + // check it's still a node + __CPROVER_assert(__CPROVER_is_sentinel_dll(&head, &tail, node), "property 1"); + + return 0; +} diff --git a/regression/cprover/lists/sentinel_dll5.desc b/regression/cprover/lists/sentinel_dll5.desc new file mode 100644 index 00000000000..781f53c5ea8 --- /dev/null +++ b/regression/cprover/lists/sentinel_dll5.desc @@ -0,0 +1,7 @@ +KNOWNBUG +sentinel_dll5.c + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function main is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/loops/assigns1.c b/regression/cprover/loops/assigns1.c new file mode 100644 index 00000000000..ae0b8e83ac8 --- /dev/null +++ b/regression/cprover/loops/assigns1.c @@ -0,0 +1,12 @@ +int main() +{ + int *p; + __CPROVER_assume(__CPROVER_r_ok(p)); + + for(int i = 0; i < 10; i++) + { + // does not assign p + } + + __CPROVER_assert(__CPROVER_r_ok(p), "property 1"); // passes +} diff --git a/regression/cprover/loops/assigns1.desc b/regression/cprover/loops/assigns1.desc new file mode 100644 index 00000000000..70e3d8c4b82 --- /dev/null +++ b/regression/cprover/loops/assigns1.desc @@ -0,0 +1,8 @@ +KNOWNBUG +assigns1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/block_loop.c b/regression/cprover/loops/block_loop.c new file mode 100644 index 00000000000..a5b98d7b83b --- /dev/null +++ b/regression/cprover/loops/block_loop.c @@ -0,0 +1,17 @@ +#define BLOCK_SIZE 128 + +int main() +{ + unsigned n; + + unsigned block_count = n / BLOCK_SIZE; + + for(unsigned i = 0; i < block_count; i++) + // clang-format off + __CPROVER_loop_invariant(block_count == n / BLOCK_SIZE) + { + __CPROVER_assert(i * BLOCK_SIZE < n, "property 1"); + __CPROVER_assert(i * BLOCK_SIZE + BLOCK_SIZE - 1 < n, "property 2"); + } + // clang-format on +} diff --git a/regression/cprover/loops/block_loop.desc b/regression/cprover/loops/block_loop.desc new file mode 100644 index 00000000000..908056c0ffc --- /dev/null +++ b/regression/cprover/loops/block_loop.desc @@ -0,0 +1,6 @@ +CORE +block_loop.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/loops/copy1.c b/regression/cprover/loops/copy1.c new file mode 100644 index 00000000000..de0924e4ef5 --- /dev/null +++ b/regression/cprover/loops/copy1.c @@ -0,0 +1,22 @@ +int main() +{ + char *input, *output; + __CPROVER_size_t size; + + __CPROVER_assume(__CPROVER_r_ok(input, size)); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(input) == 0); + __CPROVER_assume(__CPROVER_w_ok(output, size)); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(output) == 0); + + for(__CPROVER_size_t i = 0; i < size; i++) + // clang-format off + __CPROVER_loop_invariant(i >= 0 && i <= size) + __CPROVER_loop_invariant(__CPROVER_r_ok(input, size)) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(input) == 0) + __CPROVER_loop_invariant(__CPROVER_w_ok(output, size)) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(output) == 0) + // clang-format on + { + output[i] = input[i]; + } +} diff --git a/regression/cprover/loops/copy1.desc b/regression/cprover/loops/copy1.desc new file mode 100644 index 00000000000..c8902de8fb1 --- /dev/null +++ b/regression/cprover/loops/copy1.desc @@ -0,0 +1,14 @@ +CORE +copy1.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.invariant\.2\] line \d+ loop invariant: SUCCESS$ +^\[main\.invariant\.3\] line \d+ loop invariant: SUCCESS$ +^\[main\.invariant\.4\] line \d+ loop invariant: SUCCESS$ +^\[main\.invariant\.5\] line \d+ loop invariant: SUCCESS$ +^\[main\.pointer\.1\] line \d+ pointer output \+ .*i safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer input \+ .*i safe: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/count_up1.c b/regression/cprover/loops/count_up1.c new file mode 100644 index 00000000000..1911fa1701e --- /dev/null +++ b/regression/cprover/loops/count_up1.c @@ -0,0 +1,18 @@ +_Bool nondet_bool(); + +int main() +{ + int i, j; + + i = j; + + while(nondet_bool()) + { + i++; + j++; + } + + __CPROVER_assert(i == j, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/loops/count_up1.desc b/regression/cprover/loops/count_up1.desc new file mode 100644 index 00000000000..6fc992c6cbd --- /dev/null +++ b/regression/cprover/loops/count_up1.desc @@ -0,0 +1,8 @@ +CORE +count_up1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/count_up2.c b/regression/cprover/loops/count_up2.c new file mode 100644 index 00000000000..013c86c19dc --- /dev/null +++ b/regression/cprover/loops/count_up2.c @@ -0,0 +1,16 @@ +_Bool nondet_bool(); + +int main() +{ + int i, j; + + while(nondet_bool()) + { + i++; + j++; + } + + __CPROVER_assert(i == j, "property 1"); // fails + + return 0; +} diff --git a/regression/cprover/loops/count_up2.desc b/regression/cprover/loops/count_up2.desc new file mode 100644 index 00000000000..e00072ac85d --- /dev/null +++ b/regression/cprover/loops/count_up2.desc @@ -0,0 +1,8 @@ +CORE +count_up2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- +-- diff --git a/regression/cprover/loops/count_up3.c b/regression/cprover/loops/count_up3.c new file mode 100644 index 00000000000..cba62d52c1a --- /dev/null +++ b/regression/cprover/loops/count_up3.c @@ -0,0 +1,21 @@ +_Bool nondet_bool(); + +int main() +{ + unsigned int i, size; + + __CPROVER_assume(size % 2 == 0); + __CPROVER_assume(size < (0u - 2)); + + for(i = 0; i < size; i += 2) + // clang-format off + __CPROVER_loop_invariant( + i >= 0 && i <= size && i % 2 == 0 && size < (0u - 2) && size % 2 == 0) + { + } + // clang-format on + + __CPROVER_assert(i == size, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/loops/count_up3.desc b/regression/cprover/loops/count_up3.desc new file mode 100644 index 00000000000..1e48bf2f89e --- /dev/null +++ b/regression/cprover/loops/count_up3.desc @@ -0,0 +1,9 @@ +CORE +count_up3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/count_up4.c b/regression/cprover/loops/count_up4.c new file mode 100644 index 00000000000..0ae272678fb --- /dev/null +++ b/regression/cprover/loops/count_up4.c @@ -0,0 +1,20 @@ +_Bool nondet_bool(); + +int main() +{ + unsigned int i, j, size; + + __CPROVER_assume(size % 2 == 0); + __CPROVER_assume(size < (0u - 2)); + + j = 0; + for(i = 0; i < size; i += 2, j++) + // clang-format off + __CPROVER_loop_invariant(i >= 0 && i <= size && size < (0u-2) && size % 2 == 0 && i == j * 2) + { + __CPROVER_assert(j < size / 2, "property 1"); + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/count_up4.desc b/regression/cprover/loops/count_up4.desc new file mode 100644 index 00000000000..fec4514e2f4 --- /dev/null +++ b/regression/cprover/loops/count_up4.desc @@ -0,0 +1,9 @@ +CORE +count_up4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/do_while1.c b/regression/cprover/loops/do_while1.c new file mode 100644 index 00000000000..72dce5eb63f --- /dev/null +++ b/regression/cprover/loops/do_while1.c @@ -0,0 +1,15 @@ +int x; + +int main() +{ + x = 10; + + do + { + x = 20; + } while(x != 20); + + __CPROVER_assert(x == 20, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/loops/do_while1.desc b/regression/cprover/loops/do_while1.desc new file mode 100644 index 00000000000..fa38eb3a586 --- /dev/null +++ b/regression/cprover/loops/do_while1.desc @@ -0,0 +1,14 @@ +CORE +do_while1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝x❞:=10\]\)$ +^\(\d+\) ∀ ς : state \. S23T\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. S22in\(ς\) ⇒ S22\(ς\[❝x❞:=20\]\)$ +^\(\d+\) ∀ ς : state . \(S22\(ς\) ∧ ς\(❝x❞\) ≠ 20\) ⇒ S23T\(ς\)$ +^\(\d+\) ∀ ς : state . \(S22\(ς\) ∧ ¬\(ς\(❝x❞\) ≠ 20\)\) ⇒ S23\(ς\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/do_while_false.c b/regression/cprover/loops/do_while_false.c new file mode 100644 index 00000000000..9fa7643d459 --- /dev/null +++ b/regression/cprover/loops/do_while_false.c @@ -0,0 +1,13 @@ +int x; + +int main() +{ + do + { + x = 123; + } while(0); + + __CPROVER_assert(x == 123, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/loops/do_while_false.desc b/regression/cprover/loops/do_while_false.desc new file mode 100644 index 00000000000..52f0db837c9 --- /dev/null +++ b/regression/cprover/loops/do_while_false.desc @@ -0,0 +1,8 @@ +CORE +do_while_false.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/failure1.c b/regression/cprover/loops/failure1.c new file mode 100644 index 00000000000..1baaa64996b --- /dev/null +++ b/regression/cprover/loops/failure1.c @@ -0,0 +1,12 @@ +int x; + +int main() +{ + for(x = 0; x != 1000; x++) + { + // deep failure + __CPROVER_assert(x != 100000, "failing assertion"); + } + + return 0; +} diff --git a/regression/cprover/loops/failure1.desc b/regression/cprover/loops/failure1.desc new file mode 100644 index 00000000000..fc6648680d9 --- /dev/null +++ b/regression/cprover/loops/failure1.desc @@ -0,0 +1,8 @@ +CORE +failure1.c + +^EXIT=6$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ failing assertion: DROPPED$ +-- +-- diff --git a/regression/cprover/loops/for1.c b/regression/cprover/loops/for1.c new file mode 100644 index 00000000000..0cd4b494a3e --- /dev/null +++ b/regression/cprover/loops/for1.c @@ -0,0 +1,11 @@ +int main() +{ + int i; + + for(i = 0; i < 10; i++) + ; + + __CPROVER_assert(i == 10, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/loops/for1.desc b/regression/cprover/loops/for1.desc new file mode 100644 index 00000000000..c9e443cfc46 --- /dev/null +++ b/regression/cprover/loops/for1.desc @@ -0,0 +1,16 @@ +CORE +for1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ ¬\(ς\(❝main::1::i❞\) < 10\)\) ⇒ S22T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ ς\(❝main::1::i❞\) < 10\) ⇒ S22\(ς\)$ +^\(\d+\) S23 = S22$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝main::1::i❞:=ς\(❝main::1::i❞\) \+ 1\]\)$ +^\(\d+\) S25 = S24$ +^\(\d+\) S26 = S22T$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/for2.c b/regression/cprover/loops/for2.c new file mode 100644 index 00000000000..3a78abfd0aa --- /dev/null +++ b/regression/cprover/loops/for2.c @@ -0,0 +1,10 @@ +int main() +{ + for(int x = 0; x != 10; x++) + { + } + + __CPROVER_assert(0, "property 1"); // should fail + + return 0; +} diff --git a/regression/cprover/loops/for2.desc b/regression/cprover/loops/for2.desc new file mode 100644 index 00000000000..8b0f17e74ff --- /dev/null +++ b/regression/cprover/loops/for2.desc @@ -0,0 +1,14 @@ +CORE +for2.c +--text --solve --unwind 10 --inline +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ ¬\(ς\(❝main::1::1::x❞\) ≠ 10\)\) ⇒ S22T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ ς\(❝main::1::1::x❞\) ≠ 10\) ⇒ S22\(ς\)$ +^\(\d+\) S23 = S22$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝main::1::1::x❞:=ς\(❝main::1::1::x❞\) \+ 1\]\)$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- +-- diff --git a/regression/cprover/loops/for_loop1.c b/regression/cprover/loops/for_loop1.c new file mode 100644 index 00000000000..a83b0e31d83 --- /dev/null +++ b/regression/cprover/loops/for_loop1.c @@ -0,0 +1,14 @@ +int x; + +int main() +{ + for(x = 0; x < 100; x++) + { + int z = 123; + __CPROVER_assert( + z >= 0, "property 1"); // true independently of loop entry state + __CPROVER_assert(0, "property 2"); // fails + } + + return 0; +} diff --git a/regression/cprover/loops/for_loop1.desc b/regression/cprover/loops/for_loop1.desc new file mode 100644 index 00000000000..8c15e5606d9 --- /dev/null +++ b/regression/cprover/loops/for_loop1.desc @@ -0,0 +1,8 @@ +CORE +for_loop1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/loops/for_loop2.c b/regression/cprover/loops/for_loop2.c new file mode 100644 index 00000000000..26e60eb247d --- /dev/null +++ b/regression/cprover/loops/for_loop2.c @@ -0,0 +1,16 @@ +int x; + +int main() +{ + for(x = 0; x < 100; x++) + { + __CPROVER_assert(0, "property 1"); // fails + } + + int z = 123; + + // true independently of loop exit state + __CPROVER_assert(z >= 0, "property 2"); + + return 0; +} diff --git a/regression/cprover/loops/for_loop2.desc b/regression/cprover/loops/for_loop2.desc new file mode 100644 index 00000000000..27e9ed5d836 --- /dev/null +++ b/regression/cprover/loops/for_loop2.desc @@ -0,0 +1,8 @@ +CORE +for_loop2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/loops/for_loop3.c b/regression/cprover/loops/for_loop3.c new file mode 100644 index 00000000000..8d2ed0c4fae --- /dev/null +++ b/regression/cprover/loops/for_loop3.c @@ -0,0 +1,14 @@ +int x; + +int main() +{ + for(x = 0; x < 100; x++) + { + __CPROVER_assert(0, "property 1"); // fails + } + + // implied by loop condition + __CPROVER_assert(x >= 100, "property 2"); + + return 0; +} diff --git a/regression/cprover/loops/for_loop3.desc b/regression/cprover/loops/for_loop3.desc new file mode 100644 index 00000000000..625059f4e91 --- /dev/null +++ b/regression/cprover/loops/for_loop3.desc @@ -0,0 +1,8 @@ +CORE +for_loop3.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/loops/forward1.c b/regression/cprover/loops/forward1.c new file mode 100644 index 00000000000..b0542688657 --- /dev/null +++ b/regression/cprover/loops/forward1.c @@ -0,0 +1,12 @@ +int main() +{ + int x = 123; + int y = 0; + + while(1) + { + // forward-propagating x==123 and y==0 suffices + __CPROVER_assert(x != 0, "property 1"); + x += y; + } +} diff --git a/regression/cprover/loops/forward1.desc b/regression/cprover/loops/forward1.desc new file mode 100644 index 00000000000..d260a6ac5ea --- /dev/null +++ b/regression/cprover/loops/forward1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +forward1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/loops/given_invariant1.c b/regression/cprover/loops/given_invariant1.c new file mode 100644 index 00000000000..be05cd35806 --- /dev/null +++ b/regression/cprover/loops/given_invariant1.c @@ -0,0 +1,14 @@ +int x; + +int main() +{ + for(x = 0; x != 100; x++) + // clang-format off + __CPROVER_loop_invariant(x>=0 && x<=100) + { + __CPROVER_assert(x != 1000, "non-inductive invariant"); + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant1.desc b/regression/cprover/loops/given_invariant1.desc new file mode 100644 index 00000000000..d7bc6c1bab4 --- /dev/null +++ b/regression/cprover/loops/given_invariant1.desc @@ -0,0 +1,9 @@ +CORE +given_invariant1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.assertion\.1\] line \d+ non-inductive invariant: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant2.c b/regression/cprover/loops/given_invariant2.c new file mode 100644 index 00000000000..86dab3ccded --- /dev/null +++ b/regression/cprover/loops/given_invariant2.c @@ -0,0 +1,14 @@ +int x; + +int main() +{ + for(x = 0; x != 100; x++) + // clang-format off + __CPROVER_loop_invariant(x>=0 && x<=10) // this is wrong + { + __CPROVER_assert(x != 1000, "non-inductive invariant"); + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant2.desc b/regression/cprover/loops/given_invariant2.desc new file mode 100644 index 00000000000..3b6b5e02c82 --- /dev/null +++ b/regression/cprover/loops/given_invariant2.desc @@ -0,0 +1,9 @@ +CORE +given_invariant2.c + +^EXIT=6$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: DROPPED$ +^\[main\.assertion\.1\] line \d+ non-inductive invariant: DROPPED$ +-- +-- diff --git a/regression/cprover/loops/given_invariant3.c b/regression/cprover/loops/given_invariant3.c new file mode 100644 index 00000000000..ab8ec890e57 --- /dev/null +++ b/regression/cprover/loops/given_invariant3.c @@ -0,0 +1,16 @@ +int x; + +int main() +{ + for(x = 0; x < 100; x++) + // clang-format off + __CPROVER_loop_invariant(x>=0 && x<=100) // should pass + { + // whatnot + } + // clang-format on + + __CPROVER_assert(x == 100, "non-inductive property"); // should pass + + return 0; +} diff --git a/regression/cprover/loops/given_invariant3.desc b/regression/cprover/loops/given_invariant3.desc new file mode 100644 index 00000000000..ed66401ae9f --- /dev/null +++ b/regression/cprover/loops/given_invariant3.desc @@ -0,0 +1,9 @@ +CORE +given_invariant3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.assertion\.1\] line \d+ non-inductive property: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant4.c b/regression/cprover/loops/given_invariant4.c new file mode 100644 index 00000000000..0ad8e73cde2 --- /dev/null +++ b/regression/cprover/loops/given_invariant4.c @@ -0,0 +1,16 @@ +int main() +{ + int i; + + __CPROVER_assume(i >= 0); + + while(i != 10000) + // clang-format off + __CPROVER_loop_invariant(i>=0) // should pass + { + __CPROVER_assert(i >= 0, "property 1"); // should pass + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant4.desc b/regression/cprover/loops/given_invariant4.desc new file mode 100644 index 00000000000..615a9caf612 --- /dev/null +++ b/regression/cprover/loops/given_invariant4.desc @@ -0,0 +1,9 @@ +CORE +given_invariant4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant5.c b/regression/cprover/loops/given_invariant5.c new file mode 100644 index 00000000000..dec1c85a138 --- /dev/null +++ b/regression/cprover/loops/given_invariant5.c @@ -0,0 +1,16 @@ +int x; + +int main() +{ + x = -1; + + for(; x != 100; x++) + // clang-format off + __CPROVER_loop_invariant(x>=0 && x<=100) // fails base case + { + __CPROVER_assert(x < 100, "non-inductive invariant"); + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant5.desc b/regression/cprover/loops/given_invariant5.desc new file mode 100644 index 00000000000..a8fbb9b4f1b --- /dev/null +++ b/regression/cprover/loops/given_invariant5.desc @@ -0,0 +1,9 @@ +CORE +given_invariant5.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: REFUTED$ +^\[main\.assertion\.1\] line \d+ non-inductive invariant: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant6.c b/regression/cprover/loops/given_invariant6.c new file mode 100644 index 00000000000..fc9d0c34317 --- /dev/null +++ b/regression/cprover/loops/given_invariant6.c @@ -0,0 +1,12 @@ +int main() +{ + for(int x = 0; x != 100; x++) + // clang-format off + __CPROVER_loop_invariant(0) // fails base case + { + __CPROVER_assert(0, "property 1"); + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant6.desc b/regression/cprover/loops/given_invariant6.desc new file mode 100644 index 00000000000..cc52774241e --- /dev/null +++ b/regression/cprover/loops/given_invariant6.desc @@ -0,0 +1,9 @@ +CORE +given_invariant6.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +^\[main\.invariant\.1\] line \d+ loop invariant: REFUTED$ +-- +-- diff --git a/regression/cprover/loops/given_invariant7.c b/regression/cprover/loops/given_invariant7.c new file mode 100644 index 00000000000..dbe3ef84b1b --- /dev/null +++ b/regression/cprover/loops/given_invariant7.c @@ -0,0 +1,16 @@ +#define N 100 + +int main() +{ + int array[N]; + + for(int i = 0; i != N; i++) + // clang-format off + __CPROVER_loop_invariant(i >= 0 && i <= N) // passes + { + array[i] = 0; // safe and passes + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant7.desc b/regression/cprover/loops/given_invariant7.desc new file mode 100644 index 00000000000..b9c816c1cfc --- /dev/null +++ b/regression/cprover/loops/given_invariant7.desc @@ -0,0 +1,9 @@ +CORE +given_invariant7.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.array_bounds\.1\] line \d+ array bounds in array\[.*i\]: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant8.c b/regression/cprover/loops/given_invariant8.c new file mode 100644 index 00000000000..2d09d76ddcd --- /dev/null +++ b/regression/cprover/loops/given_invariant8.c @@ -0,0 +1,17 @@ +int main() +{ + int n; + __CPROVER_assume(n >= 0); + int array[n]; + + for(int i = 0; i != n; i++) + // clang-format off + __CPROVER_loop_invariant( + i >= 0 && i <= n && sizeof(array) == sizeof(int) * n) // passes + { + array[i] = 0; // safe and passes + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant8.desc b/regression/cprover/loops/given_invariant8.desc new file mode 100644 index 00000000000..fd06d220c47 --- /dev/null +++ b/regression/cprover/loops/given_invariant8.desc @@ -0,0 +1,9 @@ +CORE +given_invariant8.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.array_bounds\.1\] line \d+ array bounds in array\[.*i\]: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/given_invariant9.c b/regression/cprover/loops/given_invariant9.c new file mode 100644 index 00000000000..32b407a2b97 --- /dev/null +++ b/regression/cprover/loops/given_invariant9.c @@ -0,0 +1,19 @@ +void *malloc(__CPROVER_size_t); + +int main() +{ + int n; + __CPROVER_assume(n >= 0); + int *array = malloc(sizeof(int) * n); + + for(int i = 0; i != n; i++) + // clang-format off + __CPROVER_loop_invariant(i >= 0 && i <= n) + __CPROVER_loop_invariant(__CPROVER_r_ok(array, sizeof(int) * n)) + { + array[i] = 0; // safe and passes + } + // clang-format on + + return 0; +} diff --git a/regression/cprover/loops/given_invariant9.desc b/regression/cprover/loops/given_invariant9.desc new file mode 100644 index 00000000000..b132b12320c --- /dev/null +++ b/regression/cprover/loops/given_invariant9.desc @@ -0,0 +1,10 @@ +CORE +given_invariant9.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.invariant\.1\] line \d+ loop invariant: SUCCESS$ +^\[main\.invariant\.2\] line \d+ loop invariant: SUCCESS$ +^\[main\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/nondet_loop1.c b/regression/cprover/loops/nondet_loop1.c new file mode 100644 index 00000000000..43ec63ff9d5 --- /dev/null +++ b/regression/cprover/loops/nondet_loop1.c @@ -0,0 +1,12 @@ +int nondet_int(); + +int main() +{ + int i; + int array[2]; + + for(i = 0; i < 2; i++) + array[i] = nondet_int(); + + __CPROVER_assert(array[0] == array[1], "property 1"); // fails +} diff --git a/regression/cprover/loops/nondet_loop1.desc b/regression/cprover/loops/nondet_loop1.desc new file mode 100644 index 00000000000..01bedb6f69b --- /dev/null +++ b/regression/cprover/loops/nondet_loop1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +nondet_loop1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/loops/while-break1.c b/regression/cprover/loops/while-break1.c new file mode 100644 index 00000000000..0bee5fd80d3 --- /dev/null +++ b/regression/cprover/loops/while-break1.c @@ -0,0 +1,12 @@ +int main() +{ + int x = 0; + while(1) + { + if(x == 0) + break; + else + break; + } + __CPROVER_assert(x == 0, "property 1"); +} diff --git a/regression/cprover/loops/while-break1.desc b/regression/cprover/loops/while-break1.desc new file mode 100644 index 00000000000..6b8f4c5c8de --- /dev/null +++ b/regression/cprover/loops/while-break1.desc @@ -0,0 +1,17 @@ +CORE +while-break1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S28\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22in\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ ¬\(1 ≠ 0\)\) ⇒ S22T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22in\(ς\) ∧ 1 ≠ 0\) ⇒ S22\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22\(ς\) ∧ ¬\(ς\(❝main::1::x❞\) = 0\)\) ⇒ S23T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22\(ς\) ∧ ς\(❝main::1::x❞\) = 0\) ⇒ S23\(ς\)$ +^\(\d+\) ∀ ς : state \. S22T\(ς\) ⇒ S29in\(ς\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S29in\(ς\)$ +^\(\d+\) ∀ ς : state \. S26\(ς\) ⇒ S29in\(ς\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/while-true1.c b/regression/cprover/loops/while-true1.c new file mode 100644 index 00000000000..9e89c2392f6 --- /dev/null +++ b/regression/cprover/loops/while-true1.c @@ -0,0 +1,8 @@ +int main() +{ + while(1) + { + } + + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/loops/while-true1.desc b/regression/cprover/loops/while-true1.desc new file mode 100644 index 00000000000..407fe011c8c --- /dev/null +++ b/regression/cprover/loops/while-true1.desc @@ -0,0 +1,8 @@ +CORE +while-true1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- +-- diff --git a/regression/cprover/loops/while_loop1.c b/regression/cprover/loops/while_loop1.c new file mode 100644 index 00000000000..37f9f314918 --- /dev/null +++ b/regression/cprover/loops/while_loop1.c @@ -0,0 +1,17 @@ +int x; + +int main() +{ + x = 0; + + while(x < 100) + { + int z = 123; + __CPROVER_assert( + z >= 0, "property 1"); // true independently of loop entry state + __CPROVER_assert(0, "property 2"); // fails + x++; + } + + return 0; +} diff --git a/regression/cprover/loops/while_loop1.desc b/regression/cprover/loops/while_loop1.desc new file mode 100644 index 00000000000..e39f075f692 --- /dev/null +++ b/regression/cprover/loops/while_loop1.desc @@ -0,0 +1,8 @@ +CORE +while_loop1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/loops/while_loop2.c b/regression/cprover/loops/while_loop2.c new file mode 100644 index 00000000000..9414b8696e6 --- /dev/null +++ b/regression/cprover/loops/while_loop2.c @@ -0,0 +1,11 @@ +int nondet_int(); + +int main() +{ + int N = nondet_int(); + int x = 0; + while(x < N) + x++; + + __CPROVER_assert(x == N, "property 1"); // fails +} diff --git a/regression/cprover/loops/while_loop2.desc b/regression/cprover/loops/while_loop2.desc new file mode 100644 index 00000000000..bec032f14db --- /dev/null +++ b/regression/cprover/loops/while_loop2.desc @@ -0,0 +1,7 @@ +CORE +while_loop2.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/pointers/aliasing1.c b/regression/cprover/pointers/aliasing1.c new file mode 100644 index 00000000000..784883d28c1 --- /dev/null +++ b/regression/cprover/pointers/aliasing1.c @@ -0,0 +1,12 @@ +int main(void) +{ + int *p; + __CPROVER_assume(__CPROVER_rw_ok(p)); + + int i; + __CPROVER_assert(__CPROVER_rw_ok(p), "property 1"); // should pass + + p = &i; // take address of i + + return 0; +} diff --git a/regression/cprover/pointers/aliasing1.desc b/regression/cprover/pointers/aliasing1.desc new file mode 100644 index 00000000000..212796a26ba --- /dev/null +++ b/regression/cprover/pointers/aliasing1.desc @@ -0,0 +1,7 @@ +CORE +aliasing1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/aliasing2.c b/regression/cprover/pointers/aliasing2.c new file mode 100644 index 00000000000..a0f9636d388 --- /dev/null +++ b/regression/cprover/pointers/aliasing2.c @@ -0,0 +1,12 @@ +int main() +{ + int *in, *out; + __CPROVER_assume(__CPROVER_rw_ok(in) && __CPROVER_rw_ok(out)); + __CPROVER_assume(!__CPROVER_same_object(in, out)); + int old_in = *in; + + *out = *in; + *in = 0; // overwrite + + __CPROVER_assert(*out == old_in, "property 1"); +} diff --git a/regression/cprover/pointers/aliasing2.desc b/regression/cprover/pointers/aliasing2.desc new file mode 100644 index 00000000000..f70c642850b --- /dev/null +++ b/regression/cprover/pointers/aliasing2.desc @@ -0,0 +1,7 @@ +CORE +aliasing2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/char_pointers1.c b/regression/cprover/pointers/char_pointers1.c new file mode 100644 index 00000000000..bca51e72289 --- /dev/null +++ b/regression/cprover/pointers/char_pointers1.c @@ -0,0 +1,10 @@ +int main() +{ + int x; + char *p = (char *)&x; + p[0] = 0; + p[1] = 0; + p[2] = 0; + p[3] = 0; + __CPROVER_assert(x == 0, "property 1"); +} diff --git a/regression/cprover/pointers/char_pointers1.desc b/regression/cprover/pointers/char_pointers1.desc new file mode 100644 index 00000000000..d41af4f1c84 --- /dev/null +++ b/regression/cprover/pointers/char_pointers1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +char_pointers1.c +--text --solve +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function main is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/char_pointers2.c b/regression/cprover/pointers/char_pointers2.c new file mode 100644 index 00000000000..6f4bcad1539 --- /dev/null +++ b/regression/cprover/pointers/char_pointers2.c @@ -0,0 +1,12 @@ +int main() +{ + int x = 0x01020304; + char *p = (char *)&x; + + // passes on little endinan + __CPROVER_assert(p[0] == 0x04, "property 1"); + __CPROVER_assert(p[1] == 0x03, "property 2"); + __CPROVER_assert(p[2] == 0x02, "property 3"); + __CPROVER_assert(p[3] == 0x01, "property 4"); + return 0; +} diff --git a/regression/cprover/pointers/char_pointers2.desc b/regression/cprover/pointers/char_pointers2.desc new file mode 100644 index 00000000000..155a35cc6c1 --- /dev/null +++ b/regression/cprover/pointers/char_pointers2.desc @@ -0,0 +1,20 @@ +KNOWNBUG +char_pointers2.c +--text --solve +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝main::1::x❞:=16909060\]\)$ +^\(\d+\) S22 = S21$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::p❞:=cast\(❝main::1::x❞, signedbv\[8\]\*\)\]\) +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(cast\(ς\(ς\(❝main::1::p❞\) \+ cast\(0, signedbv\[64\]\)\), signedbv\[32\]\) = 4\)$ +^\(\d+\) S24 = S23$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(cast\(ς\(ς\(❝main::1::p❞\) \+ cast\(1, signedbv\[64\]\)\), signedbv\[32\]\) = 3\)$ +^\(\d+\) S25 = S24$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(cast\(ς\(ς\(❝main::1::p❞\) \+ cast\(2, signedbv\[64\]\)\), signedbv\[32\]\) = 2\)$ +^\(\d+\) S26 = S25$ +^\(\d+\) ∀ ς : state \. S26\(ς\) ⇒ \(cast\(ς\(ς\(❝main::1::p❞\) \+ cast\(3, signedbv\[64\]\)\), signedbv\[32\]\) = 1\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +^\[main\.assertion\.4\] line \d+ property 4: SUCCESS$ +-- diff --git a/regression/cprover/pointers/const1.c b/regression/cprover/pointers/const1.c new file mode 100644 index 00000000000..d4f7222e9cb --- /dev/null +++ b/regression/cprover/pointers/const1.c @@ -0,0 +1,9 @@ +const int months = 12; + +int main() +{ + char *p; + __CPROVER_assume(__CPROVER_w_ok(p, 1)); + *p = 123; // should not alias 'months', as we are writing + __CPROVER_assert(months == 12, "property 1"); +} diff --git a/regression/cprover/pointers/const1.desc b/regression/cprover/pointers/const1.desc new file mode 100644 index 00000000000..82f1e868d70 --- /dev/null +++ b/regression/cprover/pointers/const1.desc @@ -0,0 +1,7 @@ +CORE +const1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/const2.c b/regression/cprover/pointers/const2.c new file mode 100644 index 00000000000..28ad9094d98 --- /dev/null +++ b/regression/cprover/pointers/const2.c @@ -0,0 +1,8 @@ +const int months = 12; + +int main() +{ + char *p; + __CPROVER_assume(__CPROVER_WRITEABLE_OBJECT(p)); + __CPROVER_assert(!__CPROVER_same_object(p, &months), "property 1"); // passes +} diff --git a/regression/cprover/pointers/const2.desc b/regression/cprover/pointers/const2.desc new file mode 100644 index 00000000000..64826994747 --- /dev/null +++ b/regression/cprover/pointers/const2.desc @@ -0,0 +1,7 @@ +CORE +const2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/disjoint1.c b/regression/cprover/pointers/disjoint1.c new file mode 100644 index 00000000000..23762074ccc --- /dev/null +++ b/regression/cprover/pointers/disjoint1.c @@ -0,0 +1,12 @@ +int unrelated = 123; + +int main() +{ + char *output; + + __CPROVER_assume(__CPROVER_w_ok(output)); + __CPROVER_assume(!__CPROVER_same_object(output, &unrelated)); + *output = 'x'; + + __CPROVER_assert(unrelated == 123, "property 1"); +} diff --git a/regression/cprover/pointers/disjoint1.desc b/regression/cprover/pointers/disjoint1.desc new file mode 100644 index 00000000000..72791ffa328 --- /dev/null +++ b/regression/cprover/pointers/disjoint1.desc @@ -0,0 +1,7 @@ +CORE +disjoint1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/hex_encode.c b/regression/cprover/pointers/hex_encode.c new file mode 100644 index 00000000000..83c62570796 --- /dev/null +++ b/regression/cprover/pointers/hex_encode.c @@ -0,0 +1,43 @@ +const char *HEX_CHARS = "0123456789abcdef"; + +int main() +{ + __CPROVER_size_t input_size; + unsigned char *input; + char *output; + + __CPROVER_assume(input_size < 100); + __CPROVER_assume(__CPROVER_r_ok(input, input_size)); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(input) == 0); + __CPROVER_assume(__CPROVER_w_ok(output, input_size * 2)); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(output) == 0); + + __CPROVER_assert(__CPROVER_r_ok(HEX_CHARS, 16), "property 1"); + __CPROVER_assert(__CPROVER_POINTER_OFFSET(HEX_CHARS) == 0, "property 2"); + + __CPROVER_assume(!__CPROVER_same_object(&HEX_CHARS, output)); + + __CPROVER_size_t written = 0; + for(__CPROVER_size_t i = 0; i < input_size; ++i) + // clang-format off + __CPROVER_loop_invariant(input_size < 100) + __CPROVER_loop_invariant(i >= 0 && i <= input_size) + __CPROVER_loop_invariant(__CPROVER_r_ok(input, input_size)) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(input) == 0) + __CPROVER_loop_invariant(__CPROVER_r_ok(HEX_CHARS, 16)) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(HEX_CHARS) == 0) + __CPROVER_loop_invariant(written == i * 2) + __CPROVER_loop_invariant(__CPROVER_w_ok(output, input_size * 2)) + __CPROVER_loop_invariant(__CPROVER_POINTER_OFFSET(output) == 0) + __CPROVER_loop_invariant(!__CPROVER_same_object(&HEX_CHARS, output)) + // clang-format on + { + unsigned char ch = input[i]; + char hex0 = HEX_CHARS[ch >> 4 & 0x0f]; + char hex1 = HEX_CHARS[ch & 0x0f]; + output[written++] = hex0; + output[written++] = hex1; + } + + return 0; +} diff --git a/regression/cprover/pointers/hex_encode.desc b/regression/cprover/pointers/hex_encode.desc new file mode 100644 index 00000000000..58dee5c5422 --- /dev/null +++ b/regression/cprover/pointers/hex_encode.desc @@ -0,0 +1,6 @@ +CORE +hex_encode.c +--safety +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/pointers/local1.c b/regression/cprover/pointers/local1.c new file mode 100644 index 00000000000..ce19f07d07a --- /dev/null +++ b/regression/cprover/pointers/local1.c @@ -0,0 +1,12 @@ +void myfunc(int *p) +{ + int x = 1; + *p = 2; // can't point to x + __CPROVER_assert(x == 1, "property 1"); +} + +int main() +{ + int *p; + myfunc(p); +} diff --git a/regression/cprover/pointers/local1.desc b/regression/cprover/pointers/local1.desc new file mode 100644 index 00000000000..bef9b60154c --- /dev/null +++ b/regression/cprover/pointers/local1.desc @@ -0,0 +1,7 @@ +CORE +malloc1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/malloc1.c b/regression/cprover/pointers/malloc1.c new file mode 100644 index 00000000000..d4ae4d03f61 --- /dev/null +++ b/regression/cprover/pointers/malloc1.c @@ -0,0 +1,11 @@ +void *malloc(__CPROVER_size_t); + +int *p; + +int main() +{ + p = malloc(sizeof(int) * 10); + p[2] = 123; + __CPROVER_assert(p[2] == 123, "property 1"); + return 0; +} diff --git a/regression/cprover/pointers/malloc1.desc b/regression/cprover/pointers/malloc1.desc new file mode 100644 index 00000000000..775ddcbbaf9 --- /dev/null +++ b/regression/cprover/pointers/malloc1.desc @@ -0,0 +1,8 @@ +CORE +malloc1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝main::\$tmp::return_value_malloc❞:=allocate\(ς, 4 \* cast\(10, unsignedbv\[64\]\)\)\]\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/malloc2.c b/regression/cprover/pointers/malloc2.c new file mode 100644 index 00000000000..f75ecf28c39 --- /dev/null +++ b/regression/cprover/pointers/malloc2.c @@ -0,0 +1,12 @@ +void *malloc(__CPROVER_size_t); + +int *p; + +#define N (sizeof(int) * 10) + +int main() +{ + p = malloc(N); + __CPROVER_assert(__CPROVER_OBJECT_SIZE(p) == N, "property 1"); + return 0; +} diff --git a/regression/cprover/pointers/malloc2.desc b/regression/cprover/pointers/malloc2.desc new file mode 100644 index 00000000000..997b9889fd5 --- /dev/null +++ b/regression/cprover/pointers/malloc2.desc @@ -0,0 +1,7 @@ +CORE +malloc2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/malloc3.c b/regression/cprover/pointers/malloc3.c new file mode 100644 index 00000000000..102bdbe9e23 --- /dev/null +++ b/regression/cprover/pointers/malloc3.c @@ -0,0 +1,9 @@ +void *malloc(__CPROVER_size_t); +void free(void *); + +int main() +{ + int *p = malloc(sizeof(int)); + int *q = malloc(sizeof(int)); + __CPROVER_assert(!__CPROVER_same_object(p, q), "property 1"); // should pass +} diff --git a/regression/cprover/pointers/malloc3.desc b/regression/cprover/pointers/malloc3.desc new file mode 100644 index 00000000000..4664edc4a3d --- /dev/null +++ b/regression/cprover/pointers/malloc3.desc @@ -0,0 +1,7 @@ +KNOWNBUG +malloc3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_array1.c b/regression/cprover/pointers/pointer_array1.c new file mode 100644 index 00000000000..af0bc6f92df --- /dev/null +++ b/regression/cprover/pointers/pointer_array1.c @@ -0,0 +1,12 @@ +int some_int = 123; +void *ptr_array[10] = {&some_int}; + +int main() +{ + __CPROVER_assert(ptr_array[0] == &some_int, "property 1"); + + void **array_pointer = ptr_array; + + __CPROVER_assert(array_pointer[0] == &some_int, "property 2"); + __CPROVER_assert(*((int *)(array_pointer[0])) == 123, "property 3"); +} diff --git a/regression/cprover/pointers/pointer_array1.desc b/regression/cprover/pointers/pointer_array1.desc new file mode 100644 index 00000000000..78ec08c4c6e --- /dev/null +++ b/regression/cprover/pointers/pointer_array1.desc @@ -0,0 +1,9 @@ +CORE +pointer_array1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_into_struct1.c b/regression/cprover/pointers/pointer_into_struct1.c new file mode 100644 index 00000000000..f8e997edb44 --- /dev/null +++ b/regression/cprover/pointers/pointer_into_struct1.c @@ -0,0 +1,12 @@ +struct my_struct +{ + int x, y; +} S = {1, 2}; + +int main() +{ + int *p = &S.y; + int my_struct_data = *p; + __CPROVER_assert(my_struct_data == 2, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/pointer_into_struct1.desc b/regression/cprover/pointers/pointer_into_struct1.desc new file mode 100644 index 00000000000..6636ca99e7a --- /dev/null +++ b/regression/cprover/pointers/pointer_into_struct1.desc @@ -0,0 +1,7 @@ +CORE +pointer_into_struct1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_logic1.c b/regression/cprover/pointers/pointer_logic1.c new file mode 100644 index 00000000000..df8a3cfa547 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic1.c @@ -0,0 +1,14 @@ +int main() +{ + char *p; + __CPROVER_size_t offset; + + p = (char *)0 + offset; + + __CPROVER_assert( + __CPROVER_POINTER_OBJECT(p) == 0, "property 1"); // should pass + __CPROVER_assert( + __CPROVER_POINTER_OFFSET(p) == offset, "property 2"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointer_logic1.desc b/regression/cprover/pointers/pointer_logic1.desc new file mode 100644 index 00000000000..52e5efce3b3 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic1.desc @@ -0,0 +1,8 @@ +CORE +pointer_logic1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_logic2.c b/regression/cprover/pointers/pointer_logic2.c new file mode 100644 index 00000000000..75153481217 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic2.c @@ -0,0 +1,16 @@ +int main() +{ + int x; + char *p; + __CPROVER_size_t offset; + + p = ((char *)&x) + offset; + + __CPROVER_assert( + __CPROVER_POINTER_OBJECT(p) == __CPROVER_POINTER_OBJECT(&x), + "property 1"); // should pass + __CPROVER_assert( + __CPROVER_POINTER_OFFSET(p) == offset, "property 2"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointer_logic2.desc b/regression/cprover/pointers/pointer_logic2.desc new file mode 100644 index 00000000000..42e28455323 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic2.desc @@ -0,0 +1,8 @@ +CORE +pointer_logic2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_logic3.c b/regression/cprover/pointers/pointer_logic3.c new file mode 100644 index 00000000000..eed04791b48 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic3.c @@ -0,0 +1,19 @@ +int main() +{ + char *base, *p; + __CPROVER_size_t size, offset; + + __CPROVER_assume(__CPROVER_r_ok(base, size)); + + if(offset < size) + { + p = base + offset; + + __CPROVER_assert( + __CPROVER_same_object(base, p), "property 1"); // should pass + __CPROVER_assert(base <= p, "property 2"); // should pass + __CPROVER_assert(p < base + size, "property 3"); // should pass + } + + return 0; +} diff --git a/regression/cprover/pointers/pointer_logic3.desc b/regression/cprover/pointers/pointer_logic3.desc new file mode 100644 index 00000000000..4730793ce71 --- /dev/null +++ b/regression/cprover/pointers/pointer_logic3.desc @@ -0,0 +1,9 @@ +CORE +pointer_logic3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_to_local1.c b/regression/cprover/pointers/pointer_to_local1.c new file mode 100644 index 00000000000..d59132f78dc --- /dev/null +++ b/regression/cprover/pointers/pointer_to_local1.c @@ -0,0 +1,8 @@ +void foo(int *p) __CPROVER_requires(__CPROVER_w_ok(p)) __CPROVER_assigns(*p) +{ + int i; + + i = 123; + *p = 456; // p cannot point to i as i is local + __CPROVER_assert(i == 123, "property 1"); // should pass +} diff --git a/regression/cprover/pointers/pointer_to_local1.desc b/regression/cprover/pointers/pointer_to_local1.desc new file mode 100644 index 00000000000..12c2dec529a --- /dev/null +++ b/regression/cprover/pointers/pointer_to_local1.desc @@ -0,0 +1,7 @@ +CORE +pointer_to_local1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[foo\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointer_to_pointer1.c b/regression/cprover/pointers/pointer_to_pointer1.c new file mode 100644 index 00000000000..1ee82851c3c --- /dev/null +++ b/regression/cprover/pointers/pointer_to_pointer1.c @@ -0,0 +1,12 @@ +#include + +int *p; +void **p_ptr; + +int main() +{ + p = malloc(1); + p_ptr = &p; + __CPROVER_assert(__CPROVER_LIVE_OBJECT(*p_ptr), "property 1"); + return 0; +} diff --git a/regression/cprover/pointers/pointer_to_pointer1.desc b/regression/cprover/pointers/pointer_to_pointer1.desc new file mode 100644 index 00000000000..ed67f31ef59 --- /dev/null +++ b/regression/cprover/pointers/pointer_to_pointer1.desc @@ -0,0 +1,7 @@ +CORE +pointer_to_pointer1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers0.c b/regression/cprover/pointers/pointers0.c new file mode 100644 index 00000000000..421dcdc2a26 --- /dev/null +++ b/regression/cprover/pointers/pointers0.c @@ -0,0 +1,7 @@ +int main() +{ + int x, *p; + p = &x; + __CPROVER_assert(*p == x, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/pointers0.desc b/regression/cprover/pointers/pointers0.desc new file mode 100644 index 00000000000..9ce4f642078 --- /dev/null +++ b/regression/cprover/pointers/pointers0.desc @@ -0,0 +1,9 @@ +CORE +pointers0.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝main::1::p❞:=❝main::1::x❞\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(ς\(❝main::1::p❞\)\) = ς\(❝main::1::x❞\)\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers1.c b/regression/cprover/pointers/pointers1.c new file mode 100644 index 00000000000..3dab14e327e --- /dev/null +++ b/regression/cprover/pointers/pointers1.c @@ -0,0 +1,11 @@ +int x, y, *p; + +int main() +{ + x = 10; + p = &x; + y = *p; + __CPROVER_assert(y == 10, "property 1"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointers1.desc b/regression/cprover/pointers/pointers1.desc new file mode 100644 index 00000000000..46ccbfe4633 --- /dev/null +++ b/regression/cprover/pointers/pointers1.desc @@ -0,0 +1,11 @@ +CORE +pointers1.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝x❞:=10\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝p❞:=❝x❞\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S25\(ς\[❝y❞:=ς\(ς\(❝p❞\)\)\]\)$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(❝y❞\) = 10\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers10.c b/regression/cprover/pointers/pointers10.c new file mode 100644 index 00000000000..ceac0e98c79 --- /dev/null +++ b/regression/cprover/pointers/pointers10.c @@ -0,0 +1,13 @@ +int main() +{ + int *p; // unconstrained + int x; + + if(p == &x) // address taken + { + *p = 123; + __CPROVER_assert(x == 123, "property 1"); // should pass + } + + return 0; +} diff --git a/regression/cprover/pointers/pointers10.desc b/regression/cprover/pointers/pointers10.desc new file mode 100644 index 00000000000..9bd77e62df5 --- /dev/null +++ b/regression/cprover/pointers/pointers10.desc @@ -0,0 +1,7 @@ +CORE +pointers10.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers11.c b/regression/cprover/pointers/pointers11.c new file mode 100644 index 00000000000..e4f3e8a94f7 --- /dev/null +++ b/regression/cprover/pointers/pointers11.c @@ -0,0 +1,7 @@ +int main() +{ + int *p; + *p = 123; + __CPROVER_assert(*p == 123, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/pointers11.desc b/regression/cprover/pointers/pointers11.desc new file mode 100644 index 00000000000..c85aca8cc9b --- /dev/null +++ b/regression/cprover/pointers/pointers11.desc @@ -0,0 +1,7 @@ +CORE +pointers11.c +--no-safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers12.c b/regression/cprover/pointers/pointers12.c new file mode 100644 index 00000000000..9d4e1ae6dfa --- /dev/null +++ b/regression/cprover/pointers/pointers12.c @@ -0,0 +1,9 @@ +int x = 123; + +int main() +{ + int *p; // might alias x + *p = 456; + __CPROVER_assert(x == 123, "property 1"); // should fail + return 0; +} diff --git a/regression/cprover/pointers/pointers12.desc b/regression/cprover/pointers/pointers12.desc new file mode 100644 index 00000000000..b7bdbdb0f21 --- /dev/null +++ b/regression/cprover/pointers/pointers12.desc @@ -0,0 +1,7 @@ +CORE +pointers12.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/pointers/pointers2.c b/regression/cprover/pointers/pointers2.c new file mode 100644 index 00000000000..c12967179c7 --- /dev/null +++ b/regression/cprover/pointers/pointers2.c @@ -0,0 +1,12 @@ +int x, *p; + +int main() +{ + x = 10; + p = &x; + *p = 20; + __CPROVER_assert(x == 20, "property 1"); // passes + __CPROVER_assert(x == 10, "property 2"); // fails + + return 0; +} diff --git a/regression/cprover/pointers/pointers2.desc b/regression/cprover/pointers/pointers2.desc new file mode 100644 index 00000000000..b39c93b79b2 --- /dev/null +++ b/regression/cprover/pointers/pointers2.desc @@ -0,0 +1,14 @@ +CORE +pointers2.c +--text --solve --inline --no-safety +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝x❞:=10\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝p❞:=❝x❞\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[ς\(❝p❞\):=20\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ \(ς\(❝x❞\) = 20\)$ +^\(\d+\) S25 = S24$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(❝x❞\) = 10\)$ +^\[main\.assertion\.1] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/pointers/pointers3.c b/regression/cprover/pointers/pointers3.c new file mode 100644 index 00000000000..06bc93e3beb --- /dev/null +++ b/regression/cprover/pointers/pointers3.c @@ -0,0 +1,6 @@ +int main() +{ + int *p; + __CPROVER_assume(*p == 10); + __CPROVER_assert(*p == 10, "property 1"); // passes +} diff --git a/regression/cprover/pointers/pointers3.desc b/regression/cprover/pointers/pointers3.desc new file mode 100644 index 00000000000..b8b51405977 --- /dev/null +++ b/regression/cprover/pointers/pointers3.desc @@ -0,0 +1,9 @@ +CORE +pointers3.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. \(S20\(ς\) ∧ ς\(ς\(❝main::1::p❞\)\) = 10\) ⇒ S21\(ς\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ \(ς\(ς\(❝main::1::p❞\)\) = 10\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers4.c b/regression/cprover/pointers/pointers4.c new file mode 100644 index 00000000000..05e396d25bd --- /dev/null +++ b/regression/cprover/pointers/pointers4.c @@ -0,0 +1,12 @@ +int x; + +int main() +{ + int *p; + __CPROVER_assume(*p == 10); + p = &x; + // not provable, since p may have pointed elsewhere + __CPROVER_assert(*p == 10, "property 1"); + + return 0; +} diff --git a/regression/cprover/pointers/pointers4.desc b/regression/cprover/pointers/pointers4.desc new file mode 100644 index 00000000000..1569fe88769 --- /dev/null +++ b/regression/cprover/pointers/pointers4.desc @@ -0,0 +1,11 @@ +CORE +pointers4.c +--text --solve --inline --no-safety +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝x❞:=0\]\)$ +^\(\d+\) ∀ ς : state \. \(S21\(ς\) ∧ ς\(ς\(❝main::1::p❞\)\) = 10\) ⇒ S22\(ς\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::p❞:=❝x❞\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(ς\(❝main::1::p❞\)\) = 10\)$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +-- diff --git a/regression/cprover/pointers/pointers5.c b/regression/cprover/pointers/pointers5.c new file mode 100644 index 00000000000..d90530ca3a8 --- /dev/null +++ b/regression/cprover/pointers/pointers5.c @@ -0,0 +1,11 @@ +int x; + +int main() +{ + int *p, *q; + + __CPROVER_assume(p == q); + __CPROVER_assert(*p == *q, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/pointers/pointers5.desc b/regression/cprover/pointers/pointers5.desc new file mode 100644 index 00000000000..453b44b3ea6 --- /dev/null +++ b/regression/cprover/pointers/pointers5.desc @@ -0,0 +1,9 @@ +CORE +pointers5.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. \(S22\(ς\) ∧ ς\(❝main::1::p❞\) = ς\(❝main::1::q❞\)\) ⇒ S23\(ς\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(ς\(❝main::1::p❞\)\) = ς\(ς\(❝main::1::q❞\)\)\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers6.c b/regression/cprover/pointers/pointers6.c new file mode 100644 index 00000000000..3461a8c87b4 --- /dev/null +++ b/regression/cprover/pointers/pointers6.c @@ -0,0 +1,12 @@ +int x; + +int main() +{ + int *p, *q; + p = &x; + *p = 10; + q = &x; + __CPROVER_assert(*q == 10, "property 1"); // passes + + return 0; +} diff --git a/regression/cprover/pointers/pointers6.desc b/regression/cprover/pointers/pointers6.desc new file mode 100644 index 00000000000..35c1dff9a5b --- /dev/null +++ b/regression/cprover/pointers/pointers6.desc @@ -0,0 +1,11 @@ +CORE +pointers6.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::p❞:=❝x❞\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[ς\(❝main::1::p❞\):=10\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S25\(ς\[❝main::1::q❞:=❝x❞\]\)$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(ς\(❝main::1::q❞\)\) = 10\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers7.c b/regression/cprover/pointers/pointers7.c new file mode 100644 index 00000000000..0e1a9d92add --- /dev/null +++ b/regression/cprover/pointers/pointers7.c @@ -0,0 +1,11 @@ +int x, y, *p; + +int main() +{ + x = 10; + p = &y; + *p = 20; // unrelated to 'x' + __CPROVER_assert(x == 10, "property 1"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointers7.desc b/regression/cprover/pointers/pointers7.desc new file mode 100644 index 00000000000..6f18eb76bfb --- /dev/null +++ b/regression/cprover/pointers/pointers7.desc @@ -0,0 +1,16 @@ +CORE +pointers7.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S17\(ς\) ⇒ S18\(ς\[❝p❞:=NULL\]\)$ +^\(\d+\) ∀ ς : state \. S18\(ς\) ⇒ S19\(ς\[❝x❞:=0\]\)$ +^\(\d+\) ∀ ς : state \. S19\(ς\) ⇒ S20\(ς\[❝y❞:=0\]\)$ +^\(\d+\) S21 = S20$ +^\(\d+\) S22 = S21$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝x❞:=10\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ S24\(ς\[❝p❞:=❝y❞\]\)$ +^\(\d+\) ∀ ς : state \. S24\(ς\) ⇒ S25\(ς\[ς\(❝p❞\):=20\]\)$ +^\(\d+\) ∀ ς : state \. S25\(ς\) ⇒ \(ς\(❝x❞\) = 10\)$ +^\[main\.assertion\.1\] line \d property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers8.c b/regression/cprover/pointers/pointers8.c new file mode 100644 index 00000000000..e2a62ea7efa --- /dev/null +++ b/regression/cprover/pointers/pointers8.c @@ -0,0 +1,11 @@ +int main() +{ + int *p; // unconstrained, might point to itself! + + //__CPROVER_assume(!__CPROVER_same_object(p, &p)); + *p = 123; // can't point to itself + int p_value = *p; + __CPROVER_assert(p_value == 123, "property 1"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointers8.desc b/regression/cprover/pointers/pointers8.desc new file mode 100644 index 00000000000..c56a19d3e73 --- /dev/null +++ b/regression/cprover/pointers/pointers8.desc @@ -0,0 +1,10 @@ +CORE +pointers8.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[ς\(❝main::1::p❞\):=123\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::p_value❞:=ς\(ς\(❝main::1::p❞\)\)\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(❝main::1::p_value❞\) = 123\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/pointers9.c b/regression/cprover/pointers/pointers9.c new file mode 100644 index 00000000000..11bb1df48a2 --- /dev/null +++ b/regression/cprover/pointers/pointers9.c @@ -0,0 +1,10 @@ +int main() +{ + int *p; // unconstrained + int x = 123; + + if(p == &x) + __CPROVER_assert(*p == 123, "property 1"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/pointers9.desc b/regression/cprover/pointers/pointers9.desc new file mode 100644 index 00000000000..aa097d00389 --- /dev/null +++ b/regression/cprover/pointers/pointers9.desc @@ -0,0 +1,11 @@ +CORE +pointers9.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝main::1::x❞:=123\]\)$ +^\(\d+\) ∀ ς : state \. \(S22\(ς\) ∧ ¬\(ς\(❝main::1::p❞\) = ❝main::1::x❞\)\) ⇒ S23T\(ς\)$ +^\(\d+\) ∀ ς : state \. \(S22\(ς\) ∧ ς\(❝main::1::p❞\) = ❝main::1::x❞\) ⇒ S23\(ς\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(ς\(❝main::1::p❞\)\) = 123\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/posix_memalign1.c b/regression/cprover/pointers/posix_memalign1.c new file mode 100644 index 00000000000..07309a88bd5 --- /dev/null +++ b/regression/cprover/pointers/posix_memalign1.c @@ -0,0 +1,9 @@ +int posix_memalign(void **, __CPROVER_size_t, __CPROVER_size_t); + +int main() +{ + void *ptr; + posix_memalign(&ptr, sizeof(void *), sizeof(int)); + __CPROVER_assert(__CPROVER_r_ok(ptr, sizeof(int)), "property 1"); + return 0; +} diff --git a/regression/cprover/pointers/posix_memalign1.desc b/regression/cprover/pointers/posix_memalign1.desc new file mode 100644 index 00000000000..36bf94ef3d8 --- /dev/null +++ b/regression/cprover/pointers/posix_memalign1.desc @@ -0,0 +1,7 @@ +CORE +posix_memalign1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer1.c b/regression/cprover/pointers/struct_pointer1.c new file mode 100644 index 00000000000..7c19aabe93e --- /dev/null +++ b/regression/cprover/pointers/struct_pointer1.c @@ -0,0 +1,14 @@ +struct my_struct +{ + int data; +}; + +int main() +{ + struct my_struct *my_struct_ptr; // not constrained + // __CPROVER_assume(!__CPROVER_same_object(my_struct_ptr, &my_struct_ptr)); + my_struct_ptr->data = 123; + int my_struct_data = my_struct_ptr->data; + __CPROVER_assert(my_struct_data == 123, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/struct_pointer1.desc b/regression/cprover/pointers/struct_pointer1.desc new file mode 100644 index 00000000000..8582daf8386 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer1.desc @@ -0,0 +1,10 @@ +CORE +struct_pointer1.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[ς\(❝main::1::my_struct_ptr❞\)\.❝data❞:=123\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[❝main::1::my_struct_data❞:=ς\(ς\(❝main::1::my_struct_ptr❞\)\.❝data❞\)\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(❝main::1::my_struct_data❞\) = 123\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer2.c b/regression/cprover/pointers/struct_pointer2.c new file mode 100644 index 00000000000..a6f85af0190 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer2.c @@ -0,0 +1,15 @@ +struct my_struct +{ + int data; +}; + +int main() +{ + struct my_struct my_struct; + struct my_struct *my_struct_ptr = &my_struct; + + my_struct_ptr->data = 123; + __CPROVER_assert(my_struct.data == 123, "property 1"); // should pass + + return 0; +} diff --git a/regression/cprover/pointers/struct_pointer2.desc b/regression/cprover/pointers/struct_pointer2.desc new file mode 100644 index 00000000000..620e0f42d4a --- /dev/null +++ b/regression/cprover/pointers/struct_pointer2.desc @@ -0,0 +1,10 @@ +CORE +struct_pointer2.c +--text --solve --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝main::1::my_struct_ptr❞:=❝main::1::my_struct❞\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ S23\(ς\[ς\(❝main::1::my_struct_ptr❞\)\.❝data❞:=123\]\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(❝main::1::my_struct❞\.❝data❞\) = 123\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer3.c b/regression/cprover/pointers/struct_pointer3.c new file mode 100644 index 00000000000..139ed6ca101 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer3.c @@ -0,0 +1,12 @@ +struct S +{ + int x, y; +}; + +int main() +{ + struct S a; + struct S *p = &a; + __CPROVER_assert(p->x == a.x, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/struct_pointer3.desc b/regression/cprover/pointers/struct_pointer3.desc new file mode 100644 index 00000000000..d67a291119d --- /dev/null +++ b/regression/cprover/pointers/struct_pointer3.desc @@ -0,0 +1,7 @@ +CORE +struct_pointer3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer4.c b/regression/cprover/pointers/struct_pointer4.c new file mode 100644 index 00000000000..b5acec0331e --- /dev/null +++ b/regression/cprover/pointers/struct_pointer4.c @@ -0,0 +1,13 @@ +struct S +{ + int x, y; +}; + +int main() +{ + struct S a, b; + struct S *p = &a; + *p = b; // copy entire struct + __CPROVER_assert(a.x == b.x, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/struct_pointer4.desc b/regression/cprover/pointers/struct_pointer4.desc new file mode 100644 index 00000000000..1c94169c51c --- /dev/null +++ b/regression/cprover/pointers/struct_pointer4.desc @@ -0,0 +1,7 @@ +CORE +struct_pointer4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer5.c b/regression/cprover/pointers/struct_pointer5.c new file mode 100644 index 00000000000..0ce42304ea6 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer5.c @@ -0,0 +1,13 @@ +struct S +{ + int x, y; +}; + +int main() +{ + struct S a; + int *p = &a.y; + *p = 123; + __CPROVER_assert(a.y == 123, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/pointers/struct_pointer5.desc b/regression/cprover/pointers/struct_pointer5.desc new file mode 100644 index 00000000000..7a974b8a34b --- /dev/null +++ b/regression/cprover/pointers/struct_pointer5.desc @@ -0,0 +1,7 @@ +CORE +struct_pointer5.c +--no-safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/pointers/struct_pointer6.c b/regression/cprover/pointers/struct_pointer6.c new file mode 100644 index 00000000000..17714e09766 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer6.c @@ -0,0 +1,16 @@ +struct buf +{ + __CPROVER_size_t len; +}; + +int main() +{ + struct buf *b; + __CPROVER_assume(__CPROVER_r_ok(b)); // must not alias with x + __CPROVER_assume(b->len == 456); + + __CPROVER_size_t x, *size_t_ptr = &x; + *size_t_ptr = 123; + + __CPROVER_assert(b->len == 456, "property 1"); +} diff --git a/regression/cprover/pointers/struct_pointer6.desc b/regression/cprover/pointers/struct_pointer6.desc new file mode 100644 index 00000000000..dcd12aacda4 --- /dev/null +++ b/regression/cprover/pointers/struct_pointer6.desc @@ -0,0 +1,7 @@ +CORE +struct_pointer6.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/quantifiers/exists1.c b/regression/cprover/quantifiers/exists1.c new file mode 100644 index 00000000000..2fc67165c91 --- /dev/null +++ b/regression/cprover/quantifiers/exists1.c @@ -0,0 +1,5 @@ +int main() +{ + __CPROVER_assert(∃ int i; i == 100, "100 exists"); // passes + __CPROVER_assert(∃ int i; 0, "'false' does not exist"); // fails +} diff --git a/regression/cprover/quantifiers/exists1.desc b/regression/cprover/quantifiers/exists1.desc new file mode 100644 index 00000000000..daf85c282a9 --- /dev/null +++ b/regression/cprover/quantifiers/exists1.desc @@ -0,0 +1,10 @@ +KNOWNBUG +exists1.c +--text --solve +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S19\(ς\) ⇒ ∃ main::1::1::i : signedbv\[32\] \. main::1::1::i = 100$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ ∃ main::1::2::i : signedbv\[32] \. 0 ≠ 0$ +^\[main\.assertion\.1\] line \d+ 100 exists: REFUTED$ +^\[main\.assertion\.2\] line \d+ 'false' does not exist: REFUTED$ +-- diff --git a/regression/cprover/quantifiers/forall1.c b/regression/cprover/quantifiers/forall1.c new file mode 100644 index 00000000000..4f07b3473b6 --- /dev/null +++ b/regression/cprover/quantifiers/forall1.c @@ -0,0 +1,8 @@ +int main() +{ + __CPROVER_assert(∀ int i; i == 100, "all ints are 100"); // fails + __CPROVER_assert(∀ int i; 1, "true holds for all i"); // passes + __CPROVER_assert(∀ unsigned char ch; + ch <= 255, "all chars are ≤255"); // passes + return 0; +} diff --git a/regression/cprover/quantifiers/forall1.desc b/regression/cprover/quantifiers/forall1.desc new file mode 100644 index 00000000000..d466b9afc06 --- /dev/null +++ b/regression/cprover/quantifiers/forall1.desc @@ -0,0 +1,12 @@ +KNOWNBUG +forall1.c +--text --solve +^EXIT=10$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S19\(ς\) ⇒ ∀ main::1::1::i : signedbv\[32\] \. main::1::1::i = 100$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ ∀ main::1::2::i : signedbv\[32\] \. 1 ≠ 0$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ ∀ main::1::3::ch : unsignedbv\[8\] \. cast\(main::1::3::ch, signedbv\[32\]\) ≤ 255$ +^\[main\.assertion\.1\] line \d+ all ints are 100: REFUTED$ +^\[main\.assertion\.2\] line \d+ true holds for all i: SUCCESS$ +^\[main\.assertion\.3\] line \d+ all chars are ≤255: SUCCESS$ +-- diff --git a/regression/cprover/quantifiers/zeros1.c b/regression/cprover/quantifiers/zeros1.c new file mode 100644 index 00000000000..3233f40d0f7 --- /dev/null +++ b/regression/cprover/quantifiers/zeros1.c @@ -0,0 +1,10 @@ +int main() +{ + char array[10]; + + __CPROVER_assume(∀ int i; i >= 0 && i array[i] == 0); + + __CPROVER_assert(array[3] == 0, "property 1"); + + return 0; +} diff --git a/regression/cprover/quantifiers/zeros1.desc b/regression/cprover/quantifiers/zeros1.desc new file mode 100644 index 00000000000..08f04994e1f --- /dev/null +++ b/regression/cprover/quantifiers/zeros1.desc @@ -0,0 +1,7 @@ +KNOWNBUG +forall1.c + +^EXIT=0$ +^SIGNAL=0$ +^program is safe / function main is safe / line .* property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/array_bounds1.c b/regression/cprover/safety/array_bounds1.c new file mode 100644 index 00000000000..d7d6876a596 --- /dev/null +++ b/regression/cprover/safety/array_bounds1.c @@ -0,0 +1,8 @@ +int array[100]; + +int main() +{ + int x = array[10]; // safe + int y = array[100]; // unsafe + int z = array[-1]; // unsafe +} diff --git a/regression/cprover/safety/array_bounds1.desc b/regression/cprover/safety/array_bounds1.desc new file mode 100644 index 00000000000..59241c8ee25 --- /dev/null +++ b/regression/cprover/safety/array_bounds1.desc @@ -0,0 +1,9 @@ +CORE +array_bounds1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.array_bounds\.1\] line \d+ array bounds in array\[.*10\]: SUCCESS$ +^\[main\.array_bounds\.2\] line \d+ array bounds in array\[.*100\]: REFUTED$ +^\[main\.array_bounds\.3\] line \d+ array bounds in array\[.*-1\]: REFUTED$ +-- diff --git a/regression/cprover/safety/array_bounds2.c b/regression/cprover/safety/array_bounds2.c new file mode 100644 index 00000000000..a5383e97681 --- /dev/null +++ b/regression/cprover/safety/array_bounds2.c @@ -0,0 +1,9 @@ +int array[100]; + +int main() +{ + int *p = array; + int x = p[10]; // safe + int y = p[100]; // unsafe + int z = p[-1]; // unsafe +} diff --git a/regression/cprover/safety/array_bounds2.desc b/regression/cprover/safety/array_bounds2.desc new file mode 100644 index 00000000000..00b48326845 --- /dev/null +++ b/regression/cprover/safety/array_bounds2.desc @@ -0,0 +1,9 @@ +CORE +array_bounds2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer .* safe: REFUTED$ +^\[main\.pointer\.3\] line \d+ pointer .* safe: REFUTED$ +-- diff --git a/regression/cprover/safety/array_bounds3.c b/regression/cprover/safety/array_bounds3.c new file mode 100644 index 00000000000..3d943816bf4 --- /dev/null +++ b/regression/cprover/safety/array_bounds3.c @@ -0,0 +1,12 @@ +int main() +{ + const __CPROVER_size_t array_len; + const unsigned char *array_bytes; + + __CPROVER_assume(__CPROVER_LIVE_OBJECT(array_bytes)); + __CPROVER_assume(array_len >= 1); + __CPROVER_assume(__CPROVER_OBJECT_SIZE(array_bytes) == array_len); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(array_bytes) == 0); + + array_bytes[0]; +} diff --git a/regression/cprover/safety/array_bounds3.desc b/regression/cprover/safety/array_bounds3.desc new file mode 100644 index 00000000000..3b258df73fd --- /dev/null +++ b/regression/cprover/safety/array_bounds3.desc @@ -0,0 +1,7 @@ +CORE +array_bounds3.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer .* safe: SUCCESS$ +-- diff --git a/regression/cprover/safety/array_bounds4.c b/regression/cprover/safety/array_bounds4.c new file mode 100644 index 00000000000..61f0ad33efe --- /dev/null +++ b/regression/cprover/safety/array_bounds4.c @@ -0,0 +1,11 @@ +int array[100]; + +int main() +{ + unsigned long int index; + + // safe owing to shortcircuit semantics + if(index < 100 && array[index] != 123) + { + } +} diff --git a/regression/cprover/safety/array_bounds4.desc b/regression/cprover/safety/array_bounds4.desc new file mode 100644 index 00000000000..fb0d4826702 --- /dev/null +++ b/regression/cprover/safety/array_bounds4.desc @@ -0,0 +1,7 @@ +CORE +array_bounds4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.array_bounds\.1\] line \d+ array bounds in array\[.*index\]: SUCCESS$ +-- diff --git a/regression/cprover/safety/const1.c b/regression/cprover/safety/const1.c new file mode 100644 index 00000000000..91bd80272b3 --- /dev/null +++ b/regression/cprover/safety/const1.c @@ -0,0 +1,8 @@ +const int months = 12; + +int main() +{ + int *p; + p = (int *)&months; + *p = 123; // unsafe, since 'months' is const +} diff --git a/regression/cprover/safety/const1.desc b/regression/cprover/safety/const1.desc new file mode 100644 index 00000000000..77a03f63d94 --- /dev/null +++ b/regression/cprover/safety/const1.desc @@ -0,0 +1,7 @@ +CORE +const1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/division_by_zero.c b/regression/cprover/safety/division_by_zero.c new file mode 100644 index 00000000000..31f0a7b11ac --- /dev/null +++ b/regression/cprover/safety/division_by_zero.c @@ -0,0 +1,9 @@ +int main() +{ + int x, y, z; + + z = 10 / x; // unsafe + + if(y != 0) + z = 10 / y; // safe +} diff --git a/regression/cprover/safety/division_by_zero.desc b/regression/cprover/safety/division_by_zero.desc new file mode 100644 index 00000000000..84da044b758 --- /dev/null +++ b/regression/cprover/safety/division_by_zero.desc @@ -0,0 +1,8 @@ +CORE +division_by_zero.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.division-by-zero\.1\] line \d+ division by zero in 10 / x: REFUTED$ +^\[main\.division-by-zero\.2\] line \d+ division by zero in 10 / y: SUCCESS$ +-- diff --git a/regression/cprover/safety/division_by_zero2.c b/regression/cprover/safety/division_by_zero2.c new file mode 100644 index 00000000000..958c0fd06b9 --- /dev/null +++ b/regression/cprover/safety/division_by_zero2.c @@ -0,0 +1,11 @@ +int main() +{ + int x; + + if(x > 0 && 0xffffffff / x > 0) // safe + { + } + else + { + } +} diff --git a/regression/cprover/safety/division_by_zero2.desc b/regression/cprover/safety/division_by_zero2.desc new file mode 100644 index 00000000000..c15922bdf37 --- /dev/null +++ b/regression/cprover/safety/division_by_zero2.desc @@ -0,0 +1,7 @@ +CORE +division_by_zero2.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.division-by-zero\.1\] line \d+ division by zero in .*: SUCCESS$ +-- diff --git a/regression/cprover/safety/double_free1.c b/regression/cprover/safety/double_free1.c new file mode 100644 index 00000000000..c1b7b488c61 --- /dev/null +++ b/regression/cprover/safety/double_free1.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + int *p = malloc(sizeof(int)); + free(p); // safe + free(p); // unsafe +} diff --git a/regression/cprover/safety/double_free1.desc b/regression/cprover/safety/double_free1.desc new file mode 100644 index 00000000000..c767252dad3 --- /dev/null +++ b/regression/cprover/safety/double_free1.desc @@ -0,0 +1,8 @@ +CORE +double_free1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$ +^\[main\.free\.2\] line \d+ free argument must be valid dynamic object: REFUTED$ +-- diff --git a/regression/cprover/safety/dynamic_object1.c b/regression/cprover/safety/dynamic_object1.c new file mode 100644 index 00000000000..ca976e91b91 --- /dev/null +++ b/regression/cprover/safety/dynamic_object1.c @@ -0,0 +1,13 @@ +int main() +{ + void *p, *q; + // functional consistency of dynamic_object + __CPROVER_assume(p == q); + + __CPROVER_assert( + __CPROVER_DYNAMIC_OBJECT(p) == __CPROVER_DYNAMIC_OBJECT(q), "property 1"); + + __CPROVER_assert( + __CPROVER_DYNAMIC_OBJECT(p) == __CPROVER_DYNAMIC_OBJECT(q + 1), + "property 2"); +} diff --git a/regression/cprover/safety/dynamic_object1.desc b/regression/cprover/safety/dynamic_object1.desc new file mode 100644 index 00000000000..75cead585fb --- /dev/null +++ b/regression/cprover/safety/dynamic_object1.desc @@ -0,0 +1,8 @@ +CORE +dynamic_object1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/safety/free1.c b/regression/cprover/safety/free1.c new file mode 100644 index 00000000000..cb2f949f9a9 --- /dev/null +++ b/regression/cprover/safety/free1.c @@ -0,0 +1,10 @@ +void *malloc(__CPROVER_size_t); +void free(void *); + +int main() +{ + int *p = malloc(sizeof(int)); + __CPROVER_assert(__CPROVER_LIVE_OBJECT(p), "property 1"); // passes + free(p); + __CPROVER_assert(__CPROVER_LIVE_OBJECT(p), "property 2"); // fails +} diff --git a/regression/cprover/safety/free1.desc b/regression/cprover/safety/free1.desc new file mode 100644 index 00000000000..00b290ff5cc --- /dev/null +++ b/regression/cprover/safety/free1.desc @@ -0,0 +1,8 @@ +CORE +free1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +-- diff --git a/regression/cprover/safety/free2.c b/regression/cprover/safety/free2.c new file mode 100644 index 00000000000..7c89b31da08 --- /dev/null +++ b/regression/cprover/safety/free2.c @@ -0,0 +1,9 @@ +void *malloc(__CPROVER_size_t); +void free(void *); + +int main() +{ + int *p = malloc(sizeof(int)); + p++; + free(p); // fails, offset is not zero +} diff --git a/regression/cprover/safety/free2.desc b/regression/cprover/safety/free2.desc new file mode 100644 index 00000000000..1d74597b0d4 --- /dev/null +++ b/regression/cprover/safety/free2.desc @@ -0,0 +1,7 @@ +CORE +free2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: REFUTED$ +-- diff --git a/regression/cprover/safety/free3.c b/regression/cprover/safety/free3.c new file mode 100644 index 00000000000..f598ab8b4e4 --- /dev/null +++ b/regression/cprover/safety/free3.c @@ -0,0 +1,11 @@ +void *malloc(__CPROVER_size_t); +void free(void *); + +int main() +{ + int *p = malloc(sizeof(int)); + int *q = malloc(sizeof(int)); + free(p); + __CPROVER_assert(!__CPROVER_LIVE_OBJECT(p), "property 1"); // should pass + __CPROVER_assert(__CPROVER_LIVE_OBJECT(q), "property 2"); // should pass +} diff --git a/regression/cprover/safety/free3.desc b/regression/cprover/safety/free3.desc new file mode 100644 index 00000000000..f56f44e6e44 --- /dev/null +++ b/regression/cprover/safety/free3.desc @@ -0,0 +1,6 @@ +KNOWNBUG +free3.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/safety/free4.c b/regression/cprover/safety/free4.c new file mode 100644 index 00000000000..2148ccd73cb --- /dev/null +++ b/regression/cprover/safety/free4.c @@ -0,0 +1,20 @@ +void *malloc(__CPROVER_size_t); +void free(void *); + +struct List +{ + char *data; +}; + +void *p; + +int main() +{ + struct List *list; + __CPROVER_assume(__CPROVER_rw_ok(list)); + + p = malloc(123); + free(p); + + __CPROVER_assert(__CPROVER_rw_ok(list), "property 1"); +} diff --git a/regression/cprover/safety/free4.desc b/regression/cprover/safety/free4.desc new file mode 100644 index 00000000000..ef72c904989 --- /dev/null +++ b/regression/cprover/safety/free4.desc @@ -0,0 +1,6 @@ +KNOWNBUG +free4.c + +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/cprover/safety/free_null1.c b/regression/cprover/safety/free_null1.c new file mode 100644 index 00000000000..7c405f54cf2 --- /dev/null +++ b/regression/cprover/safety/free_null1.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + free(0); // safe + free((char *)0 + 1); // unsafe +} diff --git a/regression/cprover/safety/free_null1.desc b/regression/cprover/safety/free_null1.desc new file mode 100644 index 00000000000..5f7e90745cd --- /dev/null +++ b/regression/cprover/safety/free_null1.desc @@ -0,0 +1,8 @@ +CORE +free_null1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$ +^\[main\.free\.2\] line \d+ free argument must be valid dynamic object: REFUTED$ +-- diff --git a/regression/cprover/safety/live_object1.c b/regression/cprover/safety/live_object1.c new file mode 100644 index 00000000000..12f98dd163b --- /dev/null +++ b/regression/cprover/safety/live_object1.c @@ -0,0 +1,9 @@ +int main() +{ + void *p, *q; + // functional consistency of live_object + __CPROVER_assume(__CPROVER_LIVE_OBJECT(p)); + __CPROVER_assume(p == q); + __CPROVER_assert(__CPROVER_LIVE_OBJECT(q), "property 1"); + __CPROVER_assert(__CPROVER_LIVE_OBJECT(q + 1), "property 2"); +} diff --git a/regression/cprover/safety/live_object1.desc b/regression/cprover/safety/live_object1.desc new file mode 100644 index 00000000000..ca7bf546a88 --- /dev/null +++ b/regression/cprover/safety/live_object1.desc @@ -0,0 +1,8 @@ +CORE +live_object1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/safety/local_out_of_scope1.c b/regression/cprover/safety/local_out_of_scope1.c new file mode 100644 index 00000000000..49879d6ddbc --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope1.c @@ -0,0 +1,12 @@ +int main() +{ + int *p; + + { + int x; + p = &x; + *p; // ok + } + + // *p; // unsafe +} diff --git a/regression/cprover/safety/local_out_of_scope1.desc b/regression/cprover/safety/local_out_of_scope1.desc new file mode 100644 index 00000000000..b046bb139a4 --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope1.desc @@ -0,0 +1,8 @@ +KNOWNBUG +local_out_of_scope1.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/local_out_of_scope2.c b/regression/cprover/safety/local_out_of_scope2.c new file mode 100644 index 00000000000..b65792b9685 --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope2.c @@ -0,0 +1,11 @@ +int main() +{ + void *p; + __CPROVER_assume(__CPROVER_LIVE_OBJECT(p)); + + { + void *q; // unrelated to p + } + + __CPROVER_assert(__CPROVER_LIVE_OBJECT(p), "property 1"); // passes +} diff --git a/regression/cprover/safety/local_out_of_scope2.desc b/regression/cprover/safety/local_out_of_scope2.desc new file mode 100644 index 00000000000..3f3277c6e60 --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope2.desc @@ -0,0 +1,7 @@ +CORE +local_out_of_scope2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/local_out_of_scope3.c b/regression/cprover/safety/local_out_of_scope3.c new file mode 100644 index 00000000000..fd3e4a6459e --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope3.c @@ -0,0 +1,10 @@ +int main() +{ + int *p; + __CPROVER_assume(__CPROVER_r_ok(p)); + { + int x; + __CPROVER_assert(p != &x, "property 1"); // passes + } + __CPROVER_assert(__CPROVER_r_ok(p), "property 2"); // passes +} diff --git a/regression/cprover/safety/local_out_of_scope3.desc b/regression/cprover/safety/local_out_of_scope3.desc new file mode 100644 index 00000000000..cd9891e67d8 --- /dev/null +++ b/regression/cprover/safety/local_out_of_scope3.desc @@ -0,0 +1,8 @@ +CORE +local_out_of_scope3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/safety/malloc1.c b/regression/cprover/safety/malloc1.c new file mode 100644 index 00000000000..8a450655a5b --- /dev/null +++ b/regression/cprover/safety/malloc1.c @@ -0,0 +1,15 @@ +void *malloc(__CPROVER_size_t); + +int main() +{ + int *p = malloc(sizeof(int)); + + __CPROVER_assert(__CPROVER_LIVE_OBJECT(p), "property 1"); // safe + __CPROVER_assert( + __CPROVER_OBJECT_SIZE(p) == sizeof(int), "property 2"); // safe + __CPROVER_assert(__CPROVER_POINTER_OFFSET(p) == 0, "property 3"); // safe + __CPROVER_assert(__CPROVER_r_ok(p), "property 4"); // safe + __CPROVER_assert(__CPROVER_r_ok(p + 1), "property 5"); // unsafe + + return 0; +} diff --git a/regression/cprover/safety/malloc1.desc b/regression/cprover/safety/malloc1.desc new file mode 100644 index 00000000000..f3f9890f4b3 --- /dev/null +++ b/regression/cprover/safety/malloc1.desc @@ -0,0 +1,11 @@ +CORE +malloc1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +^\[main\.assertion\.4\] line \d+ property 4: SUCCESS$ +^\[main\.assertion\.5\] line \d+ property 5: REFUTED$ +-- diff --git a/regression/cprover/safety/memchr1.c b/regression/cprover/safety/memchr1.c new file mode 100644 index 00000000000..956a42b57fa --- /dev/null +++ b/regression/cprover/safety/memchr1.c @@ -0,0 +1,8 @@ +void *memchr(const void *, int, __CPROVER_size_t n); + +int main() +{ + int x; + memchr(&x, 123, sizeof x); // safe + memchr(&x, 123, (sizeof x) + 1); // unsafe +} diff --git a/regression/cprover/safety/memchr1.desc b/regression/cprover/safety/memchr1.desc new file mode 100644 index 00000000000..03608afb65d --- /dev/null +++ b/regression/cprover/safety/memchr1.desc @@ -0,0 +1,8 @@ +CORE +memchr1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.memchr\.1\] line \d+ memchr source must be valid: SUCCESS$ +^\[main\.memchr\.2\] line \d+ memchr source must be valid: REFUTED$ +-- diff --git a/regression/cprover/safety/memcmp1.c b/regression/cprover/safety/memcmp1.c new file mode 100644 index 00000000000..0aec94717fc --- /dev/null +++ b/regression/cprover/safety/memcmp1.c @@ -0,0 +1,8 @@ +int memcmp(const void *, const void *, __CPROVER_size_t); + +int main() +{ + int x; + memcmp(&x, &x, sizeof x); // safe + memcmp(&x, &x, (sizeof x) + 1); // unsafe +} diff --git a/regression/cprover/safety/memcmp1.desc b/regression/cprover/safety/memcmp1.desc new file mode 100644 index 00000000000..678ac6b7e0e --- /dev/null +++ b/regression/cprover/safety/memcmp1.desc @@ -0,0 +1,8 @@ +CORE +memcmp1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.memcmp\.1\] line \d+ memcmp regions must be valid: SUCCESS$ +^\[main\.memcmp\.2\] line \d+ memcmp regions must be valid: REFUTED$ +-- diff --git a/regression/cprover/safety/memset1.c b/regression/cprover/safety/memset1.c new file mode 100644 index 00000000000..b83a47c1909 --- /dev/null +++ b/regression/cprover/safety/memset1.c @@ -0,0 +1,8 @@ +void *memset(void *, int, __CPROVER_size_t); + +int main() +{ + int x; + memset(&x, 0, sizeof x); // safe + memset(&x, 0, (sizeof x) + 1); // unsafe +} diff --git a/regression/cprover/safety/memset1.desc b/regression/cprover/safety/memset1.desc new file mode 100644 index 00000000000..a5dcccdf744 --- /dev/null +++ b/regression/cprover/safety/memset1.desc @@ -0,0 +1,8 @@ +CORE +memset1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.memset\.1\] line \d+ memset destination must be valid: SUCCESS$ +^\[main\.memset\.2\] line \d+ memset destination must be valid: REFUTED$ +-- diff --git a/regression/cprover/safety/memset2.c b/regression/cprover/safety/memset2.c new file mode 100644 index 00000000000..d4fd8390e25 --- /dev/null +++ b/regression/cprover/safety/memset2.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + int x; + memset(&x, 0, sizeof x); // safe + memset(&x, 0, (sizeof x) + 1); // unsafe +} diff --git a/regression/cprover/safety/memset2.desc b/regression/cprover/safety/memset2.desc new file mode 100644 index 00000000000..ab99b8dccef --- /dev/null +++ b/regression/cprover/safety/memset2.desc @@ -0,0 +1,8 @@ +CORE +memset2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.memset\.1\] line \d+ memset destination must be valid: SUCCESS$ +^\[main\.memset\.2\] line \d+ memset destination must be valid: REFUTED$ +-- diff --git a/regression/cprover/safety/null_dereference1.c b/regression/cprover/safety/null_dereference1.c new file mode 100644 index 00000000000..a2cbd729210 --- /dev/null +++ b/regression/cprover/safety/null_dereference1.c @@ -0,0 +1,6 @@ +int main() +{ + int *p = 0; + + int x = *p; // unsafe +} diff --git a/regression/cprover/safety/null_dereference1.desc b/regression/cprover/safety/null_dereference1.desc new file mode 100644 index 00000000000..2e5a3d9e360 --- /dev/null +++ b/regression/cprover/safety/null_dereference1.desc @@ -0,0 +1,7 @@ +CORE +null_dereference1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/null_object1.c b/regression/cprover/safety/null_object1.c new file mode 100644 index 00000000000..83659515c0c --- /dev/null +++ b/regression/cprover/safety/null_object1.c @@ -0,0 +1,6 @@ +int main() +{ + int *p = 0; + p++; // still 'null object' + int x = *p; // unsafe +} diff --git a/regression/cprover/safety/null_object1.desc b/regression/cprover/safety/null_object1.desc new file mode 100644 index 00000000000..2fda5d3de6f --- /dev/null +++ b/regression/cprover/safety/null_object1.desc @@ -0,0 +1,7 @@ +CORE +null_object1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/object_size1.c b/regression/cprover/safety/object_size1.c new file mode 100644 index 00000000000..fe66c6592f1 --- /dev/null +++ b/regression/cprover/safety/object_size1.c @@ -0,0 +1,12 @@ +int main() +{ + void *p, *q; + // functional consistency of object_size + __CPROVER_assume(p == q); + + __CPROVER_assert( + __CPROVER_OBJECT_SIZE(p) == __CPROVER_OBJECT_SIZE(q), "property 1"); + + __CPROVER_assert( + __CPROVER_OBJECT_SIZE(p) == __CPROVER_OBJECT_SIZE(q + 1), "property 2"); +} diff --git a/regression/cprover/safety/object_size1.desc b/regression/cprover/safety/object_size1.desc new file mode 100644 index 00000000000..7b9f30e1274 --- /dev/null +++ b/regression/cprover/safety/object_size1.desc @@ -0,0 +1,8 @@ +CORE +object_size1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/safety/pointer_to_pointer1.c b/regression/cprover/safety/pointer_to_pointer1.c new file mode 100644 index 00000000000..b9a8fd845e0 --- /dev/null +++ b/regression/cprover/safety/pointer_to_pointer1.c @@ -0,0 +1,13 @@ +#include + +int *p, *q; +void **p_ptr; + +int main() +{ + p = malloc(1); + p_ptr = &p; + q = malloc(2); + free(*p_ptr); + return 0; +} diff --git a/regression/cprover/safety/pointer_to_pointer1.desc b/regression/cprover/safety/pointer_to_pointer1.desc new file mode 100644 index 00000000000..69955ac40e0 --- /dev/null +++ b/regression/cprover/safety/pointer_to_pointer1.desc @@ -0,0 +1,8 @@ +CORE +pointer_to_pointer1.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p_ptr safe: SUCCESS$ +^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok1.c b/regression/cprover/safety/r_ok1.c new file mode 100644 index 00000000000..5a260e3bea0 --- /dev/null +++ b/regression/cprover/safety/r_ok1.c @@ -0,0 +1,9 @@ +int main() +{ + int *p; + __CPROVER_assume(__CPROVER_r_ok(p)); + *p; // safe + *(p + 1); // unsafe + + return 0; +} diff --git a/regression/cprover/safety/r_ok1.desc b/regression/cprover/safety/r_ok1.desc new file mode 100644 index 00000000000..ca4129b3898 --- /dev/null +++ b/regression/cprover/safety/r_ok1.desc @@ -0,0 +1,8 @@ +CORE +r_ok1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer .* safe: REFUTED$ +-- diff --git a/regression/cprover/safety/r_ok2.c b/regression/cprover/safety/r_ok2.c new file mode 100644 index 00000000000..1e26a9eaded --- /dev/null +++ b/regression/cprover/safety/r_ok2.c @@ -0,0 +1,26 @@ +void int_assignment(int *p) __CPROVER_requires(__CPROVER_r_ok(p)) +{ + *p; // safe + + int i = 123; // unrelated assignment + + *p; // still safe +} + +void int_pointer_assignment(int *p) __CPROVER_requires(__CPROVER_r_ok(p)) +{ + *p; // safe + + int *q = p; // unrelated assignment + + *p; // still safe +} + +void pointer_assignment(int *p) __CPROVER_requires(__CPROVER_r_ok(p)) +{ + *p; // safe + + p = 0; // wreck it + + *p; // not safe +} diff --git a/regression/cprover/safety/r_ok2.desc b/regression/cprover/safety/r_ok2.desc new file mode 100644 index 00000000000..7da360c6ff3 --- /dev/null +++ b/regression/cprover/safety/r_ok2.desc @@ -0,0 +1,12 @@ +CORE +r_ok2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[int_assignment\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[int_assignment\.pointer\.2\] line \d+ pointer p safe: SUCCESS$ +^\[int_pointer_assignment\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[int_pointer_assignment\.pointer\.2\] line \d+ pointer p safe: SUCCESS$ +^\[pointer_assignment\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[pointer_assignment\.pointer\.2\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/r_ok3.c b/regression/cprover/safety/r_ok3.c new file mode 100644 index 00000000000..e2102820d06 --- /dev/null +++ b/regression/cprover/safety/r_ok3.c @@ -0,0 +1,13 @@ +#include + +int main() +{ + int x; + int *p = malloc(sizeof(int)); + + __CPROVER_assert(__CPROVER_r_ok(&x), "&x ok to read"); + __CPROVER_assert(__CPROVER_r_ok("foo", 4), "string literal ok to read"); + __CPROVER_assert(__CPROVER_r_ok(p), "p ok to read"); + + return 0; +} diff --git a/regression/cprover/safety/r_ok3.desc b/regression/cprover/safety/r_ok3.desc new file mode 100644 index 00000000000..a691591baf0 --- /dev/null +++ b/regression/cprover/safety/r_ok3.desc @@ -0,0 +1,9 @@ +CORE +r_ok3.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ &x ok to read: SUCCESS$ +^\[main\.assertion\.2\] line \d+ string literal ok to read: SUCCESS$ +^\[main\.assertion\.3\] line \d+ p ok to read: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok4.c b/regression/cprover/safety/r_ok4.c new file mode 100644 index 00000000000..79f0c3a219c --- /dev/null +++ b/regression/cprover/safety/r_ok4.c @@ -0,0 +1,8 @@ +int main() +{ + int *p; + __CPROVER_assume(__CPROVER_r_ok(p)); + __CPROVER_assert(p != 0, "p is not null"); + + return 0; +} diff --git a/regression/cprover/safety/r_ok4.desc b/regression/cprover/safety/r_ok4.desc new file mode 100644 index 00000000000..30251465ac4 --- /dev/null +++ b/regression/cprover/safety/r_ok4.desc @@ -0,0 +1,7 @@ +CORE +r_ok4.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ p is not null: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok5.c b/regression/cprover/safety/r_ok5.c new file mode 100644 index 00000000000..b13e0e7a3f1 --- /dev/null +++ b/regression/cprover/safety/r_ok5.c @@ -0,0 +1,7 @@ +int x; + +int main() +{ + __CPROVER_assert(__CPROVER_r_ok(&x), "property 1"); + return 0; +} diff --git a/regression/cprover/safety/r_ok5.desc b/regression/cprover/safety/r_ok5.desc new file mode 100644 index 00000000000..cf9e18f0df4 --- /dev/null +++ b/regression/cprover/safety/r_ok5.desc @@ -0,0 +1,7 @@ +CORE +r_ok5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok6.c b/regression/cprover/safety/r_ok6.c new file mode 100644 index 00000000000..869727b3c2d --- /dev/null +++ b/regression/cprover/safety/r_ok6.c @@ -0,0 +1,9 @@ +int main() +{ + const __CPROVER_size_t array_len; + const unsigned char *array_bytes; + + __CPROVER_assume(array_len >= 1); + __CPROVER_assume(__CPROVER_r_ok(array_bytes, array_len)); + __CPROVER_assert(__CPROVER_r_ok(array_bytes, 1), "property 1"); +} diff --git a/regression/cprover/safety/r_ok6.desc b/regression/cprover/safety/r_ok6.desc new file mode 100644 index 00000000000..6027f60122e --- /dev/null +++ b/regression/cprover/safety/r_ok6.desc @@ -0,0 +1,7 @@ +CORE +r_ok6.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok7.c b/regression/cprover/safety/r_ok7.c new file mode 100644 index 00000000000..4cf9ac6c554 --- /dev/null +++ b/regression/cprover/safety/r_ok7.c @@ -0,0 +1,10 @@ +int main() +{ + const __CPROVER_size_t array_len, i; + const unsigned char *array_bytes; + + __CPROVER_assume(i < array_len); + __CPROVER_assume(__CPROVER_POINTER_OFFSET(array_bytes) == 0); + __CPROVER_assume(__CPROVER_r_ok(array_bytes, array_len)); + __CPROVER_assert(__CPROVER_r_ok(array_bytes + i, 1), "property 1"); +} diff --git a/regression/cprover/safety/r_ok7.desc b/regression/cprover/safety/r_ok7.desc new file mode 100644 index 00000000000..8be2b681854 --- /dev/null +++ b/regression/cprover/safety/r_ok7.desc @@ -0,0 +1,7 @@ +CORE +r_ok7.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/r_ok8.c b/regression/cprover/safety/r_ok8.c new file mode 100644 index 00000000000..732ed5f1193 --- /dev/null +++ b/regression/cprover/safety/r_ok8.c @@ -0,0 +1,9 @@ +char ch; + +int main() +{ + __CPROVER_assert(__CPROVER_r_ok(&ch, 1), "property 1"); // passes + __CPROVER_assert(__CPROVER_r_ok(&ch + 1, 1), "property 2"); // fails + __CPROVER_assert(__CPROVER_r_ok(&ch + 1, 0), "property 3"); // passes + __CPROVER_assert(__CPROVER_r_ok(&ch + 2, 0), "property 4"); // fails +} diff --git a/regression/cprover/safety/r_ok8.desc b/regression/cprover/safety/r_ok8.desc new file mode 100644 index 00000000000..58e4cf52f65 --- /dev/null +++ b/regression/cprover/safety/r_ok8.desc @@ -0,0 +1,10 @@ +CORE +r_ok8.c + +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: REFUTED$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +^\[main\.assertion\.4\] line \d+ property 4: REFUTED$ +-- diff --git a/regression/cprover/safety/r_ok_is_function1.c b/regression/cprover/safety/r_ok_is_function1.c new file mode 100644 index 00000000000..2b8697a7f61 --- /dev/null +++ b/regression/cprover/safety/r_ok_is_function1.c @@ -0,0 +1,10 @@ +int main() +{ + int *p, *q; + + if(p == q) + __CPROVER_assert( + __CPROVER_r_ok(p, 1) == __CPROVER_r_ok(q, 1), "property 1"); + + return 0; +} diff --git a/regression/cprover/safety/r_ok_is_function1.desc b/regression/cprover/safety/r_ok_is_function1.desc new file mode 100644 index 00000000000..fe1a07e592c --- /dev/null +++ b/regression/cprover/safety/r_ok_is_function1.desc @@ -0,0 +1,7 @@ +CORE +r_ok_is_function1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/safety/realloc1.c b/regression/cprover/safety/realloc1.c new file mode 100644 index 00000000000..4e4df0e04d6 --- /dev/null +++ b/regression/cprover/safety/realloc1.c @@ -0,0 +1,13 @@ +void *malloc(__CPROVER_size_t); +void *realloc(void *, __CPROVER_size_t); +void free(void *); + +int main() +{ + int *p = malloc(sizeof(int)); + int *q = realloc(p, sizeof(int) * 2); + __CPROVER_assert(__CPROVER_LIVE_OBJECT(q), "property 1"); // passes + __CPROVER_assert( + __CPROVER_OBJECT_SIZE(q) == sizeof(int) * 2, "property 2"); // passes + free(p); +} diff --git a/regression/cprover/safety/realloc1.desc b/regression/cprover/safety/realloc1.desc new file mode 100644 index 00000000000..e1b11e8c159 --- /dev/null +++ b/regression/cprover/safety/realloc1.desc @@ -0,0 +1,10 @@ +CORE +realloc1.c +--safety +^EXIT=0$ +^SIGNAL=0$ +^\[main\.realloc\.1\] line \d+ realloc argument must be valid dynamic object: SUCCESS$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.free\.1\] line \d+ free argument must be valid dynamic object: SUCCESS$ +-- diff --git a/regression/cprover/safety/realloc2.c b/regression/cprover/safety/realloc2.c new file mode 100644 index 00000000000..3f22f9196ab --- /dev/null +++ b/regression/cprover/safety/realloc2.c @@ -0,0 +1,9 @@ +void *malloc(__CPROVER_size_t); +void *realloc(void *, __CPROVER_size_t); + +int main() +{ + int *p = malloc(sizeof(int)); + p++; + int *q = realloc(p, sizeof(int) * 2); // fails, offset not zero +} diff --git a/regression/cprover/safety/realloc2.desc b/regression/cprover/safety/realloc2.desc new file mode 100644 index 00000000000..da8fedb42b9 --- /dev/null +++ b/regression/cprover/safety/realloc2.desc @@ -0,0 +1,7 @@ +CORE +realloc2.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.realloc\.1\] line \d+ realloc argument must be valid dynamic object: REFUTED$ +-- diff --git a/regression/cprover/safety/shifts1.c b/regression/cprover/safety/shifts1.c new file mode 100644 index 00000000000..0254f2e4ef8 --- /dev/null +++ b/regression/cprover/safety/shifts1.c @@ -0,0 +1,9 @@ +int main() +{ + 1 << 1; // safe + 1 << -1; // unsafe, shift distance negative + 1 >> -1; // unsafe, shift distance negative + 1 << (sizeof(1) * 8); // unsafe, shift distance too large + 1 << (sizeof(1) * 8 - 2); // safe + 1u << (sizeof(1u) * 8 - 1); // safe +} diff --git a/regression/cprover/safety/shifts1.desc b/regression/cprover/safety/shifts1.desc new file mode 100644 index 00000000000..4429b6a35ee --- /dev/null +++ b/regression/cprover/safety/shifts1.desc @@ -0,0 +1,9 @@ +KNOWNBUG +shifts1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.undefined-shift\.3\] line \d+ shift distance is negative in 1 << -1: REFUTED$ +^\[main\.undefined-shift\.6\] line \d+ shift distance is negative in 1 >> -1: REFUTED$ +^\[main\.undefined-shift\.8\] line \d+ shift distance too large in 1 << sizeof.*: REFUTED$ +-- diff --git a/regression/cprover/safety/simple_equality1.c b/regression/cprover/safety/simple_equality1.c new file mode 100644 index 00000000000..bfb69aeb8ab --- /dev/null +++ b/regression/cprover/safety/simple_equality1.c @@ -0,0 +1,9 @@ +int main() +{ + int x, *p; + p = &x; + *p; // ok + p++; + *p; // not ok, out of bounds + return 0; +} diff --git a/regression/cprover/safety/simple_equality1.desc b/regression/cprover/safety/simple_equality1.desc new file mode 100644 index 00000000000..a4ab94d561a --- /dev/null +++ b/regression/cprover/safety/simple_equality1.desc @@ -0,0 +1,8 @@ +CORE +simple_equality1.c +--safety +^EXIT=10$ +^SIGNAL=0$ +^\[main\.pointer\.1\] line \d+ pointer p safe: SUCCESS$ +^\[main\.pointer\.2\] line \d+ pointer p safe: REFUTED$ +-- diff --git a/regression/cprover/safety/use_after_free1.c b/regression/cprover/safety/use_after_free1.c new file mode 100644 index 00000000000..d6776289d3c --- /dev/null +++ b/regression/cprover/safety/use_after_free1.c @@ -0,0 +1,9 @@ +#include + +int main() +{ + int *p = malloc(sizeof(int)); + int x = *p; // safe + free(p); + int y = *p; // unsafe +} diff --git a/regression/cprover/safety/use_after_free1.desc b/regression/cprover/safety/use_after_free1.desc new file mode 100644 index 00000000000..55f42a70080 --- /dev/null +++ b/regression/cprover/safety/use_after_free1.desc @@ -0,0 +1,8 @@ +CORE +use_after_free1.c +--text --inline --no-safety +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state . S21\(ς\) ⇒ S22\(ς\[❝main::\$tmp::return_value_malloc❞:=allocate\(ς, 4\)\]\)$ +^\(\d+\) ∀ ς : state . S25\(ς\) ⇒ S26\(deallocate_state\(ς, cast\(ς\(❝main::1::p❞\), empty\*\)\)\)$ +-- diff --git a/regression/cprover/safety/writeable1.c b/regression/cprover/safety/writeable1.c new file mode 100644 index 00000000000..4460b619363 --- /dev/null +++ b/regression/cprover/safety/writeable1.c @@ -0,0 +1,14 @@ +const int months = 12; +int x; + +void *malloc(__CPROVER_size_t); + +int main() +{ + __CPROVER_assert(__CPROVER_WRITEABLE_OBJECT(&x), "property 1"); // holds + __CPROVER_assert(!__CPROVER_WRITEABLE_OBJECT(&months), "property 2"); // holds + __CPROVER_assert( + !__CPROVER_WRITEABLE_OBJECT("foobar"), "property 3"); // holds + __CPROVER_assert( + __CPROVER_WRITEABLE_OBJECT(malloc(1)), "property 1"); // holds +} diff --git a/regression/cprover/safety/writeable1.desc b/regression/cprover/safety/writeable1.desc new file mode 100644 index 00000000000..04aa8aafa26 --- /dev/null +++ b/regression/cprover/safety/writeable1.desc @@ -0,0 +1,9 @@ +CORE +writeable1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +-- diff --git a/regression/cprover/structs/nondet_struct1.c b/regression/cprover/structs/nondet_struct1.c new file mode 100644 index 00000000000..86cb847307c --- /dev/null +++ b/regression/cprover/structs/nondet_struct1.c @@ -0,0 +1,12 @@ +struct S +{ + int x, y; +} s; + +struct S nondet_S(); + +int main() +{ + s = nondet_S(); + return 0; +} diff --git a/regression/cprover/structs/nondet_struct1.desc b/regression/cprover/structs/nondet_struct1.desc new file mode 100644 index 00000000000..e7c54f6c58e --- /dev/null +++ b/regression/cprover/structs/nondet_struct1.desc @@ -0,0 +1,7 @@ +CORE +nondet_struct1.c +--text --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state, nondet::S21-0 : signedbv\[32\], nondet::S21-1 : signedbv\[32\] \. S20\(ς\) ⇒ S21\(ς\[❝s❞\.❝x❞:=nondet::S21-0\]\[❝s❞\.❝y❞:=nondet::S21-1\]\)$ +-- diff --git a/regression/cprover/structs/struct1.c b/regression/cprover/structs/struct1.c new file mode 100644 index 00000000000..0703c045632 --- /dev/null +++ b/regression/cprover/structs/struct1.c @@ -0,0 +1,10 @@ +struct S +{ + int x, y; +} s; + +int main() +{ + s.x = 1; + __CPROVER_assert(s.x == 1, "property 1"); +} diff --git a/regression/cprover/structs/struct1.desc b/regression/cprover/structs/struct1.desc new file mode 100644 index 00000000000..0c58df9e6a3 --- /dev/null +++ b/regression/cprover/structs/struct1.desc @@ -0,0 +1,9 @@ +CORE +struct1.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝s❞.❝x❞:=1\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ \(ς\(❝s❞.❝x❞\) = 1\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/structs/struct2.c b/regression/cprover/structs/struct2.c new file mode 100644 index 00000000000..e47da2dc124 --- /dev/null +++ b/regression/cprover/structs/struct2.c @@ -0,0 +1,12 @@ +struct S +{ + int x, y; +} s; + +int main() +{ + s.x = 1; + s.y = 2; + __CPROVER_assert(s.x == 1, "property 1"); + __CPROVER_assert(s.y == 2, "property 2"); +} diff --git a/regression/cprover/structs/struct2.desc b/regression/cprover/structs/struct2.desc new file mode 100644 index 00000000000..a260597c06e --- /dev/null +++ b/regression/cprover/structs/struct2.desc @@ -0,0 +1,12 @@ +CORE +struct2.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S20\(ς\) ⇒ S21\(ς\[❝s❞.❝x❞:=1\]\)$ +^\(\d+\) ∀ ς : state \. S21\(ς\) ⇒ S22\(ς\[❝s❞.❝y❞:=2\]\)$ +^\(\d+\) ∀ ς : state \. S22\(ς\) ⇒ \(ς\(❝s❞.❝x❞\) = 1\)$ +^\(\d+\) ∀ ς : state \. S23\(ς\) ⇒ \(ς\(❝s❞.❝y❞\) = 2\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/structs/struct3.c b/regression/cprover/structs/struct3.c new file mode 100644 index 00000000000..9ec1471ef52 --- /dev/null +++ b/regression/cprover/structs/struct3.c @@ -0,0 +1,14 @@ +struct S +{ + int x, y; +} a, b; + +int main() +{ + a.x = 1; + b = a; // copy entire struct + __CPROVER_assert(a.x == 1, "property 1"); // should pass + __CPROVER_assert(b.x == 1, "property 2"); // should pass + __CPROVER_assert(b.y == a.y, "property 3"); // should pass + return 0; +} diff --git a/regression/cprover/structs/struct3.desc b/regression/cprover/structs/struct3.desc new file mode 100644 index 00000000000..53c4bbe1395 --- /dev/null +++ b/regression/cprover/structs/struct3.desc @@ -0,0 +1,10 @@ +CORE +struct3.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S\d+\(ς\) ⇒ S\d+\(ς\[❝b❞\.❝x❞:=ς\(❝a❞\.❝x❞\)\]\[❝b❞\.❝y❞:=ς\(❝a❞\.❝y❞\)\]\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +^\[main\.assertion\.3\] line \d+ property 3: SUCCESS$ +-- diff --git a/regression/cprover/structs/struct4.c b/regression/cprover/structs/struct4.c new file mode 100644 index 00000000000..d22730de2a5 --- /dev/null +++ b/regression/cprover/structs/struct4.c @@ -0,0 +1,11 @@ +struct S +{ + int x, y; +} a = {.x = 10, .y = 20}; + +int main() +{ + __CPROVER_assert(a.x == 10, "property 1"); // should pass + __CPROVER_assert(a.y == 20, "property 2"); // should pass + return 0; +} diff --git a/regression/cprover/structs/struct4.desc b/regression/cprover/structs/struct4.desc new file mode 100644 index 00000000000..6f6a1b31556 --- /dev/null +++ b/regression/cprover/structs/struct4.desc @@ -0,0 +1,9 @@ +CORE +struct4.c +--text --solve --inline +^EXIT=0$ +^SIGNAL=0$ +^\(\d+\) ∀ ς : state \. S\d+\(ς\) ⇒ S\d+\(ς\[❝a❞\.❝x❞:=10\]\[❝a❞\.❝y❞:=20\]\)$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/structs/struct5.c b/regression/cprover/structs/struct5.c new file mode 100644 index 00000000000..0cf1104e157 --- /dev/null +++ b/regression/cprover/structs/struct5.c @@ -0,0 +1,14 @@ +struct S +{ + int x, y; +} a, b; + +int main() +{ + a.x = 1; + a.y = 2; + b.x = 1; + b.y = 2; + __CPROVER_assert(a == b, "property 1"); // should pass + return 0; +} diff --git a/regression/cprover/structs/struct5.desc b/regression/cprover/structs/struct5.desc new file mode 100644 index 00000000000..497c967c620 --- /dev/null +++ b/regression/cprover/structs/struct5.desc @@ -0,0 +1,7 @@ +CORE +struct5.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/regression/cprover/trace/trace1.c b/regression/cprover/trace/trace1.c new file mode 100644 index 00000000000..8f9cb492fd6 --- /dev/null +++ b/regression/cprover/trace/trace1.c @@ -0,0 +1,12 @@ +int main() +{ + int x, y; + + y = 0; + + if(x > y) + { + x++; + __CPROVER_assert(x > y, "property 1"); // fails + } +} diff --git a/regression/cprover/trace/trace1.desc b/regression/cprover/trace/trace1.desc new file mode 100644 index 00000000000..8f431330546 --- /dev/null +++ b/regression/cprover/trace/trace1.desc @@ -0,0 +1,10 @@ +CORE +trace1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 3 main::1::x := 2147483647$ +^ 5 main::1::y := 0$ +^ 9 main::1::x := -2147483648$ +-- diff --git a/regression/cprover/trace/trace3.c b/regression/cprover/trace/trace3.c new file mode 100644 index 00000000000..2ce40d3a12c --- /dev/null +++ b/regression/cprover/trace/trace3.c @@ -0,0 +1,10 @@ +int x, y; + +int main() +{ + x = 1; + y = 2; + x = 3; + __CPROVER_assert(0, "property 1"); + return 0; +} diff --git a/regression/cprover/trace/trace3.desc b/regression/cprover/trace/trace3.desc new file mode 100644 index 00000000000..35dbd4137d1 --- /dev/null +++ b/regression/cprover/trace/trace3.desc @@ -0,0 +1,8 @@ +CORE +trace3.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: REFUTED$ +^Trace for main\.assertion\.1:$ +-- diff --git a/regression/cprover/trace/trace_array1.c b/regression/cprover/trace/trace_array1.c new file mode 100644 index 00000000000..ba19992ade4 --- /dev/null +++ b/regression/cprover/trace/trace_array1.c @@ -0,0 +1,18 @@ +struct S +{ + int a, b, c; +} x; + +__CPROVER_size_t i = 3; + +int array[10]; + +int main() +{ + x.a = 123; + x.b = x.a; + array[1] = 10; + array[2] = 20; + array[i] = 30; + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_array1.desc b/regression/cprover/trace/trace_array1.desc new file mode 100644 index 00000000000..e22a46f77fa --- /dev/null +++ b/regression/cprover/trace/trace_array1.desc @@ -0,0 +1,14 @@ +CORE +trace_array1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 8 array\[9\] := 0$ +^ 4 x.c := 0$ +^ 12 x.a := 123$ +^ 13 x.b := 123$ +^ 14 array\[1\] := 10$ +^ 15 array\[2\] := 20$ +^ 16 array\[3\] := 30$ +-- diff --git a/regression/cprover/trace/trace_contract1.c b/regression/cprover/trace/trace_contract1.c new file mode 100644 index 00000000000..df6d091df1b --- /dev/null +++ b/regression/cprover/trace/trace_contract1.c @@ -0,0 +1,7 @@ +int my_function(int parameter) + // clang-format off + __CPROVER_ensures(__CPROVER_return_value == 123) +// clang-format on +{ + return parameter; +} diff --git a/regression/cprover/trace/trace_contract1.desc b/regression/cprover/trace/trace_contract1.desc new file mode 100644 index 00000000000..e98dc997809 --- /dev/null +++ b/regression/cprover/trace/trace_contract1.desc @@ -0,0 +1,8 @@ +CORE +trace_contract1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for my_function\.postcondition\.1:$ +^ \d+ return_value := ς\(❝my_function::parameter❞\)$ +-- diff --git a/regression/cprover/trace/trace_function_call1.c b/regression/cprover/trace/trace_function_call1.c new file mode 100644 index 00000000000..955a3566574 --- /dev/null +++ b/regression/cprover/trace/trace_function_call1.c @@ -0,0 +1,11 @@ +int foo(int parameter) +{ + return parameter + 1; +} + +int main() +{ + int x; + x = foo(123); + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_function_call1.desc b/regression/cprover/trace/trace_function_call1.desc new file mode 100644 index 00000000000..2b25ee28592 --- /dev/null +++ b/regression/cprover/trace/trace_function_call1.desc @@ -0,0 +1,10 @@ +CORE +trace_function_call1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 9 foo::parameter := 123$ +^ 3 return_value := 124$ +^ 9 main::1::x := 124$ +-- diff --git a/regression/cprover/trace/trace_malloc1.c b/regression/cprover/trace/trace_malloc1.c new file mode 100644 index 00000000000..7b9592c1f21 --- /dev/null +++ b/regression/cprover/trace/trace_malloc1.c @@ -0,0 +1,11 @@ +#include + +int main() +{ + int *p = malloc(sizeof(int) * 1000); + + p[10] = 1; + p[20] = 2; + + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_malloc1.desc b/regression/cprover/trace/trace_malloc1.desc new file mode 100644 index 00000000000..98065c9caee --- /dev/null +++ b/regression/cprover/trace/trace_malloc1.desc @@ -0,0 +1,11 @@ +CORE +trace_malloc1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 5 main::\$tmp::return_value_malloc := ❝heap-1❞$ +^ 5 main::1::p := cast\(❝heap-1❞, signedbv\[32\]\*\)$ +^ 7 \[cast\(❝heap-1❞, signedbv\[32\]\*\) \+ 10\] := 1$ +^ 8 \[cast\(❝heap-1❞, signedbv\[32\]\*\) \+ 20\] := 2$ +-- diff --git a/regression/cprover/trace/trace_malloc2.c b/regression/cprover/trace/trace_malloc2.c new file mode 100644 index 00000000000..eb97645c98e --- /dev/null +++ b/regression/cprover/trace/trace_malloc2.c @@ -0,0 +1,11 @@ +#include + +int main() +{ + int *p = malloc(sizeof(int) * 1000); + + p[10] = 1; + p[10]++; + + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_malloc2.desc b/regression/cprover/trace/trace_malloc2.desc new file mode 100644 index 00000000000..32f14ddd3fe --- /dev/null +++ b/regression/cprover/trace/trace_malloc2.desc @@ -0,0 +1,9 @@ +CORE +trace_malloc2.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 7 \[cast\(❝heap-1❞, signedbv\[32\]\*\) \+ 10\] := 1$ +^ 8 \[cast\(❝heap-1❞, signedbv\[32\]\*\) \+ 10\] := 2$ +-- diff --git a/regression/cprover/trace/trace_nondet1.c b/regression/cprover/trace/trace_nondet1.c new file mode 100644 index 00000000000..bd22ed59394 --- /dev/null +++ b/regression/cprover/trace/trace_nondet1.c @@ -0,0 +1,8 @@ +int nondet_int(); +int x; + +int main() +{ + x = nondet_int(); + __CPROVER_assert(x != 20, "property 1"); // fails +} diff --git a/regression/cprover/trace/trace_nondet1.desc b/regression/cprover/trace/trace_nondet1.desc new file mode 100644 index 00000000000..355ba373700 --- /dev/null +++ b/regression/cprover/trace/trace_nondet1.desc @@ -0,0 +1,8 @@ +CORE +trace_nondet1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 6 x := 20$ +-- diff --git a/regression/cprover/trace/trace_pointer1.c b/regression/cprover/trace/trace_pointer1.c new file mode 100644 index 00000000000..46609bbe59f --- /dev/null +++ b/regression/cprover/trace/trace_pointer1.c @@ -0,0 +1,9 @@ +int global; + +int main() +{ + int *x; + __CPROVER_assume(__CPROVER_w_ok(x)); + *x = 123; + __CPROVER_assert(global == 0, "property 1"); +} diff --git a/regression/cprover/trace/trace_pointer1.desc b/regression/cprover/trace/trace_pointer1.desc new file mode 100644 index 00000000000..152b8f591d2 --- /dev/null +++ b/regression/cprover/trace/trace_pointer1.desc @@ -0,0 +1,9 @@ +CORE +trace_pointer1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 5 main::1::x := address_of\(global\)$ +^ 7 global := 123$ +-- diff --git a/regression/cprover/trace/trace_pointer2.c b/regression/cprover/trace/trace_pointer2.c new file mode 100644 index 00000000000..5df4f8f1a1e --- /dev/null +++ b/regression/cprover/trace/trace_pointer2.c @@ -0,0 +1,9 @@ +int global; + +int main() +{ + int array[100]; + int *x = array; + *(x + 2) = 123; + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_pointer2.desc b/regression/cprover/trace/trace_pointer2.desc new file mode 100644 index 00000000000..a07a1ec7510 --- /dev/null +++ b/regression/cprover/trace/trace_pointer2.desc @@ -0,0 +1,9 @@ +CORE +trace_pointer2.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 6 main::1::x := element_address\(❝main::1::array❞, 0\)$ +^ 7 main::1::array\[2\] := 123$ +-- diff --git a/regression/cprover/trace/trace_pointer3.c b/regression/cprover/trace/trace_pointer3.c new file mode 100644 index 00000000000..d95a519a394 --- /dev/null +++ b/regression/cprover/trace/trace_pointer3.c @@ -0,0 +1,11 @@ +struct S +{ + int a, b, c; +} x; + +int main() +{ + struct S *p = &x; + p->b = 123; + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_pointer3.desc b/regression/cprover/trace/trace_pointer3.desc new file mode 100644 index 00000000000..1ab787fe639 --- /dev/null +++ b/regression/cprover/trace/trace_pointer3.desc @@ -0,0 +1,9 @@ +CORE +trace_pointer3.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 8 main::1::p := ❝x❞$ +^ 9 x\.b := 123$ +-- diff --git a/regression/cprover/trace/trace_struct1.c b/regression/cprover/trace/trace_struct1.c new file mode 100644 index 00000000000..3a7e0155497 --- /dev/null +++ b/regression/cprover/trace/trace_struct1.c @@ -0,0 +1,13 @@ +struct S +{ + int a, b, c; +} x, y; + +int main() +{ + x.a = 123; + x.b = 456; + int *p = &x.c; + *p = 789; + __CPROVER_assert(0, "property 1"); +} diff --git a/regression/cprover/trace/trace_struct1.desc b/regression/cprover/trace/trace_struct1.desc new file mode 100644 index 00000000000..11d9ab47090 --- /dev/null +++ b/regression/cprover/trace/trace_struct1.desc @@ -0,0 +1,9 @@ +CORE +trace_struct1.c +--trace +^EXIT=10$ +^SIGNAL=0$ +^Trace for main\.assertion\.1:$ +^ 10 main::1::p := ❝x❞\.❝c❞$ +^ 11 x\.c := 789$ +-- diff --git a/regression/cprover/unions/union1.c b/regression/cprover/unions/union1.c new file mode 100644 index 00000000000..f98fa99e285 --- /dev/null +++ b/regression/cprover/unions/union1.c @@ -0,0 +1,18 @@ +union U +{ + int i; + char ch; +}; + +int main() +{ + union U u; + + u.i = 1; + __CPROVER_assert(u.i == 1, "property 1"); // passes + + u.ch = 2; + __CPROVER_assert(u.ch == 2, "property 2"); // passes + + return 0; +} diff --git a/regression/cprover/unions/union1.desc b/regression/cprover/unions/union1.desc new file mode 100644 index 00000000000..07a9c24e1c9 --- /dev/null +++ b/regression/cprover/unions/union1.desc @@ -0,0 +1,8 @@ +CORE +union1.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +^\[main\.assertion\.2\] line \d+ property 2: SUCCESS$ +-- diff --git a/regression/cprover/unions/union2.c b/regression/cprover/unions/union2.c new file mode 100644 index 00000000000..1de535dbd4a --- /dev/null +++ b/regression/cprover/unions/union2.c @@ -0,0 +1,16 @@ +union U +{ + int i; + char ch; +}; + +int main() +{ + union U u; + + u.i = 0; + u.ch = 1; + __CPROVER_assert(u.i == 1, "property 1"); // passes on little endian + + return 0; +} diff --git a/regression/cprover/unions/union2.desc b/regression/cprover/unions/union2.desc new file mode 100644 index 00000000000..4d3d8ec3731 --- /dev/null +++ b/regression/cprover/unions/union2.desc @@ -0,0 +1,7 @@ +KNOWNBUG +union2.c + +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ property 1: SUCCESS$ +-- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1578ca05d4f..a1e6f41f8b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -106,6 +106,7 @@ add_subdirectory(statement-list) add_subdirectory(util) add_subdirectory(cbmc) +add_subdirectory(cprover) add_subdirectory(crangler) add_subdirectory(goto-cc) add_subdirectory(goto-instrument) diff --git a/src/Makefile b/src/Makefile index 92afa0ba26b..698292d6e60 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ DIRS = analyses \ big-int \ cbmc \ cpp \ + cprover \ crangler \ goto-analyzer \ goto-cc \ @@ -28,6 +29,7 @@ DIRS = analyses \ # Empty last line all: cbmc.dir \ + cprover.dir \ crangler.dir \ goto-analyzer.dir \ goto-cc.dir \ @@ -84,6 +86,9 @@ cbmc.dir: languages solvers.dir goto-symex.dir analyses.dir \ pointer-analysis.dir goto-programs.dir linking.dir \ goto-instrument.dir goto-checker.dir +cprover.dir: ansi-c.dir langapi.dir solvers.dir goto-programs.dir \ + linking.dir analyses.dir + goto-analyzer.dir: languages analyses.dir goto-programs.dir linking.dir \ goto-instrument.dir goto-checker.dir diff --git a/src/ansi-c/c_typecheck_expr.cpp b/src/ansi-c/c_typecheck_expr.cpp index fd58ba57e59..278c5503985 100644 --- a/src/ansi-c/c_typecheck_expr.cpp +++ b/src/ansi-c/c_typecheck_expr.cpp @@ -2522,6 +2522,24 @@ exprt c_typecheck_baset::do_special_functions( return live_object_expr; } + else if(identifier == CPROVER_PREFIX "pointer_in_range") + { + // experimental feature for CHC encodings -- do not use + if(expr.arguments().size() != 3) + { + error().source_location = f_op.source_location(); + error() << "pointer_in_range expects three arguments" << eom; + throw 0; + } + + typecheck_function_call_arguments(expr); + + exprt pointer_in_range_expr = pointer_in_range_exprt( + expr.arguments()[0], expr.arguments()[1], expr.arguments()[2]); + pointer_in_range_expr.add_source_location() = source_location; + + return pointer_in_range_expr; + } else if(identifier == CPROVER_PREFIX "WRITEABLE_OBJECT") { if(expr.arguments().size() != 1) @@ -2538,6 +2556,23 @@ exprt c_typecheck_baset::do_special_functions( return writeable_object_expr; } + else if(identifier == CPROVER_PREFIX "separate") + { + // experimental feature for CHC encodings -- do not use + if(expr.arguments().size() < 2) + { + error().source_location = f_op.source_location(); + error() << "separate expects two or more arguments" << eom; + throw 0; + } + + typecheck_function_call_arguments(expr); + + exprt separate_expr = separate_exprt(expr.arguments()); + separate_expr.add_source_location() = source_location; + + return separate_expr; + } else if(identifier==CPROVER_PREFIX "POINTER_OFFSET") { if(expr.arguments().size()!=1) diff --git a/src/ansi-c/cprover_builtin_headers.h b/src/ansi-c/cprover_builtin_headers.h index 17876ea8981..2c18c8fbfcc 100644 --- a/src/ansi-c/cprover_builtin_headers.h +++ b/src/ansi-c/cprover_builtin_headers.h @@ -23,6 +23,7 @@ __CPROVER_bool __CPROVER_is_cyclic_dll(); __CPROVER_bool __CPROVER_is_sentinel_dll(); __CPROVER_bool __CPROVER_is_cstring(const char *); __CPROVER_size_t __CPROVER_cstrlen(const char *); +__CPROVER_bool __CPROVER_separate(const void *, const void *, ...); // bitvector analysis __CPROVER_bool __CPROVER_get_flag(const void *, const char *); @@ -56,6 +57,7 @@ __CPROVER_size_t __CPROVER_POINTER_OBJECT(const void *); __CPROVER_ssize_t __CPROVER_POINTER_OFFSET(const void *); __CPROVER_size_t __CPROVER_OBJECT_SIZE(const void *); __CPROVER_bool __CPROVER_DYNAMIC_OBJECT(const void *); +__CPROVER_bool __CPROVER_pointer_in_range(const void *, const void *, const void *); void __CPROVER_allocated_memory(__CPROVER_size_t address, __CPROVER_size_t extent); // float stuff diff --git a/src/ansi-c/expr2c.cpp b/src/ansi-c/expr2c.cpp index c8f665ec2de..d1e94046fef 100644 --- a/src/ansi-c/expr2c.cpp +++ b/src/ansi-c/expr2c.cpp @@ -3988,6 +3988,7 @@ optionalt expr2ct::convert_function(const exprt &src) {ID_live_object, "LIVE_OBJECT"}, {ID_writeable_object, "WRITEABLE_OBJECT"}, {ID_find_first_set, "__builtin_ffs"}, + {ID_separate, "SEPARATE"}, {ID_floatbv_div, "FLOAT/"}, {ID_floatbv_minus, "FLOAT-"}, {ID_floatbv_mult, "FLOAT*"}, diff --git a/src/ansi-c/goto_check_c.cpp b/src/ansi-c/goto_check_c.cpp index a20e0035a97..8afe96ab0a6 100644 --- a/src/ansi-c/goto_check_c.cpp +++ b/src/ansi-c/goto_check_c.cpp @@ -224,7 +224,7 @@ class goto_check_ct void conversion_check(const exprt &, const guardt &); void float_overflow_check(const exprt &, const guardt &); void nan_check(const exprt &, const guardt &); - optionalt rw_ok_check(exprt); + optionalt expand_pointer_checks(exprt); std::string array_name(const exprt &); @@ -1992,14 +1992,14 @@ void goto_check_ct::check(const exprt &expr) check_rec(expr, identity); } -/// expand the r_ok, w_ok and rw_ok predicates -optionalt goto_check_ct::rw_ok_check(exprt expr) +/// expand the r_ok, w_ok, rw_ok, pointer_in_range predicates +optionalt goto_check_ct::expand_pointer_checks(exprt expr) { bool modified = false; for(auto &op : expr.operands()) { - auto op_result = rw_ok_check(op); + auto op_result = expand_pointer_checks(op); if(op_result.has_value()) { op = *op_result; @@ -2026,6 +2026,22 @@ optionalt goto_check_ct::rw_ok_check(exprt expr) simplify(c, ns); return c; } + else if(expr.id() == ID_pointer_in_range) + { + const auto &pointer_in_range_expr = to_pointer_in_range_expr(expr); + + auto expanded = pointer_in_range_expr.lower(); + + // rec. call + auto expanded_rec_opt = expand_pointer_checks(expanded); + if(expanded_rec_opt.has_value()) + expanded = *expanded_rec_opt; + + if(enable_simplify) + simplify(expanded, ns); + + return expanded; + } else if(modified) return std::move(expr); else @@ -2228,7 +2244,7 @@ void goto_check_ct::goto_check( } } - i.transform([this](exprt expr) { return rw_ok_check(expr); }); + i.transform([this](exprt expr) { return expand_pointer_checks(expr); }); for(auto &instruction : new_code.instructions) { diff --git a/src/common b/src/common index d6de5219be7..be9afc1f58d 100644 --- a/src/common +++ b/src/common @@ -35,6 +35,7 @@ endif CXXFLAGS ?= -Wall -O2 ifeq ($(filter-out OSX OSX_Universal,$(BUILD_ENV_)),) CP_CXXFLAGS += -MMD -MP -mmacosx-version-min=10.15 -std=c++11 -stdlib=libc++ + CP_CFLAGS += -mmacosx-version-min=10.15 LINKFLAGS += -mmacosx-version-min=10.15 -stdlib=libc++ LINKNATIVE += -mmacosx-version-min=10.15 -stdlib=libc++ else @@ -215,6 +216,7 @@ CP_CXXFLAGS += $(CXXFLAGS) $(CP_EXTRA_CXXFLAGS) $(INCLUDES) OBJ += $(patsubst %.cpp, %$(OBJEXT), $(filter %.cpp, $(SRC))) OBJ += $(patsubst %.cc, %$(OBJEXT), $(filter %.cc, $(SRC))) +OBJ += $(patsubst %.c, %$(OBJEXT), $(filter %.c, $(SRC))) .SUFFIXES: .cc $(DEPEXT) .cpp @@ -242,6 +244,8 @@ clean: $(patsubst %.cpp, %$(DEPEXT), $(filter %.cpp, $(SRC))) \ $(patsubst %.cc, %$(OBJEXT), $(filter %.cc, $(SRC))) \ $(patsubst %.cc, %$(DEPEXT), $(filter %.cc, $(SRC))) \ + $(patsubst %.c, %$(OBJEXT), $(filter %.c, $(SRC))) \ + $(patsubst %.c, %$(DEPEXT), $(filter %.c, $(SRC))) \ $(CLEANFILES) .PHONY: first_target clean all diff --git a/src/cprover/CMakeLists.txt b/src/cprover/CMakeLists.txt new file mode 100644 index 00000000000..efc757bd34a --- /dev/null +++ b/src/cprover/CMakeLists.txt @@ -0,0 +1,49 @@ +# Library +file(GLOB_RECURSE sources "*.c" "*.cpp" "*.h") +list(REMOVE_ITEM sources + ${CMAKE_CURRENT_SOURCE_DIR}/cprover_main.cpp +) +add_library(cprover-lib ${sources}) + +generic_includes(cprover-lib) + +target_link_libraries(cprover-lib + analyses + ansi-c + assembler + big-int + cpp + goto-checker + goto-instrument-lib + goto-programs + goto-symex + json + json-symtab-language + langapi + linking + pointer-analysis + solvers + statement-list + util + xml +) + +add_if_library(cprover-lib bv_refinement) +add_if_library(cprover-lib jsil) + +# Executable +add_executable(cprover cprover_main.cpp) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +# There is a cyclic dependency between analyses and ansi-c, which the +# Makefile-based build system resolves by using --start-group, --end-group. +# CMake lacks direct support (cf. +# https://gitlab.kitware.com/cmake/cmake/-/issues/21511), so we ensure all +# object files from libanalyses.a remain present. +target_link_libraries(cprover + cprover-lib + -Wl,--whole-archive -Wl,${CMAKE_BINARY_DIR}/lib/libanalyses.a -Wl,--no-whole-archive +) +else() +target_link_libraries(cprover cprover-lib) +endif() +install(TARGETS cprover DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/cprover/Makefile b/src/cprover/Makefile new file mode 100644 index 00000000000..369e37f567c --- /dev/null +++ b/src/cprover/Makefile @@ -0,0 +1,65 @@ +SRC = address_taken.cpp \ + axioms.cpp \ + bv_pointers_wide.cpp \ + c_safety_checks.cpp \ + console.cpp \ + cprover_main.cpp \ + cprover_parse_options.cpp \ + counterexample_found.cpp \ + endianness_map_wide.cpp \ + equality_propagation.cpp \ + find_variables.cpp \ + flatten_ok_expr.cpp \ + format_hooks.cpp \ + free_symbols.cpp \ + help_formatter.cpp \ + instrument_contracts.cpp \ + instrument_given_invariants.cpp \ + may_alias.cpp \ + may_be_same_object.cpp \ + propagate.cpp \ + report_properties.cpp \ + report_traces.cpp \ + sentinel_dll.cpp \ + simplify_state_expr.cpp \ + state.cpp \ + state_encoding.cpp \ + state_encoding_targets.cpp \ + solver.cpp \ + variable_encoding.cpp \ + wcwidth.c \ + # Empty last line + +include ../config.inc +include ../common + +OBJ += ../util/util$(LIBEXT) \ + ../big-int/big-int$(LIBEXT) \ + ../goto-programs/goto-programs$(LIBEXT) \ + ../ansi-c/ansi-c$(LIBEXT) \ + ../linking/linking$(LIBEXT) \ + ../langapi/langapi$(LIBEXT) \ + ../solvers/solvers$(LIBEXT) \ + ../analyses/dirty$(OBJEXT) \ + ../analyses/does_remove_const$(OBJEXT) \ + ../analyses/locals$(OBJEXT) \ + ../analyses/local_bitvector_analysis$(OBJEXT) \ + ../analyses/local_cfg$(OBJEXT) \ + # Empty last line + +INCLUDES= -I .. + +LIBS = + +CLEANFILES = cprover$(EXEEXT) + +all: cprover$(EXEEXT) + +############################################################################### + +cprover$(EXEEXT): $(OBJ) + $(LINKBIN) + +.PHONY: cprover-signed +cprover-signed: cprover$(EXEEXT) + codesign -v -s $(OSX_IDENTITY) cprover$(EXEEXT) diff --git a/src/cprover/address_taken.cpp b/src/cprover/address_taken.cpp new file mode 100644 index 00000000000..b26dd4acf45 --- /dev/null +++ b/src/cprover/address_taken.cpp @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Address Taken + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Address Taken + +#include "address_taken.h" + +#include + +#include "state.h" + +// we look for ❝x❞ that's +// * not the argument of ς(...), i.e., evaluate +// * not the argument of enter_scope_state(?, ?) +// * not the argument of exit_scope_state(?, ?) +// * not on the lhs of [...:=...] +static void find_objects_rec( + const exprt &src, + std::unordered_set &result) +{ + if(src.id() == ID_object_address) + result.insert(to_object_address_expr(src).object_expr()); + else if(src.id() == ID_evaluate) + { + } + else if(src.id() == ID_enter_scope_state) + { + } + else if(src.id() == ID_exit_scope_state) + { + } + else if(src.id() == ID_update_state) + { + const auto &update_state_expr = to_update_state_expr(src); + find_objects_rec(update_state_expr.new_value(), result); + } + else + { + for(auto &op : src.operands()) + find_objects_rec(op, result); + } +} + +std::unordered_set +address_taken(const std::vector &src) +{ + std::unordered_set result; + + for(auto &expr : src) + find_objects_rec(expr, result); + + return result; +} diff --git a/src/cprover/address_taken.h b/src/cprover/address_taken.h new file mode 100644 index 00000000000..f6f24d7a2e9 --- /dev/null +++ b/src/cprover/address_taken.h @@ -0,0 +1,22 @@ +/*******************************************************************\ + +Module: Address Taken + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Address Taken + +#ifndef CPROVER_CPROVER_ADDRESS_TAKEN_H +#define CPROVER_CPROVER_ADDRESS_TAKEN_H + +#include + +#include + +std::unordered_set +address_taken(const std::vector &); + +#endif // CPROVER_CPROVER_ADDRESS_TAKEN_H diff --git a/src/cprover/axioms.cpp b/src/cprover/axioms.cpp new file mode 100644 index 00000000000..13db6bf5ecc --- /dev/null +++ b/src/cprover/axioms.cpp @@ -0,0 +1,776 @@ +/*******************************************************************\ + +Module: Axioms + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Axioms + +#include "axioms.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "simplify_state_expr.h" + +#include + +void axiomst::set_to_true(exprt src) +{ + constraints.push_back(std::move(src)); +} + +void axiomst::set_to_false(exprt src) +{ + set_to_true(not_exprt(src)); +} + +typet axiomst::replace(typet src) +{ + if(src.id() == ID_array) + { + auto &array_type = to_array_type(src); + array_type.element_type() = replace(array_type.element_type()); + array_type.size() = replace(array_type.size()); + return src; + } + else if(src.id() == ID_pointer) + { + to_pointer_type(src).base_type() = + replace(to_pointer_type(src).base_type()); + return src; + } + else + return src; +} + +void axiomst::evaluate_fc() +{ + // quadratic + for(auto a_it = evaluate_exprs.begin(); a_it != evaluate_exprs.end(); a_it++) + { + for(auto b_it = std::next(a_it); b_it != evaluate_exprs.end(); b_it++) + { + if(a_it->state() != b_it->state()) + continue; + + auto a_op = a_it->address(); + auto b_op = typecast_exprt::conditional_cast( + b_it->address(), a_it->address().type()); + auto operands_equal = equal_exprt(a_op, b_op); + auto implication = implies_exprt( + operands_equal, + equal_exprt( + *a_it, typecast_exprt::conditional_cast(*b_it, a_it->type()))); + if(verbose) + std::cout << "EVALUATE: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::is_cstring_fc() +{ + // quadratic + for(auto a_it = is_cstring_exprs.begin(); a_it != is_cstring_exprs.end(); + a_it++) + { + for(auto b_it = std::next(a_it); b_it != is_cstring_exprs.end(); b_it++) + { + if(a_it->state() != b_it->state()) + continue; + auto a_op = a_it->address(); + auto b_op = typecast_exprt::conditional_cast( + b_it->address(), a_it->address().type()); + auto operands_equal = equal_exprt(a_op, b_op); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "IS_CSTRING: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::live_object() +{ + // p = &"string_literal" -> live_object(ς, p) + for(auto a_it = live_object_exprs.begin(); a_it != live_object_exprs.end(); + a_it++) + { + for(auto b_it = address_of_exprs.begin(); b_it != address_of_exprs.end(); + b_it++) + { + auto pointers_equal = same_object(a_it->address(), *b_it); + auto implication = implies_exprt(pointers_equal, *a_it); + if(verbose) + std::cout << "LIVE_OBJECT2: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::live_object_fc() +{ + // quadratic + for(auto a_it = live_object_exprs.begin(); a_it != live_object_exprs.end(); + a_it++) + { + for(auto b_it = std::next(a_it); b_it != live_object_exprs.end(); b_it++) + { + if(a_it->state() != b_it->state()) + continue; + auto operands_equal = same_object(a_it->address(), b_it->address()); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "LIVE_OBJECT1: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::writeable_object() +{ + // p = &"string_literal" -> !writeable_object(ς, p) + for(auto a_it = writeable_object_exprs.begin(); + a_it != writeable_object_exprs.end(); + a_it++) + { + for(auto b_it = address_of_exprs.begin(); b_it != address_of_exprs.end(); + b_it++) + { + auto pointers_equal = same_object(a_it->address(), *b_it); + auto implication = implies_exprt(pointers_equal, not_exprt(*a_it)); + if(verbose) + std::cout << "WRITEABLE_OBJECT2: " << format(implication) << '\n'; + dest << replace(implication); + } + } + + // p = &some_object -> writeable_object(ς, p) as applicable + for(auto a_it = object_address_exprs.begin(); + a_it != object_address_exprs.end(); + a_it++) + { + if(a_it->object_identifier() == "return_value") + continue; + else if(has_prefix(id2string(a_it->object_identifier()), "va_args::")) + continue; + else if(has_prefix(id2string(a_it->object_identifier()), "va_arg::")) + continue; + else if(has_prefix(id2string(a_it->object_identifier()), "va_arg_array::")) + continue; + else if(has_prefix(id2string(a_it->object_identifier()), "old::")) + continue; + + auto &symbol = ns.lookup(a_it->object_expr()); + bool is_const = symbol.type.get_bool(ID_C_constant); + for(auto b_it = writeable_object_exprs.begin(); + b_it != writeable_object_exprs.end(); + b_it++) + { + auto pointers_equal = same_object(*a_it, b_it->address()); + auto rhs = is_const ? static_cast(not_exprt(*b_it)) + : static_cast(*b_it); + auto implication = implies_exprt(pointers_equal, rhs); + if(verbose) + std::cout << "WRITEABLE_OBJECT3: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::writeable_object_fc() +{ + // quadratic + for(auto a_it = writeable_object_exprs.begin(); + a_it != writeable_object_exprs.end(); + a_it++) + { + for(auto b_it = std::next(a_it); b_it != writeable_object_exprs.end(); + b_it++) + { + if(a_it->state() != b_it->state()) + continue; + auto operands_equal = same_object(a_it->address(), b_it->address()); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "WRITEABLE_OBJECT1: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::is_dynamic_object_fc() +{ + // quadratic + for(auto a_it = is_dynamic_object_exprs.begin(); + a_it != is_dynamic_object_exprs.end(); + a_it++) + { + for(auto b_it = std::next(a_it); b_it != is_dynamic_object_exprs.end(); + b_it++) + { + if(a_it->state() != b_it->state()) + continue; + auto operands_equal = same_object(a_it->address(), b_it->address()); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "IS_DYNAMIC_OBJECT: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::object_size() +{ + for(const auto &src : object_size_exprs) + { + auto src_simplified = simplify_state_expr_node(src, address_taken, ns); + if(src_simplified != src) + { + auto equal = equal_exprt(src, src_simplified); + if(verbose) + std::cout << "OBJECT_SIZE1: " << format(equal) << '\n'; + dest << replace(equal); + } + } + + // p = &"string_literal" -> object_size(ς, p) = strlen("string_literal")+1 + for(auto a_it = object_size_exprs.begin(); a_it != object_size_exprs.end(); + a_it++) + { + for(auto b_it = address_of_exprs.begin(); b_it != address_of_exprs.end(); + b_it++) + { + if(b_it->object().id() == ID_string_constant) + { + auto &string_constant = to_string_constant(b_it->object()); + auto pointers_equal = same_object(a_it->address(), *b_it); + auto size_equal = equal_exprt( + *a_it, + from_integer(string_constant.get_value().size() + 1, a_it->type())); + auto implication = implies_exprt(pointers_equal, size_equal); + if(verbose) + std::cout << "OBJECT_SIZE2: " << format(implication) << '\n'; + dest << replace(implication); + } + } + } +} + +void axiomst::object_size_fc() +{ + // quadratic + for(auto a_it = object_size_exprs.begin(); a_it != object_size_exprs.end(); + a_it++) + { + for(auto b_it = std::next(a_it); b_it != object_size_exprs.end(); b_it++) + { + if(a_it->state() != b_it->state()) + continue; + auto operands_equal = same_object(a_it->address(), b_it->address()); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "OBJECT_SIZE: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::ok_fc() +{ + // quadratic + for(auto a_it = ok_exprs.begin(); a_it != ok_exprs.end(); a_it++) + { + for(auto b_it = std::next(a_it); b_it != ok_exprs.end(); b_it++) + { + if(a_it->id() != b_it->id()) + continue; + if(a_it->state() != b_it->state()) + continue; + if(a_it->size() != b_it->size()) + continue; + auto a_op = a_it->address(); + auto b_op = typecast_exprt::conditional_cast( + b_it->address(), a_it->address().type()); + auto operands_equal = equal_exprt(a_op, b_op); + auto implication = + implies_exprt(operands_equal, equal_exprt(*a_it, *b_it)); + if(verbose) + std::cout << "OK: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +void axiomst::initial_state() +{ + for(const auto &initial_state_expr : initial_state_exprs) + { + // initial_state(ς) -> ¬live_object(ς, o) for any stack-allocated object o + for(const auto &object : address_taken) + { + const symbolt *symbol; + if(ns.lookup(object.get_identifier(), symbol)) + continue; + + if(symbol->is_static_lifetime || !symbol->is_lvalue) + continue; + + auto live_object = state_live_object_exprt( + initial_state_expr.op(), object_address_exprt(object)); + live_object_exprs.insert(live_object); + auto implication = + implies_exprt(initial_state_expr, not_exprt(live_object)); + if(verbose) + std::cout << "INITIAL_STATE: " << format(implication) << '\n'; + dest << replace(implication); + } + } +} + +exprt axiomst::translate(exprt src) const +{ + auto r = replacement_map.find(src); + if(r == replacement_map.end()) + return src; + else + return r->second; +} + +exprt axiomst::replace(exprt src) +{ + src.type() = replace(src.type()); + + if( + src.id() == ID_initial_state || src.id() == ID_evaluate || + src.id() == ID_state_is_cstring || src.id() == ID_state_cstrlen || + src.id() == ID_state_is_sentinel_dll || + src.id() == ID_state_is_dynamic_object || + src.id() == ID_state_object_size || src.id() == ID_state_live_object || + src.id() == ID_state_writeable_object || src.id() == ID_state_r_ok || + src.id() == ID_state_w_ok || src.id() == ID_state_rw_ok || + src.id() == ID_allocate || src.id() == ID_reallocate) + { + auto r = replacement_map.find(src); + if(r == replacement_map.end()) + { + auto counter = ++counters[src.id()]; + auto s = + symbol_exprt(src.id_string() + std::to_string(counter), src.type()); + replacement_map.emplace(src, s); + + if(src.id() == ID_state_is_cstring) + { + if(verbose) + std::cout << "R " << format(s) << " --> " << format(src) << '\n'; + } + + return std::move(s); + } + else + return r->second; + } + + for(auto &op : src.operands()) + op = replace(op); + + return src; +} + +void axiomst::node(const exprt &src) +{ + if(src.id() == ID_state_is_cstring) + { + add(to_state_is_cstring_expr(src), false); + } + else if(src.id() == ID_state_is_sentinel_dll) + { + auto &is_sentinel_dll_expr = to_state_is_sentinel_dll_expr(src); + is_sentinel_dll_exprs.insert(is_sentinel_dll_expr); + + auto ok_expr_h_size_opt = size_of_expr( + to_pointer_type(is_sentinel_dll_expr.head().type()).base_type(), ns); + + auto ok_expr_h = state_ok_exprt( + ID_state_rw_ok, + is_sentinel_dll_expr.state(), + is_sentinel_dll_expr.head(), + *ok_expr_h_size_opt); + + auto ok_expr_t_size_opt = size_of_expr( + to_pointer_type(is_sentinel_dll_expr.tail().type()).base_type(), ns); + + auto ok_expr_t = state_ok_exprt( + ID_state_rw_ok, + is_sentinel_dll_expr.state(), + is_sentinel_dll_expr.tail(), + *ok_expr_t_size_opt); + + { + // is_sentinel_dll(ς, h, t) ⇒ rw_ok(ς, h) ∧ rw_ok(ς, t) + auto instance1 = + replace(implies_exprt(src, and_exprt(ok_expr_h, ok_expr_t))); + if(verbose) + std::cout << "AXIOM-is-sentinel-dll-1: " << format(instance1) << "\n"; + dest << instance1; + } + + { + // rw_ok(h) ∧ rw_ok(t) ∧ ς(h->n)=t ∧ ς(t->p)=h ⇒ is_sentinel_dll(ς, h, t) + auto head_next = sentinel_dll_next( + is_sentinel_dll_expr.state(), is_sentinel_dll_expr.head(), ns); + + auto tail_prev = sentinel_dll_prev( + is_sentinel_dll_expr.state(), is_sentinel_dll_expr.tail(), ns); + + if(head_next.has_value() && tail_prev.has_value()) + { + auto head_next_is_tail = + equal_exprt(*head_next, is_sentinel_dll_expr.tail()); + + auto tail_prev_is_head = + equal_exprt(*tail_prev, is_sentinel_dll_expr.head()); + + auto instance2 = replace(implies_exprt( + and_exprt( + ok_expr_h, ok_expr_t /*, head_next_is_tail, tail_prev_is_head*/), + src)); + + if(verbose) + std::cout << "AXIOM-is-sentinel-dll-2: " << format(instance2) << "\n"; + dest << instance2; + } + } + } + else if(src.id() == ID_evaluate) + { + const auto &evaluate_expr = to_evaluate_expr(src); + evaluate_exprs.insert(evaluate_expr); + } + else if(src.id() == ID_state_live_object) + { + const auto &live_object_expr = to_state_live_object_expr(src); + live_object_exprs.insert(live_object_expr); + + { + // live_object(ς, p) --> p!=0 + auto instance = replace(implies_exprt( + src, not_exprt(null_pointer(live_object_expr.address())))); + if(verbose) + std::cout << "AXIOMc: " << format(instance) << "\n"; + dest << instance; + } + } + else if(src.id() == ID_state_writeable_object) + { + const auto &writeable_object_expr = to_state_writeable_object_expr(src); + writeable_object_exprs.insert(writeable_object_expr); + } + else if(src.id() == ID_state_is_dynamic_object) + { + const auto &is_dynamic_object_expr = to_state_is_dynamic_object_expr(src); + is_dynamic_object_exprs.insert(is_dynamic_object_expr); + } + else if(src.id() == ID_allocate) + { + const auto &allocate_expr = to_allocate_expr(src); + + // May need to consider failure. + // live_object(ς, allocate(ς, s)) + auto live_object_expr = + state_live_object_exprt(allocate_expr.state(), allocate_expr); + live_object_exprs.insert(live_object_expr); + auto instance1 = replace(live_object_expr); + if(verbose) + std::cout << "ALLOCATE1: " << format(instance1) << "\n"; + dest << instance1; + + // writeable_object(ς, allocate(ς, s)) + auto writeable_object_expr = + state_writeable_object_exprt(allocate_expr.state(), allocate_expr); + writeable_object_exprs.insert(writeable_object_expr); + auto instance2 = replace(writeable_object_expr); + if(verbose) + std::cout << "ALLOCATE2: " << format(instance2) << "\n"; + dest << instance2; + + // object_size(ς, allocate(ς, s)) = s + auto object_size_expr = state_object_size_exprt( + allocate_expr.state(), allocate_expr, allocate_expr.size().type()); + object_size_exprs.insert(object_size_expr); + auto instance3 = + replace(equal_exprt(object_size_expr, allocate_expr.size())); + if(verbose) + std::cout << "ALLOCATE3: " << format(instance3) << "\n"; + dest << instance3; + + // pointer_offset(allocate(ς, s)) = 0 + auto pointer_offset_expr = pointer_offset(allocate_expr); + // pointer_offset_exprs.insert(pointer_offset_expr); + auto instance4 = replace(equal_exprt( + pointer_offset_expr, from_integer(0, pointer_offset_expr.type()))); + if(verbose) + std::cout << "ALLOCATE4: " << format(instance4) << "\n"; + dest << instance4; + + // is_dynamic_object(ς, allocate(ς, s)) + auto is_dynamic_object_expr = + state_is_dynamic_object_exprt(allocate_expr.state(), allocate_expr); + is_dynamic_object_exprs.insert(is_dynamic_object_expr); + auto instance5 = replace(is_dynamic_object_expr); + if(verbose) + std::cout << "ALLOCATE5: " << format(instance5) << "\n"; + dest << instance5; + } + else if(src.id() == ID_reallocate) + { + const auto &reallocate_expr = to_reallocate_expr(src); + + // May need to consider failure. + // live_object(ς, reallocate(ς, a, s)) + auto live_object_expr = + state_live_object_exprt(reallocate_expr.state(), reallocate_expr); + live_object_exprs.insert(live_object_expr); + auto instance1 = replace(live_object_expr); + if(verbose) + std::cout << "REALLOCATE1: " << format(instance1) << "\n"; + dest << instance1; + + // object_size(ς, reallocate(ς, a, s)) = s + auto object_size_expr = state_object_size_exprt( + reallocate_expr.state(), reallocate_expr, reallocate_expr.size().type()); + object_size_exprs.insert(object_size_expr); + auto instance2 = + replace(equal_exprt(object_size_expr, reallocate_expr.size())); + if(verbose) + std::cout << "REALLOCATE2: " << format(instance2) << "\n"; + dest << instance2; + + // pointer_offset(reallocate(ς, a, s)) = 0 + auto pointer_offset_expr = pointer_offset(reallocate_expr); + // pointer_offset_exprs.insert(pointer_offset_expr); + auto instance3 = replace(equal_exprt( + pointer_offset_expr, from_integer(0, pointer_offset_expr.type()))); + if(verbose) + std::cout << "REALLOCATE3: " << format(instance3) << "\n"; + dest << instance3; + + // is_dynamic_object(ς, reallocate(ς, s)) + auto is_dynamic_object_expr = + state_is_dynamic_object_exprt(reallocate_expr.state(), reallocate_expr); + is_dynamic_object_exprs.insert(is_dynamic_object_expr); + auto instance4 = replace(is_dynamic_object_expr); + if(verbose) + std::cout << "REALLOCATE4: " << format(instance4) << "\n"; + dest << instance4; + } + else if(src.id() == ID_deallocate_state) + { +#if 0 + // Disabled since any other thread may have reclaimed + // the memory. + const auto &deallocate_state_expr = to_deallocate_state_expr(src); + + // ¬live_object(deallocate(ς, p), p) + auto live_object_expr = state_live_object_exprt( + deallocate_state_expr, deallocate_state_expr.address()); + live_object_exprs.insert(live_object_expr); + auto instance1 = replace(not_exprt(live_object_expr)); + if(verbose) + std::cout << "DEALLOCATE1: " << format(instance1) << "\n"; + dest << instance1; +#endif + } + else if(src.id() == ID_address_of) + { + address_of_exprs.insert(to_address_of_expr(src)); + } + else if(src.id() == ID_object_address) + { + object_address_exprs.insert(to_object_address_expr(src)); + } + else if(src.id() == ID_state_object_size) + { + const auto &object_size_expr = to_state_object_size_expr(src); + object_size_exprs.insert(object_size_expr); + } + else if(src.id() == ID_initial_state) + { + initial_state_exprs.insert(to_initial_state_expr(src)); + } + else if( + src.id() == ID_state_r_ok || src.id() == ID_state_w_ok || + src.id() == ID_state_rw_ok) + { + add(to_state_ok_expr(src)); + } +} + +void axiomst::add(const state_ok_exprt &ok_expr) +{ + if(!ok_exprs.insert(ok_expr).second) + return; // already there + + const auto &state = ok_expr.state(); + const auto &pointer = ok_expr.address(); + const auto &size = ok_expr.size(); + + { + // X_ok(p, s) <--> + // live_object(p) + // ∧ offset(p)+s≤object_size(p) + // ∧ writeable_object(p) if applicable + auto live_object = state_live_object_exprt(state, pointer); + live_object_exprs.insert(live_object); + auto live_object_simplified = + simplify_state_expr_node(live_object, address_taken, ns); + + auto writeable_object = state_writeable_object_exprt(state, pointer); + writeable_object_exprs.insert(writeable_object); + + auto writeable_object_simplified = + simplify_state_expr_node(writeable_object, address_taken, ns); + + auto ssize_type = signed_size_type(); + auto offset = pointer_offset_exprt(pointer, ssize_type); + auto offset_simplified = + simplify_state_expr_node(offset, address_taken, ns); + // auto lower = binary_relation_exprt( + // offset_simplified, ID_ge, from_integer(0, ssize_type)); + + auto size_type = ::size_type(); + + // extend one bit, to cover overflow case + auto size_type_ext = unsignedbv_typet(size_type.get_width() + 1); + + auto object_size = state_object_size_exprt(state, pointer, size_type); + object_size_exprs.insert(object_size); + + auto object_size_casted = typecast_exprt(object_size, size_type_ext); + + auto offset_casted_to_unsigned = + typecast_exprt::conditional_cast(offset_simplified, size_type); + auto offset_extended = typecast_exprt::conditional_cast( + offset_casted_to_unsigned, size_type_ext); + auto size_casted = typecast_exprt::conditional_cast(size, size_type_ext); + auto upper = binary_relation_exprt( + plus_exprt(offset_extended, size_casted), ID_le, object_size_casted); + + auto conjunction = and_exprt(live_object_simplified, upper); + + if(ok_expr.id() == ID_state_w_ok || ok_expr.id() == ID_state_rw_ok) + conjunction.add_to_operands(std::move(writeable_object)); + + auto instance = replace(equal_exprt(ok_expr, conjunction)); + + if(verbose) + std::cout << "AXIOMd: " << format(instance) << "\n"; + dest << instance; + } + + { + // X_ok(ς, p) --> p!=0 + auto instance = + replace(implies_exprt(ok_expr, not_exprt(null_pointer(pointer)))); + if(verbose) + std::cout << "AXIOMe: " << format(instance) << "\n"; + dest << instance; + } +} + +void axiomst::add(const state_is_cstring_exprt &is_cstring_expr, bool recursive) +{ + if(!is_cstring_exprs.insert(is_cstring_expr).second) + return; // already there + + { + // is_cstring(ς, p) ⇒ r_ok(ς, p, 1) + auto ok_expr = ternary_exprt( + ID_state_r_ok, + is_cstring_expr.state(), + is_cstring_expr.address(), + from_integer(1, size_type()), + bool_typet()); + + auto instance1 = replace(implies_exprt(is_cstring_expr, ok_expr)); + if(verbose) + std::cout << "AXIOMa1: " << format(instance1) << "\n"; + dest << instance1; + + auto ok_simplified = simplify_state_expr(ok_expr, address_taken, ns); + ok_simplified.visit_pre([this](const exprt &src) { node(src); }); + auto instance2 = replace(implies_exprt(is_cstring_expr, ok_simplified)); + if(verbose) + std::cout << "AXIOMa2: " << format(instance2) << "\n"; + dest << instance2; + } + + if(!recursive) + { + // is_cstring(ς, p) --> is_cstring(ς, p + 1) ∨ ς(p)=0 + auto state = is_cstring_expr.state(); + auto p = is_cstring_expr.address(); + auto one = from_integer(1, signed_size_type()); + auto p_plus_one = plus_exprt(p, one, is_cstring_expr.op1().type()); + auto is_cstring_plus_one = state_is_cstring_exprt(state, p_plus_one); + auto char_type = to_pointer_type(p.type()).base_type(); + auto zero = from_integer(0, char_type); + auto star_p = evaluate_exprt(state, p, char_type); + auto is_zero = equal_exprt(star_p, zero); + auto instance = replace( + implies_exprt(is_cstring_expr, or_exprt(is_cstring_plus_one, is_zero))); + if(verbose) + std::cout << "AXIOMb: " << format(instance) << "\n"; + dest << instance; + evaluate_exprs.insert(star_p); + add(is_cstring_plus_one, true); // rec. call + } +} + +void axiomst::emit() +{ + for(auto &constraint : constraints) + { + constraint.visit_pre([this](const exprt &src) { node(src); }); + + auto constraint_replaced = replace(constraint); + if(verbose) + { + std::cout << "CONSTRAINT1: " << format(constraint) << "\n"; + std::cout << "CONSTRAINT2: " << format(constraint_replaced) << "\n"; + } + dest << constraint_replaced; + } + + object_size(); + live_object(); + writeable_object(); + initial_state(); + + // functional consistency is done last + evaluate_fc(); + is_cstring_fc(); + ok_fc(); + live_object_fc(); + writeable_object_fc(); + object_size_fc(); + is_dynamic_object_fc(); +} diff --git a/src/cprover/axioms.h b/src/cprover/axioms.h new file mode 100644 index 00000000000..d3f804cd7dc --- /dev/null +++ b/src/cprover/axioms.h @@ -0,0 +1,104 @@ +/*******************************************************************\ + +Module: Axioms + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Axioms + +#ifndef CPROVER_CPROVER_AXIOMS_H +#define CPROVER_CPROVER_AXIOMS_H + +#include + +#include + +#include "sentinel_dll.h" +#include "state.h" + +#include +#include +#include +#include +#include + +class axiomst +{ +public: + axiomst( + decision_proceduret &__dest, + const std::unordered_set &__address_taken, + bool __verbose, + const namespacet &__ns) + : dest(__dest), address_taken(__address_taken), verbose(__verbose), ns(__ns) + { + } + + void set_to_true(exprt); + void set_to_false(exprt); + void emit(); + + exprt translate(exprt) const; + +protected: + decision_proceduret &dest; + const std::unordered_set &address_taken; + bool verbose = false; + const namespacet &ns; + + std::vector constraints; + + exprt replace(exprt); + typet replace(typet); + std::unordered_map replacement_map; + std::map counters; + + void node(const exprt &); + + std::set address_of_exprs; + + std::set object_address_exprs; + + std::set ok_exprs; + void add(const state_ok_exprt &); + void ok_fc(); + + std::set evaluate_exprs; + void evaluate_fc(); + + std::set is_cstring_exprs; + void add(const state_is_cstring_exprt &, bool recursive); + void is_cstring_fc(); + + std::set is_dynamic_object_exprs; + void is_dynamic_object_fc(); + + std::set live_object_exprs; + void live_object(); + void live_object_fc(); + + std::set writeable_object_exprs; + void writeable_object(); + void writeable_object_fc(); + + std::set object_size_exprs; + void object_size(); + void object_size_fc(); + + std::set is_sentinel_dll_exprs; + void is_sentinel_dll(); + + std::set initial_state_exprs; + void initial_state(); +}; + +static inline axiomst &operator<<(axiomst &axioms, exprt src) +{ + axioms.set_to_true(std::move(src)); + return axioms; +} + +#endif // CPROVER_CPROVER_AXIOMS_H diff --git a/src/cprover/bv_pointers_wide.cpp b/src/cprover/bv_pointers_wide.cpp new file mode 100644 index 00000000000..a8ca822bda4 --- /dev/null +++ b/src/cprover/bv_pointers_wide.cpp @@ -0,0 +1,975 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "bv_pointers_wide.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::size_t bv_pointers_widet::get_object_width(const pointer_typet &type) const +{ + return type.get_width(); +} + +std::size_t bv_pointers_widet::get_offset_width(const pointer_typet &type) const +{ + const std::size_t pointer_width = 2 * type.get_width(); + const std::size_t object_width = get_object_width(type); + PRECONDITION(pointer_width >= object_width); + return pointer_width - object_width; +} + +bvt bv_pointers_widet::object_literals(const bvt &bv, const pointer_typet &type) + const +{ + const std::size_t offset_width = get_offset_width(type); + const std::size_t object_width = get_object_width(type); + PRECONDITION(bv.size() >= offset_width + object_width); + + return bvt( + bv.begin() + offset_width, bv.begin() + offset_width + object_width); +} + +bvt bv_pointers_widet::offset_literals(const bvt &bv, const pointer_typet &type) + const +{ + const std::size_t offset_width = get_offset_width(type); + PRECONDITION(bv.size() >= offset_width); + + return bvt(bv.begin(), bv.begin() + offset_width); +} + +bvt bv_pointers_widet::object_offset_encoding( + const bvt &object, + const bvt &offset) +{ + bvt result; + result.reserve(offset.size() + object.size()); + result.insert(result.end(), offset.begin(), offset.end()); + result.insert(result.end(), object.begin(), object.end()); + + return result; +} + +literalt bv_pointers_widet::convert_rest(const exprt &expr) +{ + PRECONDITION(expr.type().id() == ID_bool); + + const exprt::operandst &operands = expr.operands(); + + if(expr.id() == ID_is_invalid_pointer) + { + if(operands.size() == 1 && operands[0].type().id() == ID_pointer) + { + const bvt &bv = convert_bv(operands[0]); + + if(!bv.empty()) + { + const pointer_typet &type = to_pointer_type(operands[0].type()); + bvt object_bv = object_literals(bv, type); + + bvt invalid_bv = object_literals( + encode(pointer_logic.get_invalid_object(), type), type); + + const std::size_t object_bits = get_object_width(type); + + bvt equal_invalid_bv; + equal_invalid_bv.reserve(object_bits); + + for(std::size_t i = 0; i < object_bits; i++) + { + equal_invalid_bv.push_back(prop.lequal(object_bv[i], invalid_bv[i])); + } + + return prop.land(equal_invalid_bv); + } + } + } + else if(expr.id() == ID_is_dynamic_object) + { + if(operands.size() == 1 && operands[0].type().id() == ID_pointer) + { + // we postpone + literalt l = prop.new_variable(); + + postponed_list.emplace_back(bvt{1, l}, convert_bv(operands[0]), expr); + + return l; + } + } + else if( + expr.id() == ID_lt || expr.id() == ID_le || expr.id() == ID_gt || + expr.id() == ID_ge) + { + if( + operands.size() == 2 && operands[0].type().id() == ID_pointer && + operands[1].type().id() == ID_pointer) + { + const bvt &bv0 = convert_bv(operands[0]); + const bvt &bv1 = convert_bv(operands[1]); + + const pointer_typet &type0 = to_pointer_type(operands[0].type()); + bvt offset_bv0 = offset_literals(bv0, type0); + + const pointer_typet &type1 = to_pointer_type(operands[1].type()); + bvt offset_bv1 = offset_literals(bv1, type1); + + // Comparison over pointers to distinct objects is undefined behavior in + // C; we choose to always produce "false" in such a case. Alternatively, + // we could do a comparison over the integer representation of a pointer + + // do the same-object-test via an expression as this may permit re-using + // already cached encodings of the equality test + const exprt same_object = ::same_object(operands[0], operands[1]); + const literalt same_object_lit = convert(same_object); + if(same_object_lit.is_false()) + return same_object_lit; + + return prop.land( + same_object_lit, + bv_utils.rel( + offset_bv0, + expr.id(), + offset_bv1, + bv_utilst::representationt:: + UNSIGNED)); // Note the UNSIGNED comparison + } + } + + return SUB::convert_rest(expr); +} + +bv_pointers_widet::bv_pointers_widet( + const namespacet &_ns, + propt &_prop, + message_handlert &message_handler, + bool get_array_constraints) + : boolbvt(_ns, _prop, message_handler, get_array_constraints), + pointer_logic(_ns) +{ +} + +optionalt bv_pointers_widet::convert_address_of_rec(const exprt &expr) +{ + if(expr.id() == ID_symbol) + { + return add_addr(expr); + } + else if(expr.id() == ID_label) + { + return add_addr(expr); + } + else if(expr.id() == ID_null_object) + { + pointer_typet pt = pointer_type(expr.type()); + return encode(pointer_logic.get_null_object(), pt); + } + else if(expr.id() == ID_index) + { + const index_exprt &index_expr = to_index_expr(expr); + const exprt &array = index_expr.array(); + const exprt &index = index_expr.index(); + const auto &array_type = to_array_type(array.type()); + + pointer_typet type = pointer_type(expr.type()); + const std::size_t bits = boolbv_width(type); + + bvt bv; + + // recursive call + if(array_type.id() == ID_pointer) + { + // this should be gone + bv = convert_pointer_type(array); + CHECK_RETURN(bv.size() == bits); + } + else if( + array_type.id() == ID_array || array_type.id() == ID_string_constant) + { + auto bv_opt = convert_address_of_rec(array); + if(!bv_opt.has_value()) + return {}; + bv = std::move(*bv_opt); + CHECK_RETURN(bv.size() == bits); + } + else + UNREACHABLE; + + // get size + auto size = pointer_offset_size(array_type.element_type(), ns); + CHECK_RETURN(size.has_value() && *size >= 0); + + bv = offset_arithmetic(type, bv, *size, index); + CHECK_RETURN(bv.size() == bits); + + return std::move(bv); + } + else if( + expr.id() == ID_byte_extract_little_endian || + expr.id() == ID_byte_extract_big_endian) + { + const auto &byte_extract_expr = to_byte_extract_expr(expr); + + // recursive call + auto bv_opt = convert_address_of_rec(byte_extract_expr.op()); + if(!bv_opt.has_value()) + return {}; + + pointer_typet type = pointer_type(expr.type()); + const std::size_t bits = boolbv_width(type); + CHECK_RETURN(bv_opt->size() == bits); + + bvt bv = offset_arithmetic(type, *bv_opt, 1, byte_extract_expr.offset()); + CHECK_RETURN(bv.size() == bits); + return std::move(bv); + } + else if(expr.id() == ID_member) + { + const member_exprt &member_expr = to_member_expr(expr); + const exprt &struct_op = member_expr.compound(); + const typet &struct_op_type = ns.follow(struct_op.type()); + + // recursive call + auto bv_opt = convert_address_of_rec(struct_op); + if(!bv_opt.has_value()) + return {}; + + bvt bv = std::move(*bv_opt); + if(struct_op_type.id() == ID_struct) + { + auto offset = member_offset( + to_struct_type(struct_op_type), member_expr.get_component_name(), ns); + CHECK_RETURN(offset.has_value()); + + // add offset + pointer_typet type = pointer_type(expr.type()); + bv = offset_arithmetic(type, bv, *offset); + } + else + { + INVARIANT( + struct_op_type.id() == ID_union, + "member expression should operate on struct or union"); + // nothing to do, all members have offset 0 + } + + return std::move(bv); + } + else if( + expr.id() == ID_constant || expr.id() == ID_string_constant || + expr.id() == ID_array) + { // constant + return add_addr(expr); + } + else if(expr.id() == ID_if) + { + const if_exprt &ifex = to_if_expr(expr); + + literalt cond = convert(ifex.cond()); + + bvt bv1, bv2; + + auto bv1_opt = convert_address_of_rec(ifex.true_case()); + if(!bv1_opt.has_value()) + return {}; + + auto bv2_opt = convert_address_of_rec(ifex.false_case()); + if(!bv2_opt.has_value()) + return {}; + + return bv_utils.select(cond, *bv1_opt, *bv2_opt); + } + + return {}; +} + +bvt bv_pointers_widet::convert_pointer_type(const exprt &expr) +{ + const pointer_typet &type = to_pointer_type(expr.type()); + + const std::size_t bits = boolbv_width(expr.type()); + + if(expr.id() == ID_symbol) + { + const irep_idt &identifier = to_symbol_expr(expr).get_identifier(); + + return map.get_literals(identifier, type, bits); + } + else if(expr.id() == ID_nondet_symbol) + { + return prop.new_variables(bits); + } + else if(expr.id() == ID_typecast) + { + const typecast_exprt &typecast_expr = to_typecast_expr(expr); + + const exprt &op = typecast_expr.op(); + const typet &op_type = op.type(); + + if(op_type.id() == ID_pointer) + return convert_bv(op); + else if( + can_cast_type(op_type) || op_type.id() == ID_bool || + op_type.id() == ID_c_enum || op_type.id() == ID_c_enum_tag) + { + // Cast from a bitvector type 'i' to pointer. + // 1) top bit not set: NULL + i, zero extended + // 2) top bit set: numbered pointer + const bvt &op_bv = convert_bv(op); + auto top_bit = op_bv.back(); + const auto numbered_pointer_bv = prop.new_variables(bits); + + for(std::size_t i = 1; i < numbered_pointers.size(); i++) + { + auto cond = bv_utils.equal( + op_bv, + bv_utilst::concatenate( + bv_utilst::build_constant(i, op_bv.size() - 1), + {const_literal(true)})); + bv_utils.cond_implies_equal( + cond, + bv_utilst::zero_extension(numbered_pointers[i], bits), + numbered_pointer_bv); + } + + auto null_object_bv = object_offset_encoding( + bv_utilst::build_constant( + pointer_logic.get_null_object(), get_object_width(type)), + bv_utilst::concatenate( + bv_utilst::zero_extension(op_bv, get_offset_width(type) - 1), + {const_literal(false)})); + + return bv_utils.select(top_bit, numbered_pointer_bv, null_object_bv); + } + } + else if(expr.id() == ID_if) + { + return SUB::convert_if(to_if_expr(expr)); + } + else if(expr.id() == ID_index) + { + return SUB::convert_index(to_index_expr(expr)); + } + else if(expr.id() == ID_member) + { + return SUB::convert_member(to_member_expr(expr)); + } + else if(expr.id() == ID_address_of) + { + const address_of_exprt &address_of_expr = to_address_of_expr(expr); + + auto bv_opt = convert_address_of_rec(address_of_expr.op()); + if(!bv_opt.has_value()) + return conversion_failed(address_of_expr); + + CHECK_RETURN(bv_opt->size() == bits); + return *bv_opt; + } + else if(expr.id() == ID_object_address) + { + const auto &object_address_expr = to_object_address_expr(expr); + return add_addr(object_address_expr.object_expr()); + } + else if(expr.id() == ID_constant) + { + const constant_exprt &c = to_constant_expr(expr); + + if(is_null_pointer(c)) + return encode(pointer_logic.get_null_object(), type); + else + { + mp_integer i = bvrep2integer(c.get_value(), bits, false); + return bv_utils.build_constant(i, bits); + } + } + else if(expr.id() == ID_plus) + { + // this has to be pointer plus integer + + const plus_exprt &plus_expr = to_plus_expr(expr); + + bvt bv; + + mp_integer size = 0; + std::size_t count = 0; + + forall_operands(it, plus_expr) + { + if(it->type().id() == ID_pointer) + { + count++; + bv = convert_bv(*it); + CHECK_RETURN(bv.size() == bits); + + typet pointer_base_type = to_pointer_type(it->type()).base_type(); + + if(pointer_base_type.id() == ID_empty) + { + // This is a gcc extension. + // https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html + size = 1; + } + else + { + auto size_opt = pointer_offset_size(pointer_base_type, ns); + CHECK_RETURN(size_opt.has_value() && *size_opt >= 0); + size = *size_opt; + } + } + } + + INVARIANT( + count == 1, + "there should be exactly one pointer-type operand in a pointer-type sum"); + + const std::size_t offset_bits = get_offset_width(type); + bvt sum = bv_utils.build_constant(0, offset_bits); + + forall_operands(it, plus_expr) + { + if(it->type().id() == ID_pointer) + continue; + + if(it->type().id() != ID_unsignedbv && it->type().id() != ID_signedbv) + { + return conversion_failed(plus_expr); + } + + bv_utilst::representationt rep = it->type().id() == ID_signedbv + ? bv_utilst::representationt::SIGNED + : bv_utilst::representationt::UNSIGNED; + + bvt op = convert_bv(*it); + CHECK_RETURN(!op.empty()); + + op = bv_utils.extension(op, offset_bits, rep); + + sum = bv_utils.add(sum, op); + } + + return offset_arithmetic(type, bv, size, sum); + } + else if(expr.id() == ID_minus) + { + // this is pointer-integer + + const minus_exprt &minus_expr = to_minus_expr(expr); + + INVARIANT( + minus_expr.lhs().type().id() == ID_pointer, + "first operand should be of pointer type"); + + if( + minus_expr.rhs().type().id() != ID_unsignedbv && + minus_expr.rhs().type().id() != ID_signedbv) + { + return conversion_failed(minus_expr); + } + + const unary_minus_exprt neg_op1(minus_expr.rhs()); + + const bvt &bv = convert_bv(minus_expr.lhs()); + + typet pointer_base_type = + to_pointer_type(minus_expr.lhs().type()).base_type(); + mp_integer element_size; + + if(pointer_base_type.id() == ID_empty) + { + // This is a gcc extension. + // https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html + element_size = 1; + } + else + { + auto element_size_opt = pointer_offset_size(pointer_base_type, ns); + CHECK_RETURN(element_size_opt.has_value() && *element_size_opt > 0); + element_size = *element_size_opt; + } + + return offset_arithmetic(type, bv, element_size, neg_op1); + } + else if( + expr.id() == ID_byte_extract_little_endian || + expr.id() == ID_byte_extract_big_endian) + { + return SUB::convert_byte_extract(to_byte_extract_expr(expr)); + } + else if( + expr.id() == ID_byte_update_little_endian || + expr.id() == ID_byte_update_big_endian) + { + return SUB::convert_byte_update(to_byte_update_expr(expr)); + } + else if(expr.id() == ID_field_address) + { + const auto &field_address_expr = to_field_address_expr(expr); + const typet &compound_type = ns.follow(field_address_expr.compound_type()); + + // recursive call + auto bv = convert_bitvector(field_address_expr.base()); + + if(compound_type.id() == ID_struct) + { + auto offset = member_offset( + to_struct_type(compound_type), field_address_expr.component_name(), ns); + CHECK_RETURN(offset.has_value()); + + // add offset + bv = offset_arithmetic(field_address_expr.type(), bv, *offset); + } + else if(compound_type.id() == ID_union) + { + // nothing to do, all fields have offset 0 + } + else + { + INVARIANT(false, "field address expressions operate on struct or union"); + } + + return bv; + } + else if(expr.id() == ID_element_address) + { + const auto &element_address_expr = to_element_address_expr(expr); + + // recursive call + auto bv = convert_bitvector(element_address_expr.base()); + + // get element size + auto size = pointer_offset_size(element_address_expr.element_type(), ns); + CHECK_RETURN(size.has_value() && *size >= 0); + + // add offset + bv = offset_arithmetic( + element_address_expr.type(), bv, *size, element_address_expr.index()); + + return bv; + } + + return conversion_failed(expr); +} + +static bool is_pointer_subtraction(const exprt &expr) +{ + if(expr.id() != ID_minus) + return false; + + const auto &minus_expr = to_minus_expr(expr); + + return minus_expr.lhs().type().id() == ID_pointer && + minus_expr.rhs().type().id() == ID_pointer; +} + +bvt bv_pointers_widet::convert_bitvector(const exprt &expr) +{ + if(expr.type().id() == ID_pointer) + return convert_pointer_type(expr); + + if(is_pointer_subtraction(expr)) + { + std::size_t width = boolbv_width(expr.type()); + + if(width == 0) + return conversion_failed(expr); + + // pointer minus pointer is subtraction over the offset divided by element + // size, iff the pointers point to the same object + const auto &minus_expr = to_minus_expr(expr); + + // do the same-object-test via an expression as this may permit re-using + // already cached encodings of the equality test + const exprt same_object = ::same_object(minus_expr.lhs(), minus_expr.rhs()); + const literalt same_object_lit = convert(same_object); + + // compute the object size (again, possibly using cached results) + const exprt object_size = ::object_size(minus_expr.lhs()); + const bvt object_size_bv = + bv_utils.zero_extension(convert_bv(object_size), width); + + bvt bv = prop.new_variables(width); + + if(!same_object_lit.is_false()) + { + const pointer_typet &lhs_pt = to_pointer_type(minus_expr.lhs().type()); + const bvt &lhs = convert_bv(minus_expr.lhs()); + const bvt lhs_offset = + bv_utils.sign_extension(offset_literals(lhs, lhs_pt), width); + + const literalt lhs_in_bounds = prop.land( + !bv_utils.sign_bit(lhs_offset), + bv_utils.rel( + lhs_offset, + ID_le, + object_size_bv, + bv_utilst::representationt::UNSIGNED)); + + const pointer_typet &rhs_pt = to_pointer_type(minus_expr.rhs().type()); + const bvt &rhs = convert_bv(minus_expr.rhs()); + const bvt rhs_offset = + bv_utils.sign_extension(offset_literals(rhs, rhs_pt), width); + + const literalt rhs_in_bounds = prop.land( + !bv_utils.sign_bit(rhs_offset), + bv_utils.rel( + rhs_offset, + ID_le, + object_size_bv, + bv_utilst::representationt::UNSIGNED)); + + bvt difference = bv_utils.sub(lhs_offset, rhs_offset); + + // Support for void* is a gcc extension, with the size treated as 1 byte + // (no division required below). + // https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html + if(lhs_pt.base_type().id() != ID_empty) + { + auto element_size_opt = pointer_offset_size(lhs_pt.base_type(), ns); + CHECK_RETURN(element_size_opt.has_value() && *element_size_opt > 0); + + if(*element_size_opt != 1) + { + bvt element_size_bv = + bv_utils.build_constant(*element_size_opt, width); + difference = bv_utils.divider( + difference, element_size_bv, bv_utilst::representationt::SIGNED); + } + } + + prop.l_set_to_true(prop.limplies( + prop.land(same_object_lit, prop.land(lhs_in_bounds, rhs_in_bounds)), + bv_utils.equal(difference, bv))); + } + + return bv; + } + else if( + expr.id() == ID_pointer_offset && + to_pointer_offset_expr(expr).pointer().type().id() == ID_pointer) + { + std::size_t width = boolbv_width(expr.type()); + + if(width == 0) + return conversion_failed(expr); + + const exprt &pointer = to_pointer_offset_expr(expr).pointer(); + const bvt &pointer_bv = convert_bv(pointer); + + bvt offset_bv = + offset_literals(pointer_bv, to_pointer_type(pointer.type())); + + // we do a sign extension to permit negative offsets + return bv_utils.sign_extension(offset_bv, width); + } + else if( + const auto object_size = expr_try_dynamic_cast(expr)) + { + // we postpone until we know the objects + std::size_t width = boolbv_width(object_size->type()); + + postponed_list.emplace_back( + prop.new_variables(width), + convert_bv(object_size->pointer()), + *object_size); + + return postponed_list.back().bv; + } + else if( + expr.id() == ID_pointer_object && + to_pointer_object_expr(expr).pointer().type().id() == ID_pointer) + { + std::size_t width = boolbv_width(expr.type()); + + if(width == 0) + return conversion_failed(expr); + + const exprt &pointer = to_pointer_object_expr(expr).pointer(); + const bvt &pointer_bv = convert_bv(pointer); + + bvt object_bv = + object_literals(pointer_bv, to_pointer_type(pointer.type())); + + return bv_utils.zero_extension(object_bv, width); + } + else if( + expr.id() == ID_typecast && + to_typecast_expr(expr).op().type().id() == ID_pointer) + { + // Pointer to int. + // We special-case NULL, which should always yield 0. + // Otherwise, we 'number' these, which are indicated by setting + // the top bit. + const auto &pointer_expr = to_typecast_expr(expr).op(); + const bvt pointer_bv = convert_pointer_type(pointer_expr); + const auto is_null = bv_utils.is_zero(pointer_bv); + const auto target_width = boolbv_width(expr.type()); + + if(target_width == 0) + return conversion_failed(expr); + + if(numbered_pointers.empty()) + numbered_pointers.emplace_back(bvt{}); + + const auto number = numbered_pointers.size(); + + numbered_pointers.push_back(pointer_bv); + + return bv_utils.select( + is_null, + bv_utils.zeros(target_width), + bv_utilst::concatenate( + bv_utils.build_constant(number, target_width - 1), + {const_literal(true)})); + } + + return SUB::convert_bitvector(expr); +} + +static std::string bits_to_string(const propt &prop, const bvt &bv) +{ + std::string result; + + for(const auto &literal : bv) + { + char ch = 0; + + // clang-format off + switch(prop.l_get(literal).get_value()) + { + case tvt::tv_enumt::TV_FALSE: ch = '0'; break; + case tvt::tv_enumt::TV_TRUE: ch = '1'; break; + case tvt::tv_enumt::TV_UNKNOWN: ch = '0'; break; + } + // clang-format on + + result = ch + result; + } + + return result; +} + +exprt bv_pointers_widet::bv_get_rec( + const exprt &expr, + const bvt &bv, + std::size_t offset) const +{ + const auto &type = expr.type(); + + if(type.id() != ID_pointer) + return SUB::bv_get_rec(expr, bv, offset); + + const pointer_typet &pt = to_pointer_type(type); + const std::size_t bits = boolbv_width(pt); + bvt value_bv(bv.begin() + offset, bv.begin() + offset + bits); + + std::string value = bits_to_string(prop, value_bv); + std::string value_addr = bits_to_string(prop, object_literals(value_bv, pt)); + std::string value_offset = + bits_to_string(prop, offset_literals(value_bv, pt)); + + // we treat these like bit-vector constants, but with + // some additional annotation + + const irep_idt bvrep = make_bvrep(bits, [&value](std::size_t i) { + return value[value.size() - 1 - i] == '1'; + }); + + constant_exprt result(bvrep, type); + + pointer_logict::pointert pointer; + pointer.object = + numeric_cast_v(binary2integer(value_addr, false)); + pointer.offset = binary2integer(value_offset, true); + + return annotated_pointer_constant_exprt{ + bvrep, pointer_logic.pointer_expr(pointer, pt)}; +} + +bvt bv_pointers_widet::encode(const mp_integer &addr, const pointer_typet &type) + const +{ + const std::size_t offset_bits = get_offset_width(type); + const std::size_t object_bits = get_object_width(type); + + bvt zero_offset(offset_bits, const_literal(false)); + bvt object = bv_utils.build_constant(addr, object_bits); + + return object_offset_encoding(object, zero_offset); +} + +bvt bv_pointers_widet::offset_arithmetic( + const pointer_typet &type, + const bvt &bv, + const mp_integer &x) +{ + const std::size_t offset_bits = get_offset_width(type); + + return offset_arithmetic( + type, bv, 1, bv_utils.build_constant(x, offset_bits)); +} + +bvt bv_pointers_widet::offset_arithmetic( + const pointer_typet &type, + const bvt &bv, + const mp_integer &factor, + const exprt &index) +{ + bvt bv_index = convert_bv(index); + + bv_utilst::representationt rep = index.type().id() == ID_signedbv + ? bv_utilst::representationt::SIGNED + : bv_utilst::representationt::UNSIGNED; + + const std::size_t offset_bits = get_offset_width(type); + bv_index = bv_utils.extension(bv_index, offset_bits, rep); + + return offset_arithmetic(type, bv, factor, bv_index); +} + +bvt bv_pointers_widet::offset_arithmetic( + const pointer_typet &type, + const bvt &bv, + const mp_integer &factor, + const bvt &index) +{ + bvt bv_index; + + if(factor == 1) + bv_index = index; + else + { + bvt bv_factor = bv_utils.build_constant(factor, index.size()); + bv_index = bv_utils.signed_multiplier(index, bv_factor); + } + + const std::size_t offset_bits = get_offset_width(type); + bv_index = bv_utils.sign_extension(bv_index, offset_bits); + + bvt offset_bv = offset_literals(bv, type); + + bvt bv_tmp = bv_utils.add(offset_bv, bv_index); + + return object_offset_encoding(object_literals(bv, type), bv_tmp); +} + +bvt bv_pointers_widet::add_addr(const exprt &expr) +{ + const auto a = pointer_logic.add_object(expr); + + const pointer_typet type = pointer_type(expr.type()); + const std::size_t object_bits = get_object_width(type); + const std::size_t max_objects = std::size_t(1) << object_bits; + + if(a == max_objects) + throw analysis_exceptiont( + "too many addressed objects: maximum number of objects is set to 2^n=" + + std::to_string(max_objects) + " (with n=" + std::to_string(object_bits) + + "); " + + "use the `--object-bits n` option to increase the maximum number"); + + return encode(a, type); +} + +void bv_pointers_widet::do_postponed(const postponedt &postponed) +{ + if(postponed.expr.id() == ID_is_dynamic_object) + { + const auto &type = + to_pointer_type(to_unary_expr(postponed.expr).op().type()); + const auto &objects = pointer_logic.objects; + std::size_t number = 0; + + for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number) + { + const exprt &expr = *it; + + bool is_dynamic = pointer_logic.is_dynamic_object(expr); + + // only compare object part + pointer_typet pt = pointer_type(expr.type()); + bvt bv = object_literals(encode(number, pt), type); + + bvt saved_bv = object_literals(postponed.op, type); + + POSTCONDITION(bv.size() == saved_bv.size()); + PRECONDITION(postponed.bv.size() == 1); + + literalt l1 = bv_utils.equal(bv, saved_bv); + literalt l2 = postponed.bv.front(); + + if(!is_dynamic) + l2 = !l2; + + prop.l_set_to_true(prop.limplies(l1, l2)); + } + } + else if( + const auto postponed_object_size = + expr_try_dynamic_cast(postponed.expr)) + { + const auto &type = to_pointer_type(postponed_object_size->pointer().type()); + const auto &objects = pointer_logic.objects; + std::size_t number = 0; + + for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number) + { + const exprt &expr = *it; + + if(expr.id() != ID_symbol && expr.id() != ID_string_constant) + continue; + + const auto size_expr = size_of_expr(expr.type(), ns); + + if(!size_expr.has_value()) + continue; + + const exprt object_size = typecast_exprt::conditional_cast( + size_expr.value(), postponed_object_size->type()); + + // only compare object part + pointer_typet pt = pointer_type(expr.type()); + bvt bv = object_literals(encode(number, pt), type); + + bvt saved_bv = object_literals(postponed.op, type); + + bvt size_bv = convert_bv(object_size); + + POSTCONDITION(bv.size() == saved_bv.size()); + PRECONDITION(postponed.bv.size() >= 1); + PRECONDITION(size_bv.size() == postponed.bv.size()); + + literalt l1 = bv_utils.equal(bv, saved_bv); + literalt l2 = bv_utils.equal(postponed.bv, size_bv); + + prop.l_set_to_true(prop.limplies(l1, l2)); + } + } + else + UNREACHABLE; +} + +void bv_pointers_widet::finish_eager_conversion() +{ + // post-processing arrays may yield further objects, do this first + SUB::finish_eager_conversion(); + + for(postponed_listt::const_iterator it = postponed_list.begin(); + it != postponed_list.end(); + it++) + do_postponed(*it); + + // Clear the list to avoid re-doing in case of incremental usage. + postponed_list.clear(); +} diff --git a/src/cprover/bv_pointers_wide.h b/src/cprover/bv_pointers_wide.h new file mode 100644 index 00000000000..790a24e3b57 --- /dev/null +++ b/src/cprover/bv_pointers_wide.h @@ -0,0 +1,129 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_BV_POINTERS_WIDE_H +#define CPROVER_CPROVER_BV_POINTERS_WIDE_H + +#include +#include + +#include +#include + +#include "endianness_map_wide.h" + +class bv_pointers_widet : public boolbvt +{ +public: + bv_pointers_widet( + const namespacet &, + propt &, + message_handlert &, + bool get_array_constraints = false); + + void finish_eager_conversion() override; + + std::size_t boolbv_width(const typet &type) const override + { + if(type.id() == ID_pointer) + return 2 * to_pointer_type(type).get_width(); + else + return boolbvt::boolbv_width(type); + } + + endianness_mapt + endianness_map(const typet &type, bool little_endian) const override + { + return endianness_map_widet{type, little_endian, ns}; + } + +protected: + pointer_logict pointer_logic; + + std::size_t get_object_width(const pointer_typet &) const; + std::size_t get_offset_width(const pointer_typet &) const; + + // NOLINTNEXTLINE(readability/identifiers) + typedef boolbvt SUB; + + NODISCARD + bvt encode(const mp_integer &object, const pointer_typet &) const; + + virtual bvt convert_pointer_type(const exprt &); + + NODISCARD + virtual bvt add_addr(const exprt &); + + // overloading + literalt convert_rest(const exprt &) override; + bvt convert_bitvector(const exprt &) override; // no cache + + exprt + bv_get_rec(const exprt &, const bvt &, std::size_t offset) const override; + + NODISCARD + optionalt convert_address_of_rec(const exprt &); + + NODISCARD + bvt offset_arithmetic(const pointer_typet &, const bvt &, const mp_integer &); + NODISCARD + bvt offset_arithmetic( + const pointer_typet &, + const bvt &, + const mp_integer &factor, + const exprt &index); + NODISCARD + bvt offset_arithmetic( + const pointer_typet &, + const bvt &, + const mp_integer &factor, + const bvt &index_bv); + + struct postponedt + { + bvt bv, op; + exprt expr; + + postponedt(bvt _bv, bvt _op, exprt _expr) + : bv(std::move(_bv)), op(std::move(_op)), expr(std::move(_expr)) + { + } + }; + + typedef std::list postponed_listt; + postponed_listt postponed_list; + + void do_postponed(const postponedt &postponed); + + /// Given a pointer encoded in \p bv, extract the literals identifying the + /// object that the pointer points to. + /// \param bv: Encoded pointer + /// \param type: Type of the encoded pointer + /// \return Vector of literals identifying the object part of \p bv + bvt object_literals(const bvt &bv, const pointer_typet &type) const; + + /// Given a pointer encoded in \p bv, extract the literals representing the + /// offset into an object that the pointer points to. + /// \param bv: Encoded pointer + /// \param type: Type of the encoded pointer + /// \return Vector of literals identifying the offset part of \p bv + bvt offset_literals(const bvt &bv, const pointer_typet &type) const; + + /// Construct a pointer encoding from given encodings of \p object and \p + /// offset. + /// \param object: Encoded object + /// \param offset: Encoded offset + /// \return Pointer encoding + static bvt object_offset_encoding(const bvt &object, const bvt &offset); + + /// Table that maps a 'pointer number' to its full-width bit-vector. + /// Used for conversion of pointers to integers. + std::vector numbered_pointers; +}; + +#endif // CPROVER_CPROVER_BV_POINTERS_WIDE_H diff --git a/src/cprover/c_safety_checks.cpp b/src/cprover/c_safety_checks.cpp new file mode 100644 index 00000000000..fbc6ee5084a --- /dev/null +++ b/src/cprover/c_safety_checks.cpp @@ -0,0 +1,449 @@ +/*******************************************************************\ + +Module: Checks for Errors in C/C++ Programs + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Checks for Errors in C/C++ Programs + +#include "c_safety_checks.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +exprt index_array_size(const typet &type) +{ + if(type.id() == ID_array) + return to_array_type(type).size(); + else if(type.id() == ID_vector) + return to_vector_type(type).size(); + else + PRECONDITION(false); +} + +enum class access_typet +{ + R, + W +}; + +void c_safety_checks_rec( + goto_functionst::function_mapt::value_type &, + const exprt::operandst &guards, + const exprt &, + access_typet, + const namespacet &, + goto_programt &); + +void c_safety_checks_address_rec( + goto_functionst::function_mapt::value_type &f, + const exprt::operandst &guards, + const exprt &expr, + const namespacet &ns, + goto_programt &dest) +{ + if(expr.id() == ID_index) + { + const auto &index_expr = to_index_expr(expr); + c_safety_checks_address_rec(f, guards, index_expr.array(), ns, dest); + c_safety_checks_rec( + f, guards, index_expr.index(), access_typet::R, ns, dest); + } + else if(expr.id() == ID_member) + { + c_safety_checks_address_rec( + f, guards, to_member_expr(expr).struct_op(), ns, dest); + } +} + +static exprt guard(const exprt::operandst &guards, exprt cond) +{ + if(guards.empty()) + return cond; + else + return implies_exprt(conjunction(guards), std::move(cond)); +} + +void c_safety_checks_rec( + goto_functionst::function_mapt::value_type &f, + const exprt::operandst &guards, + const exprt &expr, + access_typet access_type, + const namespacet &ns, + goto_programt &dest) +{ + if(expr.id() == ID_address_of) + { + c_safety_checks_address_rec( + f, guards, to_address_of_expr(expr).object(), ns, dest); + return; + } + else if(expr.id() == ID_and) + { + auto new_guards = guards; + for(auto &op : expr.operands()) + { + c_safety_checks_rec( + f, new_guards, op, access_type, ns, dest); // rec. call + new_guards.push_back(op); + } + return; + } + else if(expr.id() == ID_or) + { + auto new_guards = guards; + for(auto &op : expr.operands()) + { + c_safety_checks_rec( + f, new_guards, op, access_type, ns, dest); // rec. call + new_guards.push_back(not_exprt(op)); + } + return; + } + else if(expr.id() == ID_if) + { + const auto &if_expr = to_if_expr(expr); + c_safety_checks_rec( + f, guards, if_expr.cond(), access_typet::R, ns, dest); // rec. call + auto new_guards = guards; + new_guards.push_back(if_expr.cond()); + c_safety_checks_rec( + f, new_guards, if_expr.true_case(), access_type, ns, dest); // rec. call + new_guards.pop_back(); + new_guards.push_back(not_exprt(if_expr.cond())); + c_safety_checks_rec( + f, new_guards, if_expr.false_case(), access_type, ns, dest); // rec. call + return; + } + else if(expr.id() == ID_forall || expr.id() == ID_exists) + { + return; + } + + // now do operands + for(auto &op : expr.operands()) + c_safety_checks_rec(f, guards, op, access_type, ns, dest); // rec. call + + if(expr.id() == ID_dereference) + { + if(expr.type().id() == ID_code) + { + // dealt with elsewhere + } + else + { + auto size_opt = pointer_offset_size(expr.type(), ns); + if(size_opt.has_value()) + { + auto size = from_integer(*size_opt, size_type()); + auto pointer = to_dereference_expr(expr).pointer(); + auto condition = r_or_w_ok_exprt( + access_type == access_typet::W ? ID_w_ok : ID_r_ok, pointer, size); + condition.add_source_location() = expr.source_location(); + auto assertion_source_location = expr.source_location(); + assertion_source_location.set_property_class("pointer"); + auto pointer_text = expr2c(pointer, ns); + assertion_source_location.set_comment( + "pointer " + pointer_text + " safe"); + dest.add(goto_programt::make_assertion( + guard(guards, condition), assertion_source_location)); + } + } + } + else if(expr.id() == ID_div) + { + const auto &div_expr = to_div_expr(expr); + if( + div_expr.divisor().is_constant() && + !to_constant_expr(div_expr.divisor()).is_zero()) + { + } + else + { + auto zero = from_integer(0, div_expr.type()); + auto condition = implies_exprt( + conjunction(guards), notequal_exprt(div_expr.divisor(), zero)); + auto source_location = expr.source_location(); + condition.add_source_location() = expr.source_location(); + source_location.set_property_class("division-by-zero"); + source_location.set_comment("division by zero in " + expr2c(expr, ns)); + dest.add(goto_programt::make_assertion( + guard(guards, condition), source_location)); + } + } + else if(expr.id() == ID_mod) + { + const auto &mod_expr = to_mod_expr(expr); + if( + mod_expr.divisor().is_constant() && + !to_constant_expr(mod_expr.divisor()).is_zero()) + { + } + else + { + auto zero = from_integer(0, mod_expr.type()); + auto condition = notequal_exprt(mod_expr.divisor(), zero); + auto source_location = expr.source_location(); + condition.add_source_location() = expr.source_location(); + source_location.set_property_class("division-by-zero"); + source_location.set_comment("division by zero in " + expr2c(expr, ns)); + dest.add(goto_programt::make_assertion( + guard(guards, condition), source_location)); + } + } + else if(expr.id() == ID_index) + { + const auto &index_expr = to_index_expr(expr); + auto zero = from_integer(0, index_expr.index().type()); + auto size = typecast_exprt::conditional_cast( + index_array_size(index_expr.array().type()), index_expr.index().type()); + auto condition = and_exprt( + binary_relation_exprt(zero, ID_le, index_expr.index()), + binary_relation_exprt(size, ID_gt, index_expr.index())); + // 'index' may not have a source location, e.g., when implicitly + // taking the address of an array. + auto source_location = expr.find_source_location(); + condition.add_source_location() = expr.source_location(); + source_location.set_property_class("array bounds"); + source_location.set_comment("array bounds in " + expr2c(expr, ns)); + dest.add( + goto_programt::make_assertion(guard(guards, condition), source_location)); + } +} + +void c_safety_checks( + goto_functionst::function_mapt::value_type &f, + const exprt &expr, + access_typet access_type, + const namespacet &ns, + goto_programt &dest) +{ + c_safety_checks_rec(f, {}, expr, access_type, ns, dest); +} + +static exprt offset_is_zero(const exprt &pointer) +{ + auto offset = pointer_offset(pointer); + auto zero = from_integer(0, offset.type()); + return equal_exprt(std::move(offset), std::move(zero)); +} + +void c_safety_checks( + goto_functionst::function_mapt::value_type &f, + const namespacet &ns) +{ + auto &body = f.second.body; + goto_programt dest; + + for(auto it = body.instructions.begin(); it != body.instructions.end(); it++) + { + if(it->is_assign()) + { + c_safety_checks(f, it->assign_lhs(), access_typet::W, ns, dest); + c_safety_checks(f, it->assign_rhs(), access_typet::R, ns, dest); + } + else if(it->is_function_call()) + { + c_safety_checks(f, it->call_lhs(), access_typet::W, ns, dest); + c_safety_checks(f, it->call_function(), access_typet::R, ns, dest); + for(const auto &argument : it->call_arguments()) + c_safety_checks(f, argument, access_typet::R, ns, dest); + } + else + { + it->apply([&f, &ns, &dest](const exprt &expr) { + c_safety_checks(f, expr, access_typet::R, ns, dest); + }); + } + + if(it->is_function_call()) + { + const auto &function = it->call_function(); + if(function.id() == ID_symbol) + { + const auto &identifier = to_symbol_expr(function).get_identifier(); + if(identifier == "free") + { + if( + it->call_arguments().size() == 1 && + it->call_arguments()[0].type().id() == ID_pointer) + { + // Must be 1) dynamically allocated, 2) still alive, + // 3) have offset zero, or, alternatively, NULL. + const auto &pointer = it->call_arguments()[0]; + auto condition = or_exprt( + equal_exprt( + pointer, null_pointer_exprt(to_pointer_type(pointer.type()))), + and_exprt( + live_object_exprt(pointer), + is_dynamic_object_exprt(pointer), + offset_is_zero(pointer))); + auto source_location = it->source_location(); + source_location.set_property_class("free"); + source_location.set_comment( + "free argument must be valid dynamic object"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "realloc") + { + if( + it->call_arguments().size() == 2 && + it->call_arguments()[0].type().id() == ID_pointer) + { + // Must be 1) dynamically allocated, 2) still alive, + // 3) have offset zero, or, alternatively, NULL. + const auto &pointer = it->call_arguments()[0]; + auto condition = or_exprt( + equal_exprt( + pointer, null_pointer_exprt(to_pointer_type(pointer.type()))), + and_exprt( + live_object_exprt(pointer), + is_dynamic_object_exprt(pointer), + offset_is_zero(pointer))); + auto source_location = it->source_location(); + source_location.set_property_class("realloc"); + source_location.set_comment( + "realloc argument must be valid dynamic object"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "memcmp") + { + if( + it->call_arguments().size() == 3 && + it->call_arguments()[0].type().id() == ID_pointer && + it->call_arguments()[1].type().id() == ID_pointer && + it->call_arguments()[2].type().id() == ID_unsignedbv) + { + // int memcmp(const void *s1, const void *s2, size_t n); + const auto &p1 = it->call_arguments()[0]; + const auto &p2 = it->call_arguments()[1]; + const auto &size = it->call_arguments()[2]; + auto condition = + and_exprt(r_ok_exprt(p1, size), r_ok_exprt(p2, size)); + auto source_location = it->source_location(); + source_location.set_property_class("memcmp"); + source_location.set_comment("memcmp regions must be valid"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "memchr") + { + if( + it->call_arguments().size() == 3 && + it->call_arguments()[0].type().id() == ID_pointer && + it->call_arguments()[2].type().id() == ID_unsignedbv) + { + // void *memchr(const void *, int, size_t); + const auto &p = it->call_arguments()[0]; + const auto &size = it->call_arguments()[2]; + auto condition = r_ok_exprt(p, size); + auto source_location = it->source_location(); + source_location.set_property_class("memchr"); + source_location.set_comment("memchr source must be valid"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "memset") + { + if( + it->call_arguments().size() == 3 && + it->call_arguments()[0].type().id() == ID_pointer && + it->call_arguments()[2].type().id() == ID_unsignedbv) + { + // void *memset(void *b, int c, size_t len); + const auto &pointer = it->call_arguments()[0]; + const auto &size = it->call_arguments()[2]; + auto condition = w_ok_exprt(pointer, size); + auto source_location = it->source_location(); + source_location.set_property_class("memset"); + source_location.set_comment("memset destination must be valid"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "strlen") + { + if( + it->call_arguments().size() == 1 && + it->call_arguments()[0].type().id() == ID_pointer) + { + const auto &address = it->call_arguments()[0]; + auto condition = is_cstring_exprt(address); + auto source_location = it->source_location(); + source_location.set_property_class("strlen"); + source_location.set_comment("strlen argument must be C string"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + else if(identifier == "__builtin___memset_chk") // clang variant + { + if( + it->call_arguments().size() == 4 && + it->call_arguments()[0].type().id() == ID_pointer && + it->call_arguments()[2].type().id() == ID_unsignedbv) + { + const auto &pointer = it->call_arguments()[0]; + const auto &size = it->call_arguments()[2]; + auto condition = w_ok_exprt(pointer, size); + auto source_location = it->source_location(); + source_location.set_property_class("memset"); + source_location.set_comment("memset destination must be valid"); + dest.add(goto_programt::make_assertion(condition, source_location)); + } + } + } + } + + std::size_t n = dest.instructions.size(); + if(n != 0) + { + body.insert_before_swap(it, dest); + dest.clear(); + it = std::next(it, n); + } + } +} + +void c_safety_checks(goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + for(auto &f : goto_model.goto_functions.function_map) + c_safety_checks(f, ns); + + // update the numbering + goto_model.goto_functions.compute_location_numbers(); +} + +void no_assertions(goto_functionst::function_mapt::value_type &f) +{ + auto &body = f.second.body; + + for(auto it = body.instructions.begin(); it != body.instructions.end(); it++) + { + if( + it->is_assert() && + it->source_location().get_property_class() == ID_assertion) + { + it->turn_into_skip(); + } + } +} + +void no_assertions(goto_modelt &goto_model) +{ + for(auto &f : goto_model.goto_functions.function_map) + no_assertions(f); +} diff --git a/src/cprover/c_safety_checks.h b/src/cprover/c_safety_checks.h new file mode 100644 index 00000000000..b341744aa7e --- /dev/null +++ b/src/cprover/c_safety_checks.h @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Checks for Errors in C/C++ Programs + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Checks for Errors in C/C++ Programs + +#ifndef CPROVER_CPROVER_C_SAFETY_CHECKS_H +#define CPROVER_CPROVER_C_SAFETY_CHECKS_H + +class goto_modelt; + +void c_safety_checks(goto_modelt &); +void no_assertions(goto_modelt &); + +#endif // CPROVER_CPROVER_C_SAFETY_CHECKS_H diff --git a/src/cprover/console.cpp b/src/cprover/console.cpp new file mode 100644 index 00000000000..11b99bbbefc --- /dev/null +++ b/src/cprover/console.cpp @@ -0,0 +1,236 @@ +/*******************************************************************\ + +Module: Console + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "console.h" + +#include +#include +#include + +#ifdef _WIN32 +# include +# include +# define isatty _isatty +#else +# include +#endif + +#include +#include +#include + +#ifdef _WIN32 +class windows_coutt : public std::streambuf +{ +public: + // this turns UTF8 into Windows UTF16 + std::streamsize xsputn(const char_type *s, std::streamsize n) override + { + if(consolet::is_terminal()) + { + auto wide_string = widen(std::string(s, n)); + DWORD number_written; + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + WriteConsoleW( + h, + wide_string.c_str(), + (DWORD)wide_string.size(), + &number_written, + nullptr); + } + else + { + std::cout.write(s, n); + } + + return n; + } + + int_type overflow(int_type c) override + { + if(consolet::is_terminal()) + std::wcout << wchar_t(c); + else + std::cout << char(c); + return wchar_t(c); + } +} windows_cout; +#endif + +bool consolet::_is_terminal = false; +bool consolet::_use_SGR = false; +bool consolet::_init_done = false; +std::ostream *consolet::_out = nullptr; +std::ostream *consolet::_err = nullptr; + +void consolet::init() +{ + if(_init_done) + return; + + _init_done = true; + _is_terminal = isatty(1); + + _out = &std::cout; + _err = &std::cerr; + +#ifdef _WIN32 + if(_is_terminal) + { + HANDLE out_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + DWORD consoleMode; + if(GetConsoleMode(out_handle, &consoleMode)) + { + consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if(SetConsoleMode(out_handle, consoleMode)) + _use_SGR = true; + } + + std::cout.rdbuf(&windows_cout); + } +#else + _use_SGR = true; +#endif +} + +std::ostream &consolet::blue(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[34m"; + else + return str; +} + +std::ostream &consolet::cyan(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[36m"; + else + return str; +} + +std::ostream &consolet::green(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[32m"; + else + return str; +} + +std::ostream &consolet::red(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[31m"; + else + return str; +} + +std::ostream &consolet::yellow(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[33m"; + else + return str; +} + +std::ostream &consolet::orange(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[38;5;214m"; + else + return str; +} + +std::ostream &consolet::bold(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[1m"; + else + return str; +} + +std::ostream &consolet::faint(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[2m"; + else + return str; +} + +std::ostream &consolet::underline(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[4m"; + else + return str; +} + +std::ostream &consolet::reset(std::ostream &str) +{ + if(is_terminal() && use_SGR()) + return str << "\x1b[m"; + else + return str; +} + +std::size_t consolet::width() +{ + std::size_t width = 80; // default + + if(_is_terminal) + { +#ifdef _WIN32 + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(h, &info); + width = info.srWindow.Right - info.srWindow.Left + 1; +#else + std::ostringstream width_stream; + run("stty", {"stty", "size"}, "", width_stream, ""); + auto stty_output = split_string(width_stream.str(), ' '); + if( + stty_output.size() >= 1 && !stty_output[1].empty() && + isdigit(stty_output[1][0])) + { + auto width_l = atol(stty_output[1].c_str()); + if(width_l >= 10 && width_l <= 400) + width = width_l; + } +#endif + } + + return width; +} + +extern "C" int mk_wcwidth(wchar_t ucs); + +int consolet::wcwidth(wchar_t ucs) +{ + return mk_wcwidth(ucs); +} + +consolet::redirectt::redirectt( + std::ostream &__console_out, + std::ostream &__console_err) +{ + consolet::init(); + old_out = consolet::_out; + old_err = consolet::_err; + old_is_terminal = consolet::_is_terminal; + consolet::_out = &__console_out; + consolet::_err = &__console_err; + consolet::_is_terminal = false; +} + +consolet::redirectt::~redirectt() +{ + consolet::_out = old_out; + consolet::_err = old_err; + consolet::_is_terminal = old_is_terminal; +} diff --git a/src/cprover/console.h b/src/cprover/console.h new file mode 100644 index 00000000000..b4f11678a9c --- /dev/null +++ b/src/cprover/console.h @@ -0,0 +1,89 @@ +/*******************************************************************\ + +Module: Console + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Console + +#ifndef CPROVER_CPROVER_CONSOLE_H +#define CPROVER_CPROVER_CONSOLE_H + +#include +#include + +class consolet +{ +public: + static void init(); + + static std::ostream &blue(std::ostream &); + static std::ostream &cyan(std::ostream &); + static std::ostream &green(std::ostream &); + static std::ostream &red(std::ostream &); + static std::ostream &yellow(std::ostream &); + static std::ostream &orange(std::ostream &); + + static std::ostream &bold(std::ostream &); + static std::ostream &faint(std::ostream &); + static std::ostream &underline(std::ostream &); + + static std::ostream &reset(std::ostream &); + + static bool is_terminal() + { + init(); + return _is_terminal; + } + + static bool use_SGR() + { + init(); + return _use_SGR; + } + + static std::ostream &out() + { + init(); + return *_out; + } + + static std::ostream &err() + { + init(); + return *_err; + } + + static std::size_t width(); + + // -1: not printable + // 0: no width + // 1: usual single width + // 2: double width + static int wcwidth(wchar_t); + + // redirection + class redirectt + { + public: + // __out has some meaning on Windows, therefore using __console_out + redirectt(std::ostream &__console_out, std::ostream &__console_err); + ~redirectt(); + + protected: + std::ostream *old_out = nullptr, *old_err = nullptr; + bool old_is_terminal = false; + }; + +protected: + static bool _is_terminal; + static bool _use_SGR; + static bool _init_done; + static std::ostream *_out; + static std::ostream *_err; +}; + +#endif // CPROVER_CPROVER_CONSOLE_H diff --git a/src/cprover/counterexample_found.cpp b/src/cprover/counterexample_found.cpp new file mode 100644 index 00000000000..8d383f45568 --- /dev/null +++ b/src/cprover/counterexample_found.cpp @@ -0,0 +1,212 @@ +/*******************************************************************\ + +Module: Counterexample Found + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Counterexample Found + +#include "counterexample_found.h" + +#include +#include +#include + +#include + +#include "axioms.h" +#include "bv_pointers_wide.h" +#include "simplify_state_expr.h" +#include "state.h" + +#include + +void show_assignment(const bv_pointers_widet &solver) +{ +#if 0 + for(auto &entry : solver.get_cache()) + { + const auto &expr = entry.first; + if(expr.id() == ID_and || expr.id() == ID_or || expr.id() == ID_not) + continue; + auto value = solver.l_get(entry.second); + std::cout << "|| " << format(expr) << " --> " << value << "\n"; + } +#endif + +#if 1 + for(auto &entry : solver.get_map().get_mapping()) + { + const auto &identifier = entry.first; + auto symbol = symbol_exprt(identifier, entry.second.type); + auto value = solver.get(symbol); + std::cout << "|| " << format(symbol) << " --> " << format(value) << "\n"; + } +#endif + + for(auto &entry : solver.get_symbols()) + { + const auto &identifier = entry.first; + auto value = solver.l_get(entry.second); + std::cout << "|| " << identifier << " --> " << value << "\n"; + } +} + +static exprt evaluator_rec( + const std::unordered_map &memory, + const decision_proceduret &solver, + exprt src, + const namespacet &ns) +{ + if(src.id() == ID_evaluate) + { + const auto &evaluate_expr = to_evaluate_expr(src); + + // recursively get the address + auto address_evaluated = + evaluator_rec(memory, solver, evaluate_expr.address(), ns); + + auto address_simplified = + simplify_expr(simplify_state_expr(address_evaluated, {}, ns), ns); + + auto m_it = memory.find(address_simplified); + if(m_it == memory.end()) + return src; + else + return m_it->second; + } + else if(src.id() == ID_symbol) + { + // nondet -- ask the solver + return solver.get(src); + } + else + { + for(auto &op : src.operands()) + op = evaluator_rec(memory, solver, op, ns); + + return src; + } +} + +static exprt evaluator( + const std::unordered_map &memory, + const decision_proceduret &solver, + exprt src, + const namespacet &ns) +{ + auto tmp = evaluator_rec(memory, solver, src, ns); + return simplify_expr(simplify_state_expr(tmp, {}, ns), ns); +} + +propertyt::tracet counterexample( + const std::vector &frames, + const workt &work, + const bv_pointers_widet &solver, + const axiomst &axioms, + const namespacet &ns) +{ + propertyt::tracet trace; + + // map from memory addresses to memory values + std::unordered_map memory; + + // heap object counter + std::size_t heap_object_counter = 0; + + // work.path goes backwards, we want a forwards trace + for(auto r_it = work.path.rbegin(); r_it != work.path.rend(); ++r_it) + { + const auto &frame = frames[r_it->index]; + propertyt::trace_statet state; + state.frame = *r_it; + + for(auto &implication : frame.implications) + { + if(implication.rhs.arguments().size() != 1) + continue; + auto &argument = implication.rhs.arguments().front(); + if(argument.id() == ID_update_state) + { + const auto &update_state = to_update_state_expr(argument); + auto address = evaluator(memory, solver, update_state.address(), ns); + auto value = evaluator(memory, solver, update_state.new_value(), ns); + + if(value.id() == ID_allocate) + { + // replace by a numbered 'heap object' + heap_object_counter++; + auto object_type = to_pointer_type(value.type()).base_type(); + auto identifier = "heap-" + std::to_string(heap_object_counter); + value = object_address_exprt(symbol_exprt(identifier, object_type)); + } + + state.updates.emplace_back(address, value); + memory[address] = value; + } + else if(argument.id() == ID_enter_scope_state) + { + // do we have a value? + const auto &enter_scope_state = to_enter_scope_state_expr(argument); + auto address = enter_scope_state.address(); + auto evaluate_expr = evaluate_exprt(enter_scope_state.state(), address); + auto translated = axioms.translate(evaluate_expr); + auto value = solver.get(translated); + if(value.is_not_nil() && value != evaluate_expr) + { + state.updates.emplace_back(address, value); + memory[address] = value; + } + } + } + + trace.push_back(std::move(state)); + } + + return trace; +} + +optionalt counterexample_found( + const std::vector &frames, + const workt &work, + const std::unordered_set &address_taken, + bool verbose, + const namespacet &ns) +{ + auto &f = frames[work.frame.index]; + + for(const auto &implication : f.implications) + { + if(implication.lhs.id() == ID_initial_state) + { + cout_message_handlert message_handler; + message_handler.set_verbosity(verbose ? 10 : 1); + satcheckt satcheck(message_handler); + bv_pointers_widet solver(ns, satcheck, message_handler); + axiomst axioms(solver, address_taken, verbose, ns); + + // These are initial states, i.e., initial_state(ς) ⇒ SInitial(ς). + // Ask the solver whether the invariant is 'true'. + axioms.set_to_false(work.invariant); + axioms.set_to_true(implication.lhs); + axioms.emit(); + + switch(solver()) + { + case decision_proceduret::resultt::D_SATISFIABLE: + if(verbose) + show_assignment(solver); + return counterexample(frames, work, solver, axioms, ns); + case decision_proceduret::resultt::D_UNSATISFIABLE: + break; + case decision_proceduret::resultt::D_ERROR: + throw "error reported by solver"; + } + } + } + + return {}; +} diff --git a/src/cprover/counterexample_found.h b/src/cprover/counterexample_found.h new file mode 100644 index 00000000000..bd848f0ad2a --- /dev/null +++ b/src/cprover/counterexample_found.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Counterexample Found + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Counterexample Found + +#ifndef CPROVER_CPROVER_COUNTEREXAMPLE_FOUND_H +#define CPROVER_CPROVER_COUNTEREXAMPLE_FOUND_H + +#include "solver_types.h" + +#include + +optionalt counterexample_found( + const std::vector &, + const workt &, + const std::unordered_set &address_taken, + bool verbose, + const namespacet &); + +class bv_pointers_widet; + +void show_assignment(const bv_pointers_widet &); + +#endif // CPROVER_CPROVER_COUNTEREXAMPLE_FOUND_H diff --git a/src/cprover/cprover_main.cpp b/src/cprover/cprover_main.cpp new file mode 100644 index 00000000000..3265987a7b2 --- /dev/null +++ b/src/cprover/cprover_main.cpp @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: CPROVER Main Module + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// cprover Main Module + +#include + +#include "cprover_parse_options.h" + +#ifdef _MSC_VER +# include +#endif + +#ifdef _MSC_VER +int wmain(int argc, const wchar_t **argv_wide) +{ + auto vec = narrow_argv(argc, argv_wide); + auto narrow = to_c_str_array(std::begin(vec), std::end(vec)); + auto argv = narrow.data(); +#else +int main(int argc, const char **argv) +{ +#endif + cprover_parse_optionst parse_options(argc, argv); + return parse_options.main(); +} diff --git a/src/cprover/cprover_parse_options.cpp b/src/cprover/cprover_parse_options.cpp new file mode 100644 index 00000000000..b4ca605f357 --- /dev/null +++ b/src/cprover/cprover_parse_options.cpp @@ -0,0 +1,336 @@ +/*******************************************************************\ + +Module: CPROVER Command Line Option Processing + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// cprover Command Line Options Processing + +#include "cprover_parse_options.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "c_safety_checks.h" +#include "format_hooks.h" +#include "help_formatter.h" +#include "instrument_contracts.h" +#include "instrument_given_invariants.h" +#include "state_encoding.h" + +#include +#include + +static void show_goto_functions(const goto_modelt &goto_model) +{ + // sort alphabetically + const auto sorted = goto_model.goto_functions.sorted(); + + const namespacet ns(goto_model.symbol_table); + for(const auto &fun : sorted) + { + const symbolt &symbol = ns.lookup(fun->first); + const bool has_body = fun->second.body_available(); + + if(has_body) + { + std::cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n"; + + std::cout << symbol.display_name() << " /* " << symbol.name << " */\n"; + fun->second.body.output(ns, symbol.name, std::cout); + } + } +} + +static void show_functions_with_loops(const goto_modelt &goto_model) +{ + // sort alphabetically + const auto sorted = goto_model.goto_functions.sorted(); + + const namespacet ns(goto_model.symbol_table); + for(const auto &fun : sorted) + { + const symbolt &symbol = ns.lookup(fun->first); + + if(symbol.is_file_local) + continue; + + bool has_loop = false; + for(auto &instruction : fun->second.body.instructions) + if(instruction.is_backwards_goto()) + has_loop = true; + + if(has_loop) + std::cout << symbol.display_name() << '\n'; + } +} + +int cprover_parse_optionst::main() +{ + try + { + install_signal_catcher(); + + cmdlinet cmdline; + + if(cmdline.parse(argc, argv, CPROVER_OPTIONS)) + { + std::cerr << "Usage error!" << '\n'; + return CPROVER_EXIT_USAGE_ERROR; + } + + if(cmdline.isset("version")) + { + std::cout << CBMC_VERSION << '\n'; + return CPROVER_EXIT_SUCCESS; + } + + if(cmdline.isset('?') || cmdline.isset("help") || cmdline.isset('h')) + { + help(); + return CPROVER_EXIT_SUCCESS; + } + + register_language(new_ansi_c_language); + format_hooks(); + + if(cmdline.args.empty()) + { + std::cerr << "Please provide a program to verify\n"; + return CPROVER_EXIT_INCORRECT_TASK; + } + + config.set(cmdline); + + // configure gcc, if required + if(config.ansi_c.preprocessor == configt::ansi_ct::preprocessort::GCC) + { + gcc_versiont gcc_version; + gcc_version.get("gcc"); + configure_gcc(gcc_version); + } + + console_message_handlert message_handler; + null_message_handlert null_message_handler; + + optionst options; + auto goto_model = + initialize_goto_model(cmdline.args, message_handler, options); + + auto &remove_function_pointers_message_handler = + cmdline.isset("verbose") + ? static_cast(message_handler) + : static_cast(null_message_handler); + + const bool safety = !cmdline.isset("no-safety"); + + remove_function_pointers( + remove_function_pointers_message_handler, goto_model, safety); + + adjust_float_expressions(goto_model); + instrument_given_invariants(goto_model); + + bool perform_inlining; + + if(cmdline.isset("smt2")) + { + perform_inlining = !cmdline.isset("no-inline"); + } + else + { + perform_inlining = cmdline.isset("inline"); + } + + if(!perform_inlining) + instrument_contracts(goto_model); + + if(safety) + c_safety_checks(goto_model); + + if(cmdline.isset("no-assertions")) + no_assertions(goto_model); + + label_properties(goto_model); + + // bool perform_inlining = false; + bool variable_encoding = cmdline.isset("variables"); + + if(perform_inlining || variable_encoding) + { + std::cout << "Performing inlining\n"; + goto_inline(goto_model, message_handler, false); + } + + goto_model.goto_functions.compute_target_numbers(); + goto_model.goto_functions.compute_location_numbers(); + + if(cmdline.isset("show-goto-functions")) + { + show_goto_functions(goto_model); + return CPROVER_EXIT_SUCCESS; + } + + // show loop ids? + if(cmdline.isset("show-loops")) + { + show_loop_ids(ui_message_handlert::uit::PLAIN, goto_model); + return CPROVER_EXIT_SUCCESS; + } + + if(cmdline.isset("show-functions-with-loops")) + { + show_functions_with_loops(goto_model); + return CPROVER_EXIT_SUCCESS; + } + + if(cmdline.isset("validate-goto-model")) + { + goto_model.validate(); + } + + if(cmdline.isset("show-properties")) + { + ui_message_handlert ui_message_handler(cmdline, "cprover"); + show_properties(goto_model, ui_message_handler); + return CPROVER_EXIT_SUCCESS; + } + +// gcc produces a spurious warning on optionalt. +// This will likely go away once we use std::optional. +// To make clang ignore the pragma, we need to guard it with an ifdef. +#pragma GCC diagnostic push +#ifndef __clang__ +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + optionalt contract = cmdline.isset("contract") + ? irep_idt(cmdline.get_value("contract")) + : optionalt{}; +#pragma GCC diagnostic pop + + if(cmdline.isset("smt2") || cmdline.isset("text") || variable_encoding) + { + auto format = cmdline.isset("smt2") ? state_encoding_formatt::SMT2 + : state_encoding_formatt::ASCII; + + if(cmdline.isset("outfile")) + { + auto file_name = cmdline.get_value("outfile"); +#ifdef _WIN32 + std::ofstream out(widen(file_name)); +#else + std::ofstream out(file_name); +#endif + if(!out) + { + std::cerr << "failed to open " << file_name << '\n'; + return CPROVER_EXIT_PARSE_ERROR; + } + + if(variable_encoding) + ::variable_encoding(goto_model, format, out); + else + state_encoding(goto_model, format, perform_inlining, contract, out); + + std::cout << "formula written to " << file_name << '\n'; + } + else + { + if(variable_encoding) + ::variable_encoding(goto_model, format, std::cout); + else + state_encoding( + goto_model, format, perform_inlining, contract, std::cout); + } + + if(!cmdline.isset("solve")) + return CPROVER_EXIT_SUCCESS; + } + + solver_optionst solver_options; + + if(cmdline.isset("unwind")) + { + solver_options.loop_limit = std::stoull(cmdline.get_value("unwind")); + } + else + solver_options.loop_limit = 1; + + solver_options.trace = cmdline.isset("trace"); + solver_options.verbose = cmdline.isset("verbose"); + + // solve + auto result = state_encoding_solver( + goto_model, perform_inlining, contract, solver_options); + + switch(result) + { + case solver_resultt::ALL_PASS: + return CPROVER_EXIT_SUCCESS; + + case solver_resultt::SOME_FAIL: + return CPROVER_EXIT_VERIFICATION_UNSAFE; + + case solver_resultt::ERROR: + return CPROVER_EXIT_INTERNAL_ERROR; + } + } + catch(const cprover_exception_baset &e) + { + std::cerr << "error: " << e.what() << '\n'; + return CPROVER_EXIT_EXCEPTION; + } + + UNREACHABLE; // to silence a gcc warning +} + +/// display command line help +void cprover_parse_optionst::help() +{ + std::cout << '\n'; + + std::cout + << u8"* * CPROVER " << CBMC_VERSION << " (" << (sizeof(void *) * 8) + << "-bit)" + << " * *\n" + << "* * Copyright 2022 * *\n"; + + // clang-format off + std::cout << help_formatter( + "\n" + "Usage: \tPurpose:\n" + "\n" + " {bcprover} [{y-?}] [{y-h}] [{y--help}] \t show this help\n" + " {bcprover} {usource-file.c} \t convert a given C program\n" + "\n" + "Other options:\n" + " {y--inline} \t perform function call inlining before\n" + " \t generating the formula\n" + " {y--no-safety} \t disable safety checks\n" + " {y--contract} {ufunction} \t check contract of given function\n" + " {y--outfile} {ufile-name} \t set output file for formula\n" + " {y--smt2} \t output formula in SMT-LIB2 format\n" + " {y--text} \t output formula in text format\n" + "\n"); +} diff --git a/src/cprover/cprover_parse_options.h b/src/cprover/cprover_parse_options.h new file mode 100644 index 00000000000..0627241a952 --- /dev/null +++ b/src/cprover/cprover_parse_options.h @@ -0,0 +1,49 @@ +/*******************************************************************\ + +Module: CPROVER Command Line Option Processing + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Command Line Parsing + +#ifndef CPROVER_CPROVER_CPROVER_PARSE_OPTIONS_H +#define CPROVER_CPROVER_CPROVER_PARSE_OPTIONS_H + +#include + +#define CPROVER_OPTIONS \ + "(help)?h(version)" \ + "(smt2)(text)(outfile):" \ + "(variables)" \ + "(safety)(no-safety)(no-assertions)" \ + "(contract):" \ + "(solve)(unwind):(trace)" \ + "(inline)(no-inline)" \ + "D:I:" \ + "(32)(64)" \ + "(show-goto-functions)(show-loops)(show-properties)" \ + "(show-functions-with-loops)" \ + "(validate-goto-model)" \ + "(verbose)" + +class cprover_parse_optionst +{ +public: + int main(); + + cprover_parse_optionst(int _argc, const char **_argv) + : argc(_argc), argv(_argv) + { + } + +protected: + int argc; + const char **argv; + + void help(); +}; + +#endif // CPROVER_CPROVER_CPROVER_PARSE_OPTIONS_H diff --git a/src/cprover/endianness_map_wide.cpp b/src/cprover/endianness_map_wide.cpp new file mode 100644 index 00000000000..f3d2d03229d --- /dev/null +++ b/src/cprover/endianness_map_wide.cpp @@ -0,0 +1,31 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "endianness_map_wide.h" + +#include +#include +#include + +void endianness_map_widet::build_little_endian(const typet &src) +{ + if(src.id() == ID_pointer) + { + auto s = pointer_offset_bits(src, ns); + CHECK_RETURN(s.has_value()); + s.value() = s.value() * 2; + + const std::size_t new_size = map.size() + numeric_cast_v(*s); + map.reserve(new_size); + + for(std::size_t i = map.size(); i < new_size; ++i) + map.push_back(i); + } + else + endianness_mapt::build_little_endian(src); +} diff --git a/src/cprover/endianness_map_wide.h b/src/cprover/endianness_map_wide.h new file mode 100644 index 00000000000..ee0a9cc7ae2 --- /dev/null +++ b/src/cprover/endianness_map_wide.h @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_ENDIANNESS_MAP_WIDE_H +#define CPROVER_CPROVER_ENDIANNESS_MAP_WIDE_H + +#include + +class endianness_map_widet : public endianness_mapt +{ +public: + endianness_map_widet( + const typet &type, + bool little_endian, + const namespacet &_ns) + : endianness_mapt(type, little_endian, _ns) + { + } + +protected: + void build_little_endian(const typet &) override; +}; + +#endif // CPROVER_CPROVER_ENDIANNESS_MAP_WIDE_H diff --git a/src/cprover/equality_propagation.cpp b/src/cprover/equality_propagation.cpp new file mode 100644 index 00000000000..41874eb08ad --- /dev/null +++ b/src/cprover/equality_propagation.cpp @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: Equality Propagation + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Equality Propagation + +#include "equality_propagation.h" + +#include +#include + +void equality_propagation(std::vector &constraints) +{ + using valuest = std::map; + valuest values; + + std::vector new_constraints; + new_constraints.reserve(constraints.size()); + + // forward-propagation of equalities + for(auto &expr : constraints) + { + bool retain_constraint = true; + + // apply the map + auto substitution_result = substitute_symbols(values, expr); + if(substitution_result.has_value()) + expr = std::move(substitution_result.value()); + + if(expr.id() == ID_equal) + { + const auto &equal_expr = to_equal_expr(expr); + if(equal_expr.lhs().id() == ID_symbol) + { + const auto &symbol_expr = to_symbol_expr(equal_expr.lhs()); + // this is a (deliberate) no-op when the symbol is already in the map + auto insert_result = + values.insert({symbol_expr.get_identifier(), equal_expr.rhs()}); + if(insert_result.second) + { + // insertion has happened + retain_constraint = false; + } + } + } + + if(retain_constraint) + new_constraints.push_back(std::move(expr)); + } + + constraints = std::move(new_constraints); + + // apply the map again, to catch any backwards definitions + for(auto &expr : constraints) + { + if(expr.id() == ID_equal && to_equal_expr(expr).lhs().id() == ID_symbol) + { + // it's a definition + } + else + { + // apply the map + auto substitution_result = substitute_symbols(values, expr); + if(substitution_result.has_value()) + expr = std::move(substitution_result.value()); + } + } +} diff --git a/src/cprover/equality_propagation.h b/src/cprover/equality_propagation.h new file mode 100644 index 00000000000..641cb097299 --- /dev/null +++ b/src/cprover/equality_propagation.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Equality Propagation + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Equality Propagation + +#ifndef CPROVER_CPROVER_EQUALITY_PROPAGATION_H +#define CPROVER_CPROVER_EQUALITY_PROPAGATION_H + +#include + +void equality_propagation(std::vector &); + +#endif // CPROVER_CPROVER_EQUALITY_PROPAGATION_H diff --git a/src/cprover/find_variables.cpp b/src/cprover/find_variables.cpp new file mode 100644 index 00000000000..aa983983523 --- /dev/null +++ b/src/cprover/find_variables.cpp @@ -0,0 +1,40 @@ +/*******************************************************************\ + +Module: Find Variables + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Find Variables + +#include "find_variables.h" + +#include + +#include "state.h" + +static void find_variables_rec( + const exprt &src, + std::unordered_set &result) +{ + if(src.id() == ID_object_address) + result.insert(to_object_address_expr(src).object_expr()); + else + { + for(auto &op : src.operands()) + find_variables_rec(op, result); + } +} + +std::unordered_set +find_variables(const std::vector &src) +{ + std::unordered_set result; + + for(auto &expr : src) + find_variables_rec(expr, result); + + return result; +} diff --git a/src/cprover/find_variables.h b/src/cprover/find_variables.h new file mode 100644 index 00000000000..b81c3ba5bc6 --- /dev/null +++ b/src/cprover/find_variables.h @@ -0,0 +1,24 @@ +/*******************************************************************\ + +Module: Find Variables + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Find Variables + +#ifndef CPROVER_CPROVER_FIND_VARIABLES_H +#define CPROVER_CPROVER_FIND_VARIABLES_H + +#include + +#include + +/// Returns the set of program variables (as identified by object_address +/// expressions) in the given expression. +std::unordered_set +find_variables(const std::vector &); + +#endif // CPROVER_CPROVER_FIND_VARIABLES_H diff --git a/src/cprover/flatten_ok_expr.cpp b/src/cprover/flatten_ok_expr.cpp new file mode 100644 index 00000000000..9ae4673549a --- /dev/null +++ b/src/cprover/flatten_ok_expr.cpp @@ -0,0 +1,54 @@ +/*******************************************************************\ + +Module: Flatten OK Expressions + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "flatten_ok_expr.h" + +#include + +exprt flatten(const state_ok_exprt &ok_expr) +{ + const auto &state = ok_expr.state(); + const auto &pointer = ok_expr.address(); + const auto &size = ok_expr.size(); + + // X_ok(p, s) <--> + // live_object(p) + // ∧ offset(p)+s≤object_size(p) + // ∧ writeable_object(p) if applicable + + auto live_object = state_live_object_exprt(state, pointer); + + auto writeable_object = state_writeable_object_exprt(state, pointer); + + auto ssize_type = signed_size_type(); + auto offset = pointer_offset_exprt(pointer, ssize_type); + + auto size_type = ::size_type(); + + // extend one bit, to cover overflow case + auto size_type_ext = unsignedbv_typet(size_type.get_width() + 1); + + auto object_size = state_object_size_exprt(state, pointer, size_type); + + auto object_size_casted = typecast_exprt(object_size, size_type_ext); + + auto offset_casted_to_unsigned = + typecast_exprt::conditional_cast(offset, size_type); + auto offset_extended = + typecast_exprt::conditional_cast(offset_casted_to_unsigned, size_type_ext); + auto size_casted = typecast_exprt::conditional_cast(size, size_type_ext); + auto upper = binary_relation_exprt( + plus_exprt(offset_extended, size_casted), ID_le, object_size_casted); + + auto conjunction = and_exprt(live_object, upper); + + if(ok_expr.id() == ID_state_w_ok || ok_expr.id() == ID_state_rw_ok) + conjunction.add_to_operands(std::move(writeable_object)); + + return std::move(conjunction); +} diff --git a/src/cprover/flatten_ok_expr.h b/src/cprover/flatten_ok_expr.h new file mode 100644 index 00000000000..258e47aa2e3 --- /dev/null +++ b/src/cprover/flatten_ok_expr.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: Flatten OK Expressions + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_FLATTEN_OK_EXPR_H +#define CPROVER_CPROVER_FLATTEN_OK_EXPR_H + +#include "state.h" + +// X_ok(p, s) <--> +// live_object(p) +// ∧ offset(p)+s≤object_size(p) +// ∧ writeable_object(p) if applicable + +exprt flatten(const state_ok_exprt &); + +#endif // CPROVER_CPROVER_FLATTEN_OK_EXPR_H diff --git a/src/cprover/format_hooks.cpp b/src/cprover/format_hooks.cpp new file mode 100644 index 00000000000..ca999ec50df --- /dev/null +++ b/src/cprover/format_hooks.cpp @@ -0,0 +1,145 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "format_hooks.h" + +#include +#include +#include +#include + +#include "state.h" + +void format_hooks() +{ + add_format_hook( + ID_object_address, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &object_address_expr = to_object_address_expr(expr); + os << u8"\u275d" << object_address_expr.object_identifier() << u8"\u275e"; + return os; + }); + + add_format_hook( + ID_object_size, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &object_size_expr = to_object_size_expr(expr); + os << "object_size(" << format(object_size_expr.op()) << ')'; + return os; + }); + + add_format_hook( + ID_pointer_offset, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &pointer_offset_expr = to_pointer_offset_expr(expr); + os << "pointer_offset(" << format(pointer_offset_expr.op()) << ')'; + return os; + }); + + add_format_hook( + ID_state_object_size, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &object_size_expr = to_binary_expr(expr); + os << "object_size(" << format(object_size_expr.op0()) << ", " + << format(object_size_expr.op1()) << ')'; + return os; + }); + + add_format_hook( + ID_field_address, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &field_address_expr = to_field_address_expr(expr); + os << format(field_address_expr.base()) << u8".\u275d" + << field_address_expr.component_name() << u8"\u275e"; + return os; + }); + + add_format_hook( + ID_evaluate, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &evaluate_expr = to_evaluate_expr(expr); + if(evaluate_expr.op0().id() == ID_symbol) + os << format(evaluate_expr.op0()); + else + os << '(' << format(evaluate_expr.op0()) << ')'; + os << '(' << format(evaluate_expr.op1()) << ')'; + return os; + }); + + add_format_hook( + ID_update_state, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &update_state_expr = to_update_state_expr(expr); + os << format(update_state_expr.state()) << '[' + << format(update_state_expr.address()) + << ":=" << format(update_state_expr.new_value()) << ']'; + return os; + }); + + add_format_hook( + ID_is_cstring, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &is_cstring_expr = to_unary_expr(expr); + return os << "is_cstring(" << format(is_cstring_expr.op()) << ')'; + }); + + add_format_hook( + ID_state_is_cstring, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &is_cstring_expr = to_state_is_cstring_expr(expr); + return os << "is_cstring(" << format(is_cstring_expr.state()) << ", " + << format(is_cstring_expr.address()) << ')'; + }); + + add_format_hook( + ID_state_is_dynamic_object, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &is_dynamic_object_expr = + to_state_is_dynamic_object_expr(expr); + return os << "is_dynamic_object(" + << format(is_dynamic_object_expr.state()) << ", " + << format(is_dynamic_object_expr.address()) << ')'; + }); + + add_format_hook( + ID_state_r_ok, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &r_ok_expr = to_ternary_expr(expr); + return os << "r_ok(" << format(r_ok_expr.op0()) << ", " + << format(r_ok_expr.op1()) << ", " << format(r_ok_expr.op2()) + << ')'; + }); + + add_format_hook( + ID_state_live_object, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &live_object_expr = to_binary_expr(expr); + return os << "live_object(" << format(live_object_expr.op0()) << ", " + << format(live_object_expr.op1()) << ')'; + }); + + add_format_hook( + ID_state_writeable_object, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &writeable_object_expr = to_binary_expr(expr); + return os << "writeable_object(" << format(writeable_object_expr.op0()) + << ", " << format(writeable_object_expr.op1()) << ')'; + }); + + add_format_hook( + ID_state_type_compatible, + [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &type_compatible_expr = to_state_type_compatible_expr(expr); + return os << "type_compatible(" << format(type_compatible_expr.state()) + << ", " << format(type_compatible_expr.address()) << ')'; + }); + + add_format_hook( + ID_side_effect, [](std::ostream &os, const exprt &expr) -> std::ostream & { + const auto &side_effect_expr = to_side_effect_expr(expr); + if(side_effect_expr.get_statement() == ID_nondet) + return os << "nondet " << format(side_effect_expr.type()); + else + return os << "side-effect-" << side_effect_expr.get_statement(); + }); +} diff --git a/src/cprover/format_hooks.h b/src/cprover/format_hooks.h new file mode 100644 index 00000000000..fd498833b99 --- /dev/null +++ b/src/cprover/format_hooks.h @@ -0,0 +1,14 @@ +/*******************************************************************\ + +Module: + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_FORMAT_HOOKS_H +#define CPROVER_CPROVER_FORMAT_HOOKS_H + +void format_hooks(); + +#endif // CPROVER_CPROVER_FORMAT_HOOKS_H diff --git a/src/cprover/free_symbols.cpp b/src/cprover/free_symbols.cpp new file mode 100644 index 00000000000..b20d0a75092 --- /dev/null +++ b/src/cprover/free_symbols.cpp @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: Free Symbols + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file util/substitute_symbols.cpp +/// Free Symbols + +#include "free_symbols.h" + +#include + +static void free_symbols_rec( + const std::unordered_set &bound_symbols, + const exprt &src, + const std::function &f) +{ + if(src.id() == ID_symbol) + { + const auto &symbol_expr = to_symbol_expr(src); + auto b_it = bound_symbols.find(symbol_expr); + if(b_it == bound_symbols.end()) + f(symbol_expr); + } + else if( + src.id() == ID_forall || src.id() == ID_exists || src.id() == ID_lambda) + { + // bindings may hide symbols + const auto &binding_expr = to_binding_expr(src); + + auto new_bound_symbols = bound_symbols; // copy + + for(const auto &s : binding_expr.variables()) + new_bound_symbols.insert(s); + + free_symbols_rec(new_bound_symbols, binding_expr.where(), f); + } + else if(src.id() == ID_let) + { + // bindings may hide symbols + const auto &let_expr = to_let_expr(src); + + for(const auto &v : let_expr.values()) + free_symbols_rec(bound_symbols, v, f); + + auto new_bound_symbols = bound_symbols; // copy + + for(const auto &s : let_expr.variables()) + new_bound_symbols.insert(s); + + free_symbols_rec(new_bound_symbols, let_expr.where(), f); + } + else + { + for(const auto &op : src.operands()) + free_symbols_rec(bound_symbols, op, f); + } +} + +void free_symbols( + const exprt &expr, + const std::function &f) +{ + std::unordered_set free_symbols, bound_symbols; + free_symbols_rec(bound_symbols, expr, f); +} diff --git a/src/cprover/free_symbols.h b/src/cprover/free_symbols.h new file mode 100644 index 00000000000..b33f76a1d75 --- /dev/null +++ b/src/cprover/free_symbols.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: Free Symbols + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_FREE_SYMBOLS_H +#define CPROVER_CPROVER_FREE_SYMBOLS_H + +/// \file +/// Free Symbols + +#include + +#include + +void free_symbols( + const exprt &, + const std::function &); + +#endif // CPROVER_CPROVER_FREE_SYMBOLS_H diff --git a/src/cprover/help_formatter.cpp b/src/cprover/help_formatter.cpp new file mode 100644 index 00000000000..c92be2888dc --- /dev/null +++ b/src/cprover/help_formatter.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Help Formatter + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "help_formatter.h" + +#include "console.h" + +#include + +void help_formattert::operator()(std::ostream &out) const +{ + std::size_t pos = 0; + auto next = [this, &pos]() -> char { + if(pos < s.size()) + return s[pos++]; + else + return 0; + }; + + std::size_t col = 0; + + while(pos < s.size()) + { + auto ch = next(); + + if(ch == '{') + { + auto what = next(); + switch(what) + { + case 'b': + out << consolet::bold; + break; + case 'u': + out << consolet::underline; + break; + case 'y': + out << consolet::yellow; + break; + default: + break; + } + + while(pos < s.size() && (ch = next()) != '}') + { + out << ch; + col++; + } + + out << consolet::reset; + } + else if(ch == '\t') + { + if(col < 29) + { + out << std::string(29 - col, ' '); + col = 40; + } + } + else if(ch == '\n') + { + out << ch; + col = 0; + } + else + { + out << ch; + col++; + } + } +} diff --git a/src/cprover/help_formatter.h b/src/cprover/help_formatter.h new file mode 100644 index 00000000000..2b1f028f89a --- /dev/null +++ b/src/cprover/help_formatter.h @@ -0,0 +1,41 @@ +/*******************************************************************\ + +Module: Help Formatter + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Help Formatter + +#ifndef CPROVER_CPROVER_HELP_FORMATTER_H +#define CPROVER_CPROVER_HELP_FORMATTER_H + +#include +#include + +class help_formattert +{ +public: + explicit help_formattert(const std::string &_s) : s(_s) + { + } + + const std::string &s; + void operator()(std::ostream &) const; +}; + +static inline help_formattert help_formatter(const std::string &s) +{ + return help_formattert(s); +} + +static inline std::ostream & +operator<<(std::ostream &out, const help_formattert &h) +{ + h(out); + return out; +} + +#endif // CPROVER_CPROVER_HELP_FORMATTER_H diff --git a/src/cprover/instrument_contracts.cpp b/src/cprover/instrument_contracts.cpp new file mode 100644 index 00000000000..1c546e31d65 --- /dev/null +++ b/src/cprover/instrument_contracts.cpp @@ -0,0 +1,566 @@ +/*******************************************************************\ + +Module: Instrument Contracts + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Instrument Contracts + +#include "instrument_contracts.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#define MAX_TEXT 20 + +optionalt +get_contract(const irep_idt &function_identifier, const namespacet &ns) +{ + // contracts are in a separate symbol, with prefix "contract::" + auto contract_identifier = "contract::" + id2string(function_identifier); + const symbolt *symbol_ptr; + if(ns.lookup(contract_identifier, symbol_ptr)) + return {}; // symbol not found + else + return to_code_with_contract_type(symbol_ptr->type); +} + +bool has_contract(const irep_idt &function_identifier, const namespacet &ns) +{ + return get_contract(function_identifier, ns).has_value(); +} + +static std::string expr2text(const exprt &src, const namespacet &ns) +{ + auto text = expr2c(src, ns); + if(text.size() >= MAX_TEXT) + return std::string(text, 0, MAX_TEXT - 3) + "..."; + else + return text; +} + +static exprt make_address(exprt src) +{ + if(src.id() == ID_dereference) + { + return to_dereference_expr(src).pointer(); + } + else + return address_of_exprt(src); +} + +// add the function to the source location +source_locationt +add_function(irep_idt function_identifier, source_locationt src) +{ + if(!src.get_file().empty()) + src.set_function(function_identifier); + + return src; +} + +// add the function to the source location +exprt add_function(irep_idt function_identifier, exprt src) +{ + for(auto &op : src.operands()) + op = add_function(function_identifier, op); + + if(!src.source_location().get_file().empty()) + src.add_source_location().set_function(function_identifier); + + return src; +} + +exprt replace_source_location( + exprt src, + const source_locationt &source_location) +{ + for(auto &op : src.operands()) + op = replace_source_location(op, source_location); + + src.add_source_location() = source_location; + + return src; +} + +static bool is_symbol_member(const exprt &expr) +{ + if(expr.id() == ID_symbol) + return true; + else if(expr.id() == ID_member) + return is_symbol_member(to_member_expr(expr).struct_op()); + else + return false; +} + +exprt assigns_match(const exprt &assigns, const exprt &lhs) +{ + if(is_symbol_member(lhs) && assigns == lhs) + return true_exprt(); // trivial match + + if(lhs.id() == ID_member) + { + if(assigns_match(assigns, to_member_expr(lhs).struct_op()).is_true()) + return true_exprt(); + } + else if(lhs.id() == ID_index) + { + if(assigns_match(assigns, to_index_expr(lhs).array()).is_true()) + return true_exprt(); + } + + auto assigns_address = make_address(assigns); + auto lhs_address = make_address(lhs); + + if(lhs.type() == assigns.type()) + { + return equal_exprt(assigns_address, lhs_address); + } + else + { + // need to compare offset ranges + auto same_object = ::same_object(assigns_address, lhs_address); + return same_object; + } +} + +static exprt instantiate_contract_lambda(exprt src) +{ + return src.id() == ID_lambda ? to_lambda_expr(src).where() : src; +} + +static exprt make_assigns_assertion( + irep_idt function_identifier, + const exprt::operandst &assigns, + const exprt &lhs) +{ + exprt::operandst disjuncts; + + for(auto &assigns_clause : assigns) + { + auto a = instantiate_contract_lambda(assigns_clause); + + if(a.id() == ID_conditional_target_group) + { + auto &condition = to_binary_expr(a).op0(); + auto &targets = to_multi_ary_expr(to_binary_expr(a).op1()); + for(auto &target : targets.operands()) + { + auto target_address = make_address(target); + auto lhs_address = make_address(lhs); + lhs_address = + typecast_exprt::conditional_cast(lhs_address, target_address.type()); + disjuncts.push_back( + and_exprt(condition, equal_exprt(target_address, lhs_address))); + } + } + else + { + auto match = assigns_match(a, lhs); + + // trivial? + if(match.is_true()) + return true_exprt(); + + disjuncts.push_back(std::move(match)); + } + } + + return disjunction(disjuncts); +} + +static bool +is_procedure_local(const irep_idt &function_identifier, const exprt &lhs) +{ + if(lhs.id() == ID_member) + return is_procedure_local( + function_identifier, to_member_expr(lhs).struct_op()); + else if(lhs.id() == ID_index) + return is_procedure_local(function_identifier, to_index_expr(lhs).array()); + else if(lhs.id() == ID_symbol) + { + const auto &symbol_expr = to_symbol_expr(lhs); + return has_prefix( + id2string(symbol_expr.get_identifier()), + id2string(function_identifier) + "::"); + } + else + return false; +} + +static bool is_old(const exprt &lhs) +{ + if(lhs.id() == ID_symbol) + { + const auto &symbol_expr = to_symbol_expr(lhs); + return has_prefix(id2string(symbol_expr.get_identifier()), "old::"); + } + else + return false; +} + +symbol_exprt find_old_expr( + const exprt &src, + const std::string &prefix, + std::vector> &old_exprs) +{ + for(std::size_t i = 0; i < old_exprs.size(); i++) + { + if(old_exprs[i].second == src) + return old_exprs[i].first; + } + + auto index = old_exprs.size(); + irep_idt identifier = prefix + std::to_string(index); + old_exprs.emplace_back(symbol_exprt(identifier, src.type()), src); + + return old_exprs.back().first; +} + +exprt replace_old( + exprt src, + const std::string &prefix, + std::vector> &old_exprs) +{ + if(src.id() == ID_old) + { + const auto &old_expr = to_unary_expr(src); + return find_old_expr(old_expr.op(), prefix, old_exprs); + } + else + { + // rec. call + for(auto &op : src.operands()) + op = replace_old(op, prefix, old_exprs); + return src; + } +} + +goto_programt old_assignments( + const std::vector> &old_exprs, + const source_locationt &source_location) +{ + goto_programt dest; + + for(const auto &old_expr : old_exprs) + { + auto lhs = old_expr.first; + auto fixed_rhs = replace_source_location(old_expr.second, source_location); + auto assignment_instruction = + goto_programt::make_assignment(lhs, fixed_rhs, source_location); + dest.add(std::move(assignment_instruction)); + } + + return dest; +} + +void instrument_contract_checks( + goto_functionst::function_mapt::value_type &f, + const namespacet &ns) +{ + // contracts are in a separate symbol, with prefix "contract::" + auto contract_identifier = "contract::" + id2string(f.first); + const symbolt *symbol_ptr; + if(ns.lookup(contract_identifier, symbol_ptr)) + return; // nothing to check + + auto &contract = to_code_with_contract_type(symbol_ptr->type); + + auto &body = f.second.body; + + if(body.instructions.empty()) + return; // nothing to check + + // new instructions to add at the beginning of the function + goto_programt add_at_beginning; + + // precondition? + if(!contract.requires().empty()) + { + // stick these in as assumptions, preserving the ordering + goto_programt dest; + for(auto &assumption : contract.requires()) + { + exprt assumption_instance = instantiate_contract_lambda(assumption); + auto fixed_assumption = add_function(f.first, assumption_instance); + add_at_beginning.add(goto_programt::make_assumption( + fixed_assumption, fixed_assumption.source_location())); + } + } + + // record "old(...)" expressions. + std::vector> old_exprs; + const auto old_prefix = "old::" + id2string(f.first); + + // postcondition? + if(!contract.ensures().empty()) + { + // Stick the postconditions in as assertions at the end + auto last = body.instructions.end(); + if(std::prev(last)->is_end_function()) + last = std::prev(last); + + for(auto &assertion : contract.ensures()) + { + exprt assertion_instance = instantiate_contract_lambda(assertion); + + std::string comment = "postcondition"; + if(contract.ensures().size() >= 2) + comment += " " + expr2text(assertion_instance, ns); + + auto location = assertion.source_location(); + location.set_function(f.first); // seems to be missing + location.set_property_class(ID_postcondition); + location.set_comment(comment); + + auto replaced_assertion = + replace_old(assertion_instance, old_prefix, old_exprs); + + auto fixed_assertion = add_function(f.first, replaced_assertion); + + auto assertion_instruction = + goto_programt::make_assertion(fixed_assertion, std::move(location)); + + body.insert_before_swap(last, assertion_instruction); + } + } + + // do 'old' in the body + if( + !contract.assigns().empty() || !contract.requires().empty() || + !contract.ensures().empty()) + { + for(auto &instruction : body.instructions) + instruction.transform( + [&old_prefix, &old_exprs](exprt expr) -> optionalt { + return replace_old(expr, old_prefix, old_exprs); + }); + } + + // Add assignments to 'old' symbols at the beginning of the function. + { + auto tmp = + old_assignments(old_exprs, add_function(f.first, symbol_ptr->location)); + add_at_beginning.destructive_append(tmp); + } + + body.destructive_insert(body.instructions.begin(), add_at_beginning); + + // assigns? + if( + !contract.assigns().empty() || !contract.requires().empty() || + !contract.ensures().empty()) + { + for(auto it = body.instructions.begin(); it != body.instructions.end(); + it++) + { + if(it->is_assign()) + { + const auto &lhs = it->assign_lhs(); + + // Parameter or local or old? Ignore. + if(is_procedure_local(f.first, lhs)) + continue; // ok + + if(is_old(lhs)) + continue; // ok + + // maybe not ok + auto assigns_assertion = + make_assigns_assertion(f.first, contract.assigns(), lhs); + auto location = it->source_location(); + location.set_property_class("assigns"); + location.set_comment("assigns clause"); + auto assertion_instruction = goto_programt::make_assertion( + std::move(assigns_assertion), std::move(location)); + body.insert_before_swap(it, assertion_instruction); + it++; // skip over the assertion we have just generated + } + } + } +} + +void replace_function_calls_by_contracts( + goto_functionst::function_mapt::value_type &f, + const goto_modelt &goto_model) +{ + auto &body = f.second.body; + const namespacet ns(goto_model.symbol_table); + + std::size_t call_site_counter = 0; + + for(auto it = body.instructions.begin(); it != body.instructions.end(); it++) + { + if(it->is_function_call()) + { + const auto &function = it->call_function(); + if(function.id() == ID_symbol) + { + const auto &symbol = ns.lookup(to_symbol_expr(function)); + + const auto contract_opt = get_contract(symbol.name, ns); + + if(!contract_opt.has_value()) + continue; + + auto &contract = contract_opt.value(); + + // record "old(...)" expressions. + std::vector> old_exprs; + const auto old_prefix = "old::" + id2string(f.first) + "::call-site-" + + std::to_string(++call_site_counter) + "::"; + + // need to substitute parameters + const auto f_it = + goto_model.goto_functions.function_map.find(symbol.name); + + if(f_it == goto_model.goto_functions.function_map.end()) + DATA_INVARIANT(false, "failed to find function in function_map"); + + replace_symbolt replace_symbol; + const auto ¶meters = to_code_type(symbol.type).parameters(); + const auto &arguments = it->call_arguments(); + + for(std::size_t p = 0; p < f_it->second.parameter_identifiers.size(); + p++) + { + auto p_symbol = symbol_exprt( + f_it->second.parameter_identifiers[p], parameters[p].type()); + replace_symbol.insert(p_symbol, arguments[p]); + } + + // replace __CPROVER_return_value by the lhs of the call + const auto &call_lhs = it->call_lhs(); + replace_symbol.insert( + symbol_exprt(CPROVER_PREFIX "return_value", call_lhs.type()), + call_lhs); + + goto_programt dest; + + // assert the preconditions + for(auto &precondition : contract.requires()) + { + auto instantiated_precondition = + instantiate_contract_lambda(precondition); + + auto location = it->source_location(); + location.set_property_class(ID_precondition); + location.set_comment( + id2string(symbol.display_name()) + " precondition " + + expr2text(instantiated_precondition, ns)); + + auto replaced_precondition = instantiated_precondition; + replace_symbol(replaced_precondition); + + dest.add( + goto_programt::make_assertion(replaced_precondition, location)); + } + + // havoc the 'assigned' variables + for(auto &assigns_clause_lambda : contract.assigns()) + { + auto location = it->source_location(); + + auto assigns_clause = + instantiate_contract_lambda(assigns_clause_lambda); + + if(assigns_clause.id() == ID_conditional_target_group) + { + const auto &condition = to_binary_expr(assigns_clause).op0(); + auto replaced_condition = condition; + replace_symbol(replaced_condition); + + const auto &targets = + to_multi_ary_expr(to_binary_expr(assigns_clause).op1()) + .operands(); + + for(auto &target : targets) + { + auto rhs = side_effect_expr_nondett(target.type(), location); + + auto replaced_lhs = target; + replace_symbol(replaced_lhs); + + auto goto_instruction = + dest.add(goto_programt::make_incomplete_goto( + not_exprt(replaced_condition), location)); + + dest.add( + goto_programt::make_assignment(replaced_lhs, rhs, location)); + + auto skip_instruction = + dest.add(goto_programt::make_skip(location)); + + goto_instruction->complete_goto(skip_instruction); + } + } + else + { + const auto &lhs = assigns_clause; + auto rhs = side_effect_expr_nondett(lhs.type(), location); + + auto replaced_lhs = lhs; + replace_symbol(replaced_lhs); + auto fixed_lhs = replace_source_location(replaced_lhs, location); + + dest.add(goto_programt::make_assignment(fixed_lhs, rhs, location)); + } + } + + // assume the postconditions + for(auto &postcondition : contract.ensures()) + { + auto &location = it->source_location(); + + auto replaced_postcondition1 = + instantiate_contract_lambda(postcondition); + replace_symbol(replaced_postcondition1); + + auto replaced_postcondition2 = + replace_old(replaced_postcondition1, old_prefix, old_exprs); + + dest.add( + goto_programt::make_assumption(replaced_postcondition2, location)); + } + + // now insert the assignents to old::... at the beginning + // of 'dest' + { + auto tmp = old_assignments(old_exprs, it->source_location()); + dest.destructive_insert(dest.instructions.begin(), tmp); + } + + // remove the function call + it->turn_into_skip(); + + // insert after 'it' to preserve branches to the call + body.destructive_insert(std::next(it), dest); + } + } + } +} + +void instrument_contracts(goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + for(auto &f : goto_model.goto_functions.function_map) + { + instrument_contract_checks(f, ns); + replace_function_calls_by_contracts(f, goto_model); + } +} diff --git a/src/cprover/instrument_contracts.h b/src/cprover/instrument_contracts.h new file mode 100644 index 00000000000..f33f17c1c02 --- /dev/null +++ b/src/cprover/instrument_contracts.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Instrument Contracts + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Instrument Given Invariants + +#ifndef CPROVER_CPROVER_INSTRUMENT_CONTRACTS_H +#define CPROVER_CPROVER_INSTRUMENT_CONTRACTS_H + +#include +#include + +class code_with_contract_typet; +class goto_modelt; +class namespacet; + +void instrument_contracts(goto_modelt &); + +optionalt +get_contract(const irep_idt &function_identifier, const namespacet &); + +#endif // CPROVER_CPOVER_INSTRUMENT_CONTRACTS_H diff --git a/src/cprover/instrument_given_invariants.cpp b/src/cprover/instrument_given_invariants.cpp new file mode 100644 index 00000000000..9cd5bdc4186 --- /dev/null +++ b/src/cprover/instrument_given_invariants.cpp @@ -0,0 +1,48 @@ +/*******************************************************************\ + +Module: Instrument Given Invariants + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Instrument Given Invariants + +#include "instrument_given_invariants.h" + +#include + +void instrument_given_invariants(goto_functionst::function_mapt::value_type &f) +{ + auto &body = f.second.body; + + for(auto it = body.instructions.begin(); it != body.instructions.end(); it++) + { + // annotated invariants -- these are stuck to the condition + // of the (backwards) goto + if(it->is_backwards_goto()) + { + const auto &invariants = static_cast( + it->condition().find(ID_C_spec_loop_invariant)); + + for(const auto &invariant : invariants.operands()) + { + auto source_location = invariant.source_location(); // copy + source_location.set_property_class("invariant"); + source_location.set_comment("loop invariant"); + + auto assertion = + goto_programt::make_assertion(invariant, source_location); + + body.insert_before_swap(it->get_target(), assertion); + } + } + } +} + +void instrument_given_invariants(goto_modelt &goto_model) +{ + for(auto &f : goto_model.goto_functions.function_map) + instrument_given_invariants(f); +} diff --git a/src/cprover/instrument_given_invariants.h b/src/cprover/instrument_given_invariants.h new file mode 100644 index 00000000000..1685573dc06 --- /dev/null +++ b/src/cprover/instrument_given_invariants.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Instrument Given Invariants + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Instrument Given Invariants + +#ifndef CPROVER_CPROVER_INSTRUMENT_GIVEN_INVARIANTS_H +#define CPROVER_CPROVER_INSTRUMENT_GIVEN_INVARIANTS_H + +class goto_modelt; + +void instrument_given_invariants(goto_modelt &); + +#endif // CPROVER_CPOVER_INSTRUMENT_GIVEN_INVARIANTS_H diff --git a/src/cprover/may_alias.cpp b/src/cprover/may_alias.cpp new file mode 100644 index 00000000000..9dc02a0621e --- /dev/null +++ b/src/cprover/may_alias.cpp @@ -0,0 +1,266 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// May Alias + +#include "may_alias.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +bool permitted_by_strict_aliasing(const typet &a, const typet &b) +{ + // C99; ISO/IEC 9899:1999 6.5/7 + if(a == b) + return true; + else if(a == signed_char_type() || a == unsigned_char_type()) + return true; // char * can alias anyting + else if(b == signed_char_type() || b == unsigned_char_type()) + return true; // char * can alias anyting + else if( + (a.id() == ID_unsignedbv || a.id() == ID_signedbv) && + (b.id() == ID_unsignedbv || b.id() == ID_signedbv)) + { + // signed/unsigned of same width can alias + return to_bitvector_type(a).get_width() == to_bitvector_type(b).get_width(); + } + else if(a.id() == ID_empty) + return true; // void * can alias any pointer + else if(b.id() == ID_empty) + return true; // void * can alias any pointer + else if( + a.id() == ID_pointer && to_pointer_type(a).base_type().id() == ID_empty) + return true; // void * can alias any pointer + else if( + b.id() == ID_pointer && to_pointer_type(b).base_type().id() == ID_empty) + return true; // void * can alias any pointer + else if( + a.id() == ID_pointer && to_pointer_type(a).base_type().id() == ID_pointer && + to_pointer_type(to_pointer_type(a).base_type()).base_type().id() == + ID_empty) + return true; // void * can alias any pointer + else if( + b.id() == ID_pointer && to_pointer_type(b).base_type().id() == ID_pointer && + to_pointer_type(to_pointer_type(b).base_type()).base_type().id() == + ID_empty) + return true; // void * can alias any pointer + else + { + return false; + } +} + +bool is_object_field_element(const exprt &expr) +{ + if(expr.id() == ID_object_address) + return true; + else if(expr.id() == ID_element_address) + return is_object_field_element(to_element_address_expr(expr).base()); + else if(expr.id() == ID_field_address) + return is_object_field_element(to_field_address_expr(expr).base()); + else + return false; +} + +bool prefix_of(const typet &a, const typet &b, const namespacet &ns) +{ + if(a == b) + return true; + + if(a.id() == ID_struct_tag) + return prefix_of(ns.follow_tag(to_struct_tag_type(a)), b, ns); + + if(b.id() == ID_struct_tag) + return prefix_of(a, ns.follow_tag(to_struct_tag_type(b)), ns); + + if(a.id() != ID_struct || b.id() != ID_struct) + return false; + + const auto &a_struct = to_struct_type(a); + const auto &b_struct = to_struct_type(b); + + return a_struct.is_prefix_of(b_struct) || b_struct.is_prefix_of(a_struct); +} + +static optionalt find_object(const exprt &expr) +{ + if(expr.id() == ID_object_address) + return to_object_address_expr(expr); + else if(expr.id() == ID_field_address) + return find_object(to_field_address_expr(expr).base()); + else if(expr.id() == ID_element_address) + return find_object(to_element_address_expr(expr).base()); + else + return {}; +} + +// Is 'expr' on the stack and it's address is not taken? +bool stack_and_not_dirty( + const exprt &expr, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + auto object = find_object(expr); + if(object.has_value()) + { + auto symbol_expr = object->object_expr(); + auto identifier = symbol_expr.get_identifier(); + if(has_prefix(id2string(identifier), "va_arg::")) + return true; // on the stack, and might alias + else if(has_prefix(id2string(identifier), "var_args::")) + return false; // on the stack -- can take address? + else if(has_prefix(id2string(identifier), "va_args::")) + return false; // on the stack -- can take address? + else if(has_prefix(id2string(identifier), "va_arg_array::")) + return false; // on the stack -- can take address? + else if(has_prefix(id2string(identifier), "old::")) + return true; // on the stack, but can't take address + else if(identifier == "return_value") + return true; // on the stack, but can't take address + const auto &symbol = ns.lookup(symbol_expr); + return !symbol.is_static_lifetime && + address_taken.find(symbol_expr) == address_taken.end(); + } + else + return false; +} + +static exprt drop_pointer_typecasts(exprt src) +{ + if( + src.id() == ID_typecast && + to_typecast_expr(src).op().type().id() == ID_pointer) + return drop_pointer_typecasts(to_typecast_expr(src).op()); + else + return src; +} + +optionalt +same_address(const exprt &a, const exprt &b, const namespacet &ns) +{ + static const auto true_expr = true_exprt(); + static const auto false_expr = false_exprt(); + + // syntactically the same? + if(drop_pointer_typecasts(a) == drop_pointer_typecasts(b)) + return true_expr; + + // a and b are both object/field/element? + if(is_object_field_element(a) && is_object_field_element(b)) + { + if(a.id() == ID_object_address && b.id() == ID_object_address) + { + if( + to_object_address_expr(a).object_identifier() == + to_object_address_expr(b).object_identifier()) + { + return true_expr; // the same + } + else + return false_expr; // different + } + else if(a.id() == ID_element_address && b.id() == ID_element_address) + { + const auto &a_element_address = to_element_address_expr(a); + const auto &b_element_address = to_element_address_expr(b); + + // rec. call + auto base_same_address = + same_address(a_element_address.base(), b_element_address.base(), ns); + + CHECK_RETURN(base_same_address.has_value()); + + if(base_same_address->is_false()) + return false_expr; + else + { + return and_exprt( + *base_same_address, + equal_exprt(a_element_address.index(), b_element_address.index())); + } + } + else if(a.id() == ID_field_address && b.id() == ID_field_address) + { + const auto &a_field_address = to_field_address_expr(a); + const auto &b_field_address = to_field_address_expr(b); + + // structs can't alias unless one is a prefix of the other + if(!prefix_of( + a_field_address.type().base_type(), + b_field_address.type().base_type(), + ns)) + { + return false_expr; + } + + if(a_field_address.component_name() == b_field_address.component_name()) + { + // rec. call + return same_address(a_field_address.base(), b_field_address.base(), ns); + } + else + return false_expr; + } + else + return false_expr; + } + + // don't know + return {}; +} + +optionalt may_alias( + const exprt &a, + const exprt &b, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + PRECONDITION(a.type().id() == ID_pointer); + PRECONDITION(b.type().id() == ID_pointer); + + static const auto true_expr = true_exprt(); + static const auto false_expr = false_exprt(); + + // syntactically the same? + if(drop_pointer_typecasts(a) == drop_pointer_typecasts(b)) + return true_expr; + + // consider the strict aliasing rule + const auto &a_base = to_pointer_type(a.type()).base_type(); + const auto &b_base = to_pointer_type(b.type()).base_type(); + + if(!permitted_by_strict_aliasing(a_base, b_base)) + { + // The object is known to be different, because of strict aliasing. + return false_expr; + } + + // a and b the same addresses? + auto same_address_opt = same_address(a, b, ns); + if(same_address_opt.has_value()) + return same_address_opt; + + // is one of them stack-allocated and it's address is not taken? + if(stack_and_not_dirty(a, address_taken, ns)) + return false_expr; // can't alias + + if(stack_and_not_dirty(b, address_taken, ns)) + return false_expr; // can't alias + + // we don't know + return {}; +} diff --git a/src/cprover/may_alias.h b/src/cprover/may_alias.h new file mode 100644 index 00000000000..7c42bb01f55 --- /dev/null +++ b/src/cprover/may_alias.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// May Alias + +#ifndef CPROVER_CPROVER_MAY_ALIAS_H +#define CPROVER_CPROVER_MAY_ALIAS_H + +#include + +#include + +class namespacet; + +bool is_object_field_element(const exprt &); + +// check whether the given two addresses may be aliases +optionalt may_alias( + const exprt &, + const exprt &, + const std::unordered_set &address_taken, + const namespacet &); + +bool stack_and_not_dirty( + const exprt &, + const std::unordered_set &address_taken, + const namespacet &); + +#endif // CPROVER_CPROVER_MAY_ALIAS_H diff --git a/src/cprover/may_be_same_object.cpp b/src/cprover/may_be_same_object.cpp new file mode 100644 index 00000000000..95a2ac8bbd7 --- /dev/null +++ b/src/cprover/may_be_same_object.cpp @@ -0,0 +1,52 @@ +/*******************************************************************\ + +Module: May Be Same Object + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// May Be Same Object + +#include "may_be_same_object.h" + +#include +#include + +#include "may_alias.h" + +exprt strip_member_element(const exprt &src) +{ + if(src.id() == ID_element_address) + return strip_member_element(to_element_address_expr(src).base()); + else if(src.id() == ID_field_address) + return strip_member_element(to_field_address_expr(src).base()); + else + return src; +} + +exprt may_be_same_object( + const exprt &a, + const exprt &b, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + auto a_stripped = strip_member_element(a); + auto b_stripped = strip_member_element(b); + + if(a_stripped == b_stripped) + return true_exprt(); + else if( + a_stripped.id() == ID_object_address && + b_stripped.id() == ID_object_address) + return false_exprt(); + + if(stack_and_not_dirty(a, address_taken, ns)) + return false_exprt(); + + if(stack_and_not_dirty(b, address_taken, ns)) + return false_exprt(); + + return ::same_object(a_stripped, b_stripped); +} diff --git a/src/cprover/may_be_same_object.h b/src/cprover/may_be_same_object.h new file mode 100644 index 00000000000..54e6c8d4bd8 --- /dev/null +++ b/src/cprover/may_be_same_object.h @@ -0,0 +1,28 @@ +/*******************************************************************\ + +Module: May Be Same Object + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// May Be Same Object + +#ifndef CPROVER_CPROVER_MAY_BE_SAME_OBJECT_H +#define CPROVER_CPROVER_MAY_BE_SAME_OBJECT_H + +#include + +#include + +class namespacet; + +// check whether the given two addresses may point to same object +exprt may_be_same_object( + const exprt &, + const exprt &, + const std::unordered_set &address_taken, + const namespacet &); + +#endif // CPROVER_CPROVER_MAY_BE_SAME_OBJECT_H diff --git a/src/cprover/module_dependencies.txt b/src/cprover/module_dependencies.txt new file mode 100644 index 00000000000..5e719f8b1d8 --- /dev/null +++ b/src/cprover/module_dependencies.txt @@ -0,0 +1,5 @@ +ansi-c +goto-programs +langapi # should go away +solvers +util diff --git a/src/cprover/propagate.cpp b/src/cprover/propagate.cpp new file mode 100644 index 00000000000..a4590d2f5b6 --- /dev/null +++ b/src/cprover/propagate.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Propagate + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Propagate + +#include "propagate.h" + +#include +#include + +#include "simplify_state_expr.h" +#include "state.h" + +#include + +void propagate( + const std::vector &frames, + const workt &work, + const std::unordered_set &address_taken, + bool verbose, + const namespacet &ns, + const std::function + &propagator) +{ + if(verbose) + { + std::cout << "PROP"; + for(const auto &p : work.path) + std::cout << ' ' << p.index; + std::cout << ": " << format(work.invariant) << '\n'; + } + + auto &f = frames[work.frame.index]; + + for(const auto &implication : f.implications) + { + auto &next_state = implication.rhs.arguments().front(); + auto lambda_expr = lambda_exprt({state_expr()}, work.invariant); + auto instance = lambda_expr.instantiate({next_state}); + auto simplified1 = simplify_state_expr(instance, address_taken, ns); + auto simplified1a = simplify_state_expr(simplified1, address_taken, ns); + if(simplified1 != simplified1a) + { + std::cout << "SIMP0: " << format(instance) << "\n"; + std::cout << "SIMP1: " << format(simplified1) << "\n"; + std::cout << "SIMPa: " << format(simplified1a) << "\n"; + abort(); + } + auto simplified2 = simplify_expr(simplified1, ns); + + if(implication.lhs.id() == ID_function_application) + { + // Sxx(ς) ⇒ Syy(ς[update]) + auto &state = to_symbol_expr( + to_function_application_expr(implication.lhs).function()); + propagator(state, simplified2, work.path); + } + else if( + implication.lhs.id() == ID_and && + to_and_expr(implication.lhs).op0().id() == ID_function_application) + { + // Sxx(ς) ∧ ς(COND) ⇒ Syy(ς) + auto &function_application = + to_function_application_expr(to_and_expr(implication.lhs).op0()); + auto &state = to_symbol_expr(function_application.function()); + auto cond = to_and_expr(implication.lhs).op1(); + propagator(state, implies_exprt(cond, simplified2), work.path); + } + } +} diff --git a/src/cprover/propagate.h b/src/cprover/propagate.h new file mode 100644 index 00000000000..3637176260a --- /dev/null +++ b/src/cprover/propagate.h @@ -0,0 +1,38 @@ +/*******************************************************************\ + +Module: Propagate + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Propagate + +#ifndef CPROVER_CPROVER_PROPAGATE_H +#define CPROVER_CPROVER_PROPAGATE_H + +#include "solver_types.h" + +#include + +void propagate( + const std::vector &, + const workt &, + const std::unordered_set &address_taken, + bool verbose, + const namespacet &, + const std::function + &propagator); + +exprt simplify_state_expr( + exprt, + const std::unordered_set &address_taken, + const namespacet &); + +exprt simplify_state_expr_node( + exprt, + const std::unordered_set &address_taken, + const namespacet &); + +#endif // CPROVER_CPROVER_PROPAGATE_H diff --git a/src/cprover/report_properties.cpp b/src/cprover/report_properties.cpp new file mode 100644 index 00000000000..7ef9b1abc29 --- /dev/null +++ b/src/cprover/report_properties.cpp @@ -0,0 +1,113 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Solver + +#include "report_properties.h" + +#include + +#include "console.h" + +#include + +void report_properties(const std::vector &properties) +{ + irep_idt current_file, current_function; + + for(auto &property : properties) + { + const auto &l = property.source_location; + + if(l.get_function() != current_function) + { + if(!current_function.empty()) + consolet::out() << '\n'; + current_function = l.get_function(); + if(!current_function.empty()) + { + current_file = l.get_file(); + if(!current_file.empty()) + consolet::out() << current_file << ' '; + if(!l.get_function().empty()) + consolet::out() << "function " << l.get_function(); + consolet::out() << '\n'; + } + } + + auto property_id = property.property_id(); + consolet::out() << consolet::faint << '['; + if(property_id.empty()) + consolet::out() << '?'; + else + consolet::out() << property_id; + consolet::out() << ']' << consolet::reset; + + if(l.get_file() != current_file) + consolet::out() << ' ' << l.get_file(); + + if(!l.get_line().empty()) + consolet::out() << " line " << l.get_line(); + + auto comment = l.get_comment(); + if(!comment.empty()) + consolet::out() << ' ' << comment; + + consolet::out() << ": "; + + switch(property.status) + { + case propertyt::PASS: + consolet::out() << consolet::green << "SUCCESS" << consolet::reset; + break; + + case propertyt::REFUTED: + consolet::out() << consolet::red << "REFUTED" << consolet::reset; + break; + + case propertyt::ERROR: + consolet::out() << consolet::red << consolet::bold << "ERROR" + << consolet::reset; + break; + + case propertyt::DROPPED: + consolet::out() << consolet::red << consolet::bold << "DROPPED" + << consolet::reset; + break; + + case propertyt::UNKNOWN: + consolet::out() << consolet::yellow << "UNKNOWN" << consolet::reset; + break; + } + +#if 0 + consolet::out() + << ' ' << consolet::faint << std::setw(1) << std::setprecision(1) + << std::chrono::duration(property.stop - property.start).count() + << 's' << consolet::reset; +#endif + + consolet::out() << '\n'; + } +} + +solver_resultt overall_outcome(const std::vector &properties) +{ + auto result = solver_resultt::ALL_PASS; + + for(auto &property : properties) + if(property.status == propertyt::ERROR) + result = solver_resultt::ERROR; + else if(property.status == propertyt::DROPPED) + result = solver_resultt::ERROR; + else if(property.status == propertyt::REFUTED) + result = solver_resultt::SOME_FAIL; + + return result; +} diff --git a/src/cprover/report_properties.h b/src/cprover/report_properties.h new file mode 100644 index 00000000000..8cf7d3abe54 --- /dev/null +++ b/src/cprover/report_properties.h @@ -0,0 +1,22 @@ +/*******************************************************************\ + +Module: Property Reporting + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Property Reporting + +#ifndef CPROVER_CPROVER_REPORT_PROPERTIES_H +#define CPROVER_CPROVER_REPORT_PROPERTIES_H + +#include "solver.h" +#include "solver_types.h" + +void report_properties(const std::vector &); + +solver_resultt overall_outcome(const std::vector &); + +#endif // CPROVER_CPROVER_REPORT_PROPERTIES_H diff --git a/src/cprover/report_traces.cpp b/src/cprover/report_traces.cpp new file mode 100644 index 00000000000..1bf90c1542c --- /dev/null +++ b/src/cprover/report_traces.cpp @@ -0,0 +1,157 @@ +/*******************************************************************\ + +Module: Report Traces + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Solver + +#include "report_traces.h" + +#include + +#include "console.h" +#include "state.h" + +#include + +optionalt address_to_lvalue(exprt src) +{ + if(src.id() == ID_object_address) + return to_object_address_expr(src).object_expr(); + else if(src.id() == ID_field_address) + { + const auto &field_address_expr = to_field_address_expr(src); + auto compound_opt = address_to_lvalue(field_address_expr.base()); + if(compound_opt.has_value()) + return member_exprt( + *compound_opt, + field_address_expr.component_name(), + field_address_expr.field_type()); + else + return {}; + } + else if(src.id() == ID_element_address) + { + const auto &element_address_expr = to_element_address_expr(src); + auto array_opt = address_to_lvalue(element_address_expr.base()); + if(array_opt.has_value()) + return index_exprt( + *array_opt, + element_address_expr.index(), + element_address_expr.element_type()); + else + return {}; + } + else if(src.id() == ID_annotated_pointer_constant) + { + const auto &pointer = + to_annotated_pointer_constant_expr(src).symbolic_pointer(); + if( + pointer.id() == ID_address_of && + to_address_of_expr(pointer).object().id() == ID_symbol) + return to_address_of_expr(pointer).object(); + else + return {}; + } + else + return {}; +} + +void show_trace( + const std::vector &frames, + const propertyt &property, + bool verbose, + const namespacet &ns) +{ + irep_idt function_id, file; + + for(auto &step : property.trace) + { + const auto &frame = frames[step.frame.index]; + + if( + frame.source_location.get_function() != function_id || + frame.source_location.get_file() != file) + { + consolet::out() << consolet::faint << frame.source_location.get_file(); + if(frame.source_location.get_function() != "") + consolet::out() << " function " << frame.source_location.get_function(); + consolet::out() << consolet::reset << '\n'; + file = frame.source_location.get_file(); + function_id = frame.source_location.get_function(); + } + + consolet::out() << consolet::faint << std::setw(4) + << frame.source_location.get_line() << consolet::reset + << ' '; + + if(step.updates.empty()) + { + if(!verbose) + { + consolet::out() << "(no assignment)\n"; + } + else + { + bool first = true; + for(auto &implication : frame.implications) + { + if(first) + first = false; + else + { + consolet::out() << std::setw(4) << ' '; + } + consolet::out() << "constraint: " << format(implication.as_expr()) + << '\n'; + } + } + } + else + { + bool first = true; + for(auto &update : step.updates) + { + if(first) + first = false; + else + { + consolet::out() << std::setw(4) << ' '; + } + + auto lvalue_opt = address_to_lvalue(update.address); + if(lvalue_opt.has_value()) + consolet::out() << format(*lvalue_opt); + else + consolet::out() << '[' << format(update.address) << ']'; + + consolet::out() << " := " << format(update.value); + consolet::out() << '\n'; + } + } + } +} + +void report_traces( + const std::vector &frames, + const std::vector &properties, + bool verbose, + const namespacet &ns) +{ + for(auto &property : properties) + { + if(property.status == propertyt::REFUTED) + { + consolet::out() << '\n' + << "Trace for " << consolet::bold + << property.property_id() << consolet::reset << ':' + << '\n'; + + show_trace(frames, property, verbose, ns); + } + } +} diff --git a/src/cprover/report_traces.h b/src/cprover/report_traces.h new file mode 100644 index 00000000000..163422273bb --- /dev/null +++ b/src/cprover/report_traces.h @@ -0,0 +1,23 @@ +/*******************************************************************\ + +Module: Report Traces + +Author: Daniel Kroening + +\*******************************************************************/ + +/// \file +/// Report Traces + +#ifndef CPROVER_CPROVER_REPORT_TRACES_H +#define CPROVER_CPROVER_REPORT_TRACES_H + +#include "solver_types.h" + +void report_traces( + const std::vector &frames, + const std::vector &properties, + bool verbose, + const namespacet &); + +#endif // CPROVER_CPROVER_REPORT_TRACES_H diff --git a/src/cprover/sentinel_dll.cpp b/src/cprover/sentinel_dll.cpp new file mode 100644 index 00000000000..071e811bb0e --- /dev/null +++ b/src/cprover/sentinel_dll.cpp @@ -0,0 +1,82 @@ +/*******************************************************************\ + +Module: Sentinel Doubly Linked Lists + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Axioms + +#include "sentinel_dll.h" + +#include +#include +#include + +#include "state.h" + +optionalt sentinel_dll_member( + const exprt &state, + const exprt &node, + bool next, // vs. prev + const namespacet &ns) +{ + if(node.type().id() != ID_pointer) + return {}; + + if(to_pointer_type(node.type()).base_type().id() != ID_struct_tag) + return {}; + + const auto &struct_type = + ns.follow_tag(to_struct_tag_type(to_pointer_type(node.type()).base_type())); + + // the first pointer to a struct is 'next', the second 'prev' + const struct_typet::componentt *next_m = nullptr, *prev_m = nullptr; + + for(auto &m : struct_type.components()) + { + if(m.type() == node.type()) // we are strict on the type + { + if(next_m == nullptr) + next_m = &m; + else + prev_m = &m; + } + } + + struct_typet::componentt component; + + if(next) + { + if(next_m == nullptr) + return {}; + else + component = *next_m; + } + else + { + if(prev_m == nullptr) + return {}; + else + component = *prev_m; + } + + auto field_address = field_address_exprt( + node, component.get_name(), pointer_type(component.type())); + + return evaluate_exprt(state, field_address, component.type()); +} + +optionalt +sentinel_dll_next(const exprt &state, const exprt &node, const namespacet &ns) +{ + return sentinel_dll_member(state, node, true, ns); +} + +optionalt +sentinel_dll_prev(const exprt &state, const exprt &node, const namespacet &ns) +{ + return sentinel_dll_member(state, node, false, ns); +} diff --git a/src/cprover/sentinel_dll.h b/src/cprover/sentinel_dll.h new file mode 100644 index 00000000000..88331c6720d --- /dev/null +++ b/src/cprover/sentinel_dll.h @@ -0,0 +1,110 @@ +/*******************************************************************\ + +Module: Sentinel Doubly Linked Lists + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_SENTINEL_DLL_H +#define CPROVER_CPROVER_SENTINEL_DLL_H + +#include + +class state_is_sentinel_dll_exprt : public multi_ary_exprt +{ +public: + state_is_sentinel_dll_exprt(exprt state, exprt head, exprt tail) + : multi_ary_exprt( + ID_state_is_sentinel_dll, + {state, head, tail}, + bool_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->head().type().id() == ID_pointer); + PRECONDITION(this->tail().type().id() == ID_pointer); + } + + state_is_sentinel_dll_exprt(exprt state, exprt head, exprt tail, exprt node) + : multi_ary_exprt( + ID_state_is_sentinel_dll, + {state, head, tail, node}, + bool_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->head().type().id() == ID_pointer); + PRECONDITION(this->tail().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &head() const + { + return op1(); + } + + exprt &head() + { + return op1(); + } + + const exprt &tail() const + { + return op2(); + } + + exprt &tail() + { + return op2(); + } + + // helper + state_is_sentinel_dll_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_is_sentinel_dll_exprt +/// +/// \a expr must be known to be \ref state_is_sentinel_dll_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_is_sentinel_dll_exprt +inline const state_is_sentinel_dll_exprt & +to_state_is_sentinel_dll_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_sentinel_dll); + const state_is_sentinel_dll_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_is_sentinel_dll_expr(const exprt &) +inline state_is_sentinel_dll_exprt &to_state_is_sentinel_dll_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_sentinel_dll); + state_is_sentinel_dll_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +optionalt +sentinel_dll_next(const exprt &state, const exprt &node, const namespacet &); + +optionalt +sentinel_dll_prev(const exprt &state, const exprt &node, const namespacet &); + +#endif // CPROVER_CPROVER_SENTINEL_DLL_H diff --git a/src/cprover/simplify_state_expr.cpp b/src/cprover/simplify_state_expr.cpp new file mode 100644 index 00000000000..dac7612ecf7 --- /dev/null +++ b/src/cprover/simplify_state_expr.cpp @@ -0,0 +1,1103 @@ +/*******************************************************************\ + +Module: Simplify State Expressions + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Simplify State Expressions + +#include "simplify_state_expr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "may_alias.h" +#include "may_be_same_object.h" +#include "sentinel_dll.h" +#include "state.h" + +#include + +std::size_t allocate_counter = 0; + +exprt simplify_state_expr_node( + exprt, + const std::unordered_set &address_taken, + const namespacet &); + +static bool types_are_compatible(const typet &a, const typet &b) +{ + if(a == b) + return true; + else if(a.id() == ID_pointer && b.id() == ID_pointer) + return true; + else + return false; +} + +exprt simplify_evaluate_update( + evaluate_exprt evaluate_expr, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + PRECONDITION(evaluate_expr.state().id() == ID_update_state); + + const auto &update_state_expr = to_update_state_expr(evaluate_expr.state()); + +#if 0 + std::cout << "U: " << format(update_state_expr) << "\n"; + std::cout << "u: " << format(update_state_expr.address()) << "\n"; + std::cout << "T: " << format(update_state_expr.address().type()) << "\n"; + std::cout << "E: " << format(evaluate_expr.address()) << "\n"; + std::cout << "T: " << format(evaluate_expr.address().type()) << "\n"; +#endif + + auto may_alias = ::may_alias( + evaluate_expr.address(), update_state_expr.address(), address_taken, ns); + +#if 0 + if(may_alias.has_value()) + std::cout << "M: " << format(*may_alias) << "\n"; + else + std::cout << "M: ?\n"; +#endif + + if(may_alias.has_value()) + { + // 'simple' case + if(may_alias->is_true()) + { + // The object is known to be the same. + // (ς[A:=V])(A) --> V + return simplify_state_expr_node( + typecast_exprt::conditional_cast( + update_state_expr.new_value(), evaluate_expr.type()), + address_taken, + ns); + } + else if(may_alias->is_false()) + { + // The object is known to be different. + // (ς[❝x❞:=V])(❝y❞) --> ς(❝y❞) + // It might be possible to further simplify ς(❝y❞). + return simplify_state_expr_node( + evaluate_expr.with_state(update_state_expr.state()), address_taken, ns); + } + else + { + // The object may or may not be the same. + // (ς[A:=V])(B) --> if cond then V else ς(B) endif + auto simplified_cond = simplify_state_expr(*may_alias, address_taken, ns); + auto new_evaluate_expr = + evaluate_expr.with_state(update_state_expr.state()); + auto simplified_new_evaluate_expr = simplify_state_expr_node( + new_evaluate_expr, address_taken, ns); // rec. call + auto if_expr = if_exprt( + std::move(simplified_cond), + simplify_state_expr_node( + typecast_exprt::conditional_cast( + update_state_expr.new_value(), evaluate_expr.type()), + address_taken, + ns), + std::move(simplified_new_evaluate_expr)); + return simplify_expr(if_expr, ns); + } + } + + auto new_evaluate_expr = evaluate_expr.with_state(update_state_expr.state()); + auto simplified_new_evaluate_expr = + simplify_state_expr(new_evaluate_expr, address_taken, ns); // rec. call + + // Types are compatible? + if(types_are_compatible( + update_state_expr.new_value().type(), evaluate_expr.type())) + { + // Disregard case where the two memory regions overlap. + // + // (ς[w:=v])(r) --> + // IF same_object(w, r) ∧ offset(w) = offset(r) THEN + // v + // ELSE + // ς(r) + // ENDIF + auto same_object = + ::same_object(evaluate_expr.address(), update_state_expr.address()); + + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + + auto offset_w = simplify_state_expr( + pointer_offset(evaluate_expr.address()), address_taken, ns); + + auto offset_r = simplify_state_expr( + pointer_offset(update_state_expr.address()), address_taken, ns); + + auto same_offset = equal_exprt(offset_w, offset_r); + + auto same = and_exprt(simplified_same_object, same_offset); + + auto simplified_same = + simplify_state_expr(simplify_expr(same, ns), address_taken, ns); + + auto new_value = typecast_exprt::conditional_cast( + update_state_expr.new_value(), evaluate_expr.type()); + + auto if_expr = + if_exprt(simplified_same, new_value, simplified_new_evaluate_expr); + + return simplify_expr(if_expr, ns); + } + else + { + // Complex case. Types don't match. + return simplified_new_evaluate_expr; + +#if 0 + auto object = update_state_expr.new_value(); + + auto offset = simplify_state_expr_node( + pointer_offset(evaluate_expr.address()), address_taken, ns); + + auto byte_extract = make_byte_extract(object, offset, evaluate_expr.type()); + + return if_exprt( + std::move(simplified_same_object), + std::move(byte_extract), + std::move(simplified_new_evaluate_expr)); +#endif + } +} + +exprt simplify_allocate(allocate_exprt src) +{ + // A store does not affect the result. + // allocate(ς[A:=V]), size) --> allocate(ς, size) + if(src.state().id() == ID_update_state) + { + // rec. call + return simplify_allocate( + src.with_state(to_update_state_expr(src.state()).state())); + } + else if(src.state().id() == ID_enter_scope_state) + { + // rec. call + return simplify_allocate( + src.with_state(to_enter_scope_state_expr(src.state()).state())); + } + else if(src.state().id() == ID_exit_scope_state) + { + // rec. call + return simplify_allocate( + src.with_state(to_exit_scope_state_expr(src.state()).state())); + } + + return std::move(src); +} + +exprt simplify_evaluate_allocate_state( + evaluate_exprt evaluate_expr, + const namespacet &ns) +{ + PRECONDITION(evaluate_expr.state().id() == ID_allocate_state); + + // const auto &allocate_expr = to_allocate_expr(evaluate_expr.state()); + +#if 0 + // Same address? + if(evaluate_expr.address() == allocate_expr.address()) + { +# if 0 + irep_idt identifier = "allocate-" + std::to_string(++allocate_counter); + auto object_size = allocate_expr.size(); + auto object_type = array_typet(char_type(), object_size); + auto symbol_expr = symbol_exprt(identifier, object_type); + return object_address_exprt(symbol_expr); +# endif + return std::move(evaluate_expr); + } + else // different + { + auto new_evaluate_expr = evaluate_expr; + new_evaluate_expr.state() = allocate_expr.state(); + return std::move(new_evaluate_expr); + } +#endif + return std::move(evaluate_expr); +} + +exprt simplify_evaluate_deallocate_state( + evaluate_exprt evaluate_expr, + const namespacet &ns) +{ + PRECONDITION(evaluate_expr.state().id() == ID_deallocate_state); + + // deallocate isn't visible to 'evaluate' + const auto &deallocate_state_expr = + to_deallocate_state_expr(evaluate_expr.state()); + auto new_evaluate_expr = + evaluate_expr.with_state(deallocate_state_expr.state()); + return std::move(new_evaluate_expr); +} + +exprt simplify_evaluate_enter_scope_state( + evaluate_exprt evaluate_expr, + const namespacet &ns) +{ + PRECONDITION(evaluate_expr.state().id() == ID_enter_scope_state); + + const auto &enter_scope_state_expr = + to_enter_scope_state_expr(evaluate_expr.state()); + auto new_evaluate_expr = + evaluate_expr.with_state(enter_scope_state_expr.state()); + return std::move(new_evaluate_expr); +} + +exprt simplify_evaluate_exit_scope_state( + evaluate_exprt evaluate_expr, + const namespacet &ns) +{ + PRECONDITION(evaluate_expr.state().id() == ID_exit_scope_state); + + const auto &exit_scope_state_expr = + to_exit_scope_state_expr(evaluate_expr.state()); + auto new_evaluate_expr = + evaluate_expr.with_state(exit_scope_state_expr.state()); + return std::move(new_evaluate_expr); +} + +exprt simplify_object_expression_rec(exprt src) +{ + if(src.id() == ID_object_address) + return src; + else if(src.id() == ID_element_address) + return simplify_object_expression_rec(to_element_address_expr(src).base()); + else if(src.id() == ID_field_address) + return simplify_object_expression_rec(to_field_address_expr(src).base()); + else if(src.id() == ID_plus) + { + const auto &plus_expr = to_plus_expr(src); + for(auto &op : plus_expr.operands()) + if(op.type().id() == ID_pointer) + return simplify_object_expression_rec(op); + return src; // no change + } + else if(src.id() == ID_typecast) + { + const auto &op = to_typecast_expr(src).op(); + if(op.type().id() == ID_pointer) + return simplify_object_expression_rec(op); + else + return src; // no change + } + else + return src; +} + +exprt simplify_object_expression(exprt src) +{ + return simplify_object_expression_rec(src); +} + +exprt simplify_live_object_expr( + state_live_object_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + const auto &pointer = src.address(); + + auto object = simplify_object_expression(pointer); + + if(object.id() == ID_object_address) + { + auto identifier = to_object_address_expr(object).object_identifier(); + + if(has_prefix(id2string(identifier), "allocate-")) + { + } + else if(identifier == "return_value") + { + return true_exprt(); // never dies + } + else if(has_prefix(id2string(identifier), "va_args::")) + { + return true_exprt(); // might be 'dead' + } + else + { + const auto &symbol = ns.lookup(identifier); + if(symbol.is_static_lifetime) + { + return true_exprt(); // always live + } + } + } + + // A store does not affect the result. + // live_object(ς[A:=V]), p) --> live_object(ς, p) + if(src.state().id() == ID_update_state) + { + src.state() = to_update_state_expr(src.state()).state(); + + // rec. call + return simplify_live_object_expr(std::move(src), address_taken, ns); + } + else if(src.state().id() == ID_deallocate_state) + { + const auto &deallocate_state_expr = to_deallocate_state_expr(src.state()); + // live_object(deallocate_state(ς, p), q) --> + // IF same_object(p, q) THEN false ELSE live_object(ς, q) ENDIF + auto same_object = ::same_object(object, deallocate_state_expr.address()); + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + auto new_live_object_expr = simplify_live_object_expr( + src.with_state(deallocate_state_expr.state()), address_taken, ns); + return if_exprt( + simplified_same_object, false_exprt(), new_live_object_expr); + } + else if(src.state().id() == ID_enter_scope_state) + { + // This begins the life of a local-scoped variable. + const auto &enter_scope_state_expr = to_enter_scope_state_expr(src.state()); + if( + address_taken.find(enter_scope_state_expr.symbol()) != + address_taken.end()) + { + // live_object(enter_scope_state(ς, p), q) --> + // IF same_object(p, q) THEN true ELSE live_object(ς, q) ENDIF + auto same_object = + ::same_object(object, enter_scope_state_expr.address()); + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + auto new_live_object_expr = simplify_live_object_expr( // rec. call + src.with_state(enter_scope_state_expr.state()), + address_taken, + ns); + return if_exprt( + simplified_same_object, true_exprt(), new_live_object_expr); + } + else + { + return simplify_live_object_expr( // rec. call + src.with_state(enter_scope_state_expr.state()), + address_taken, + ns); + } + } + else if(src.state().id() == ID_exit_scope_state) + { + // This ends the life of a local-scoped variable. + const auto &exit_scope_state_expr = to_exit_scope_state_expr(src.state()); + if( + address_taken.find(exit_scope_state_expr.symbol()) != address_taken.end()) + { + // live_object(exit_scope_state(ς, p), q) --> + // IF same_object(p, q) THEN false ELSE live_object(ς, q) ENDIF + auto same_object = ::same_object(object, exit_scope_state_expr.address()); + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + auto new_live_object_expr = simplify_live_object_expr( // rec. call + src.with_state(exit_scope_state_expr.state()), + address_taken, + ns); + return if_exprt( + simplified_same_object, false_exprt(), new_live_object_expr); + } + else + { + return simplify_live_object_expr( // rec. call + src.with_state(exit_scope_state_expr.state()), + address_taken, + ns); + } + } + + return std::move(src); +} + +exprt simplify_writeable_object_expr( + state_writeable_object_exprt src, + const namespacet &ns) +{ + const auto &pointer = src.address(); + + auto object = simplify_object_expression(pointer); + + if(object.id() == ID_object_address) + { + auto identifier = to_object_address_expr(object).object_identifier(); + + if(has_prefix(id2string(identifier), "allocate-")) + { + } + else if(has_prefix(id2string(identifier), "va_args::")) + { + return true_exprt(); // always writeable + } + else + { + const auto &symbol = ns.lookup(identifier); + return make_boolean_expr(!symbol.type.get_bool(ID_C_constant)); + } + } + + // A store does not affect the result. + // writeable_object(ς[A:=V]), p) --> writeable_object(ς, p) + if(src.state().id() == ID_update_state) + { + src.state() = to_update_state_expr(src.state()).state(); + return std::move(src); + } + + return std::move(src); +} + +exprt simplify_is_dynamic_object_expr(state_is_dynamic_object_exprt src) +{ + const auto &pointer = src.address(); + + auto object = simplify_object_expression(pointer); + + if(object.id() == ID_object_address) + { + // these are not dynamic + return false_exprt(); + } + + // A store does not affect the result. + // is_dynamic_object(ς[A:=V]), p) --> is_dynamic_object(ς, p) + if(src.state().id() == ID_update_state) + { + src.state() = to_update_state_expr(src.state()).state(); + // rec. call + return simplify_is_dynamic_object_expr(std::move(src)); + } + else if(src.state().id() == ID_enter_scope_state) + { + return simplify_is_dynamic_object_expr( + src.with_state(to_enter_scope_state_expr(src.state()).state())); + } + else if(src.state().id() == ID_exit_scope_state) + { + return simplify_is_dynamic_object_expr( + src.with_state(to_exit_scope_state_expr(src.state()).state())); + } + + return std::move(src); +} + +exprt simplify_object_size_expr( + state_object_size_exprt src, + const namespacet &ns) +{ + const auto &pointer = src.address(); + + auto object = simplify_object_expression(pointer); + + if(object.id() == ID_object_address) + { + const auto &object_address_expr = to_object_address_expr(object); + auto size_opt = size_of_expr(object_address_expr.object_type(), ns); + if(size_opt.has_value()) + return typecast_exprt::conditional_cast(*size_opt, src.type()); + else + return std::move(src); // no change + } + + // A store does not affect the result. + // object_size(ς[A:=V]), p) --> object_size(ς, p) + if(src.state().id() == ID_update_state) + { + return src.with_state(to_update_state_expr(src.state()).state()); + } + + return std::move(src); +} + +exprt simplify_ok_expr( + state_ok_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + auto &state = src.state(); + auto &pointer = src.address(); + auto &size = src.size(); + + if(state.id() == ID_update_state) + { + // A store does not affect the result. + // X_ok(ς[A:=V]), A, S) --> X_ok(ς, A, S) + state = to_update_state_expr(state).state(); + + // rec. call + return simplify_state_expr_node(std::move(src), address_taken, ns); + } + else if(state.id() == ID_enter_scope_state) + { + const auto &enter_scope_state_expr = to_enter_scope_state_expr(state); + + // rec. call + auto rec_result = simplify_state_expr_node( + src.with_state(enter_scope_state_expr.state()), address_taken, ns); + +#if 0 + // replace array by array[0] + auto enter_scope_address = + enter_scope_state_expr.object_type().id() == ID_array ? + element_address_exprt(enter_scope_state_expr.address(), from_integer(0, to_array_type(enter_scope_state_expr.object_type()).index_type())) : + enter_scope_state_expr.address(); + + auto may_alias = + ::may_alias(enter_scope_address, pointer, address_taken, ns); + + if(may_alias.has_value() && *may_alias == false_exprt()) + return rec_result; + + auto same_object = ::same_object(pointer, enter_scope_state_expr.address()); +#else + auto same_object = may_be_same_object( + pointer, enter_scope_state_expr.address(), address_taken, ns); +#endif + + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + + // Known to be live, only need to check upper bound. + // Extend one bit, to cover overflow case. + auto size_type = ::size_type(); + auto size_type_ext = unsignedbv_typet(size_type.get_width() + 1); + auto offset = pointer_offset_exprt(pointer, size_type_ext); + auto size_casted = typecast_exprt(size, size_type_ext); + auto object_size_opt = + size_of_expr(enter_scope_state_expr.object_type(), ns); + if(!object_size_opt.has_value()) + return std::move(src); + + auto upper = binary_relation_exprt( + plus_exprt(offset, size_casted), + ID_le, + typecast_exprt(*object_size_opt, size_type_ext)); + + auto simplified_upper = simplify_state_expr(upper, address_taken, ns); + + auto implication = + if_exprt(simplified_same_object, simplified_upper, rec_result); + + return std::move(implication); + } + else if(state.id() == ID_exit_scope_state) + { +#if 0 + const auto &exit_scope_state_expr = to_exit_scope_state_expr(state); + + // rec. call + auto rec_result = simplify_state_expr_node( + src.with_state(exit_scope_state_expr.state()), address_taken, ns); + + auto same_object = ::same_object(pointer, exit_scope_state_expr.address()); + + auto simplified_same_object = + simplify_state_expr(same_object, address_taken, ns); + + // Known to be dead if it's the same object. + auto implication = + if_exprt(simplified_same_object, false_exprt(), rec_result); + + return implication; +#else + return simplify_state_expr_node( + src.with_state(to_exit_scope_state_expr(state).state()), + address_taken, + ns); +#endif + } + + return std::move(src); +} + +#if 0 +static bool is_one(const exprt &src) +{ + if(src.id() == ID_typecast) + return is_one(to_typecast_expr(src).op()); + else if(src.id() == ID_constant) + { + auto value_opt = numeric_cast(src); + return value_opt.has_value() && *value_opt == 1; + } + else + return false; +} +#endif + +static bool is_a_char_type(const typet &type) +{ + return (type.id() == ID_signedbv || type.id() == ID_unsignedbv) && + to_bitvector_type(type).get_width() == char_type().get_width(); +} + +static bool is_zero_char(const exprt &src, const namespacet &ns) +{ + if(!is_a_char_type(src.type())) + return false; + + auto src_simplified = simplify_expr(src, ns); + + auto integer_opt = numeric_cast(src); + + return integer_opt.has_value() && *integer_opt == 0; +} + +exprt simplify_is_cstring_expr( + state_is_cstring_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + PRECONDITION(src.type().id() == ID_bool); + const auto &state = src.state(); + const auto &pointer = src.address(); + + if(state.id() == ID_update_state) + { + const auto &update_state_expr = to_update_state_expr(state); + + auto cstring_in_old_state = src; + cstring_in_old_state.op0() = update_state_expr.state(); + auto simplified_cstring_in_old_state = + simplify_state_expr_node(cstring_in_old_state, address_taken, ns); + + auto may_alias = + ::may_alias(pointer, update_state_expr.address(), address_taken, ns); + + if(may_alias.has_value() && may_alias->is_false()) + { + // different objects + // cstring(s[x:=v], p) --> cstring(s, p) + return simplified_cstring_in_old_state; + } + + // maybe the same + + // Are we writing zero? + if(update_state_expr.new_value().is_zero()) + { + // cstring(s[p:=0], q) --> if p alias q then true else cstring(s, q) + auto same_object = ::same_object(pointer, update_state_expr.address()); + + auto simplified_same_object = + simplify_expr(simplify_state_expr(same_object, address_taken, ns), ns); + + return if_exprt( + simplified_same_object, true_exprt(), simplified_cstring_in_old_state); + } + } + else if(state.id() == ID_enter_scope_state) + { + // rec. call + return simplify_is_cstring_expr( + src.with_state(to_enter_scope_state_expr(state).state()), + address_taken, + ns); + } + else if(state.id() == ID_exit_scope_state) + { + // rec. call + return simplify_is_cstring_expr( + src.with_state(to_exit_scope_state_expr(state).state()), + address_taken, + ns); + } + + if(pointer.id() == ID_plus) + { +#if 0 + auto &plus_expr = to_plus_expr(pointer); + if(plus_expr.operands().size() == 2 && is_one(plus_expr.op1())) + { + // is_cstring(ς, p + 1)) --> is_cstring(ς, p) ∨ ς(p)=0 + auto new_is_cstring = src; + new_is_cstring.op1() = plus_expr.op0(); + auto type = to_pointer_type(pointer.type()).base_type(); + auto zero = from_integer(0, type); + auto is_zero = + equal_exprt(evaluate_exprt(state, plus_expr.op0(), type), zero); + return or_exprt(new_is_cstring, is_zero); + } +#endif + } + else if( + pointer.id() == ID_address_of && + to_address_of_expr(pointer).object().id() == ID_string_constant) + { + // is_cstring(ς, &"...")) --> true + return true_exprt(); + } + else if( + pointer.id() == ID_element_address && + to_element_address_expr(pointer).base().id() == ID_address_of && + to_address_of_expr(to_element_address_expr(pointer).base()).object().id() == + ID_string_constant) + { + // TODO: compare offset to length + // is_cstring(ς, element_address(&"...", 0))) --> true + return true_exprt(); + } + + return std::move(src); +} + +exprt simplify_cstrlen_expr( + state_cstrlen_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + const auto &state = src.state(); + const auto &pointer = src.address(); + + if(state.id() == ID_update_state) + { + const auto &update_state_expr = to_update_state_expr(state); + + auto cstrlen_in_old_state = src.with_state(update_state_expr.state()); + auto simplified_cstrlen_in_old_state = + simplify_state_expr_node(cstrlen_in_old_state, address_taken, ns); + + auto may_be_same_object = ::may_be_same_object( + pointer, update_state_expr.address(), address_taken, ns); + + if(may_be_same_object.is_false()) + { + // different objects + // cstrlen(s[x:=v], p) --> cstrlen(s, p) + return simplified_cstrlen_in_old_state; + } + + // maybe the same + + // Are we writing zero? + if(is_zero_char(update_state_expr.new_value(), ns)) + { + // cstrlen(s[p:=0], p) --> 0 + if(pointer == update_state_expr.address()) + return from_integer(0, src.type()); + } + } + + if(pointer.id() == ID_plus) + { +#if 0 + auto &plus_expr = to_plus_expr(pointer); + if(plus_expr.operands().size() == 2 && is_one(plus_expr.op1())) + { + // is_cstring(ς, p + 1)) --> is_cstring(ς, p) ∨ ς(p)=0 + auto new_is_cstring = src; + new_is_cstring.op1() = plus_expr.op0(); + auto type = to_pointer_type(pointer.type()).base_type(); + auto zero = from_integer(0, type); + auto is_zero = + equal_exprt(evaluate_exprt(state, plus_expr.op0(), type), zero); + return or_exprt(new_is_cstring, is_zero); + } +#endif + } + else if( + pointer.id() == ID_address_of && + to_address_of_expr(pointer).object().id() == ID_string_constant) + { +#if 0 + // is_cstring(ς, &"...")) --> true + return true_exprt(); +#endif + } + else if( + pointer.id() == ID_element_address && + to_element_address_expr(pointer).base().id() == ID_address_of && + to_address_of_expr(to_element_address_expr(pointer).base()).object().id() == + ID_string_constant) + { +#if 0 + // TODO: compare offset to length + // is_cstring(ς, element_address(&"...", 0))) --> true + return true_exprt(); +#endif + } + + return std::move(src); +} + +exprt simplify_is_sentinel_dll_expr( + state_is_sentinel_dll_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + PRECONDITION(src.type().id() == ID_bool); + const auto &state = src.state(); + const auto &head = src.head(); + const auto &tail = src.tail(); + + { + // ς(h.❝n❞) = t ∧ ς(t.❝p❞) = h --> is_sentinel_dll(ς, h, t) + auto head_next = sentinel_dll_next(state, head, ns); + auto tail_prev = sentinel_dll_prev(state, tail, ns); + + if(head_next.has_value() && tail_prev.has_value()) + { + // rec. calls + auto head_next_simplified = + simplify_state_expr(*head_next, address_taken, ns); + auto tail_prev_simplified = + simplify_state_expr(*tail_prev, address_taken, ns); + if(head_next_simplified == tail && tail_prev_simplified == head) + return true_exprt(); + } + } + + if(state.id() == ID_update_state) + { + const auto &update_state_expr = to_update_state_expr(state); + + // are we writing to something that might be a node pointer? + const auto &update_type = update_state_expr.new_value().type(); + if(update_type != src.head().type()) + { + // update is irrelevant, drop update + auto without_update = src.with_state(update_state_expr.state()); + return simplify_is_sentinel_dll_expr(without_update, address_taken, ns); + } + } + else if(state.id() == ID_enter_scope_state) + { + return simplify_is_sentinel_dll_expr( + src.with_state(to_enter_scope_state_expr(state).state()), + address_taken, + ns); + } + else if(state.id() == ID_exit_scope_state) + { + return simplify_is_sentinel_dll_expr( + src.with_state(to_exit_scope_state_expr(state).state()), + address_taken, + ns); + } + + return std::move(src); +} + +exprt simplify_pointer_offset_expr( + pointer_offset_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + if(src.pointer().id() == ID_object_address) + { + // pointer_offset(❝x❞) -> 0 + return from_integer(0, src.type()); + } + else if(src.pointer().id() == ID_element_address) + { + const auto &element_address_expr = to_element_address_expr(src.pointer()); + // pointer_offset(element_address(Z, y)) --> + // pointer_offset(Z) + y*sizeof(x) + auto size_opt = size_of_expr(element_address_expr.element_type(), ns); + if(size_opt.has_value()) + { + auto product = mult_exprt( + typecast_exprt::conditional_cast( + element_address_expr.index(), src.type()), + typecast_exprt::conditional_cast(*size_opt, src.type())); + auto pointer_offset = simplify_pointer_offset_expr( + pointer_offset_exprt(element_address_expr.base(), src.type()), + address_taken, + ns); + auto sum = plus_exprt(pointer_offset, std::move(product)); + return std::move(sum); + } + } + else if(src.pointer().id() == ID_address_of) + { + const auto &address_of_expr = to_address_of_expr(src.pointer()); + if(address_of_expr.object().id() == ID_string_constant) + return from_integer(0, src.type()); + } + else if(src.pointer().id() == ID_typecast) + { + if(to_typecast_expr(src.pointer()).op().type().id() == ID_pointer) + { + // remove the typecast + return simplify_pointer_offset_expr( + pointer_offset_exprt(to_typecast_expr(src.pointer()).op(), src.type()), + address_taken, + ns); + } + } + + return std::move(src); +} + +exprt simplify_pointer_object_expr( + pointer_object_exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + auto simplified_pointer = simplify_object_expression_rec(src.pointer()); + + if(src.pointer() != simplified_pointer) + return pointer_object_exprt(simplified_pointer, src.type()); + + return std::move(src); +} + +exprt simplify_state_expr_node( + exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + if(src.id() == ID_allocate) + { + return simplify_allocate(to_allocate_expr(src)); + } + else if(src.id() == ID_evaluate) + { + auto &evaluate_expr = to_evaluate_expr(src); + + if(evaluate_expr.state().id() == ID_update_state) + { + return simplify_evaluate_update(evaluate_expr, address_taken, ns); + } + else if(evaluate_expr.state().id() == ID_allocate_state) + { + return simplify_evaluate_allocate_state(evaluate_expr, ns); + } + else if(evaluate_expr.state().id() == ID_deallocate_state) + { + return simplify_evaluate_deallocate_state(evaluate_expr, ns); + } + else if(evaluate_expr.state().id() == ID_enter_scope_state) + { + return simplify_evaluate_enter_scope_state(evaluate_expr, ns); + } + else if(evaluate_expr.state().id() == ID_exit_scope_state) + { + return simplify_evaluate_exit_scope_state(evaluate_expr, ns); + } + } + else if( + src.id() == ID_state_r_ok || src.id() == ID_state_w_ok || + src.id() == ID_state_rw_ok) + { + return simplify_ok_expr(to_state_ok_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_live_object) + { + return simplify_live_object_expr( + to_state_live_object_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_writeable_object) + { + return simplify_writeable_object_expr( + to_state_writeable_object_expr(src), ns); + } + else if(src.id() == ID_state_is_cstring) + { + return simplify_is_cstring_expr( + to_state_is_cstring_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_cstrlen) + { + return simplify_cstrlen_expr(to_state_cstrlen_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_is_sentinel_dll) + { + return simplify_is_sentinel_dll_expr( + to_state_is_sentinel_dll_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_is_dynamic_object) + { + return simplify_is_dynamic_object_expr( + to_state_is_dynamic_object_expr(src)); + } + else if(src.id() == ID_plus) + { + auto &plus_expr = to_plus_expr(src); + if( + src.type().id() == ID_pointer && + plus_expr.op0().id() == ID_element_address) + { + // element_address(X, Y) + Z --> element_address(X, Y + Z) + auto new_element_address_expr = to_element_address_expr(plus_expr.op0()); + new_element_address_expr.index() = plus_exprt( + new_element_address_expr.index(), + typecast_exprt::conditional_cast( + plus_expr.op1(), new_element_address_expr.index().type())); + new_element_address_expr.index() = + simplify_expr(new_element_address_expr.index(), ns); + return simplify_state_expr_node( + new_element_address_expr, address_taken, ns); + } + } + else if(src.id() == ID_pointer_offset) + { + return simplify_pointer_offset_expr( + to_pointer_offset_expr(src), address_taken, ns); + } + else if(src.id() == ID_pointer_object) + { + return simplify_pointer_object_expr( + to_pointer_object_expr(src), address_taken, ns); + } + else if(src.id() == ID_state_object_size) + { + return simplify_object_size_expr(to_state_object_size_expr(src), ns); + } + else if(src.id() == ID_equal) + { + const auto &equal_expr = to_equal_expr(src); + if( + equal_expr.lhs().id() == ID_pointer_object && + equal_expr.rhs().id() == ID_pointer_object) + { + const auto &lhs_p = to_pointer_object_expr(equal_expr.lhs()).pointer(); + const auto &rhs_p = to_pointer_object_expr(equal_expr.rhs()).pointer(); + if(lhs_p.id() == ID_object_address && rhs_p.id() == ID_object_address) + { + if( + to_object_address_expr(lhs_p).object_identifier() == + to_object_address_expr(rhs_p).object_identifier()) + return true_exprt(); + else + return false_exprt(); + } + } + } + + return src; +} + +exprt simplify_state_expr( + exprt src, + const std::unordered_set &address_taken, + const namespacet &ns) +{ + // operands first, recursively + for(auto &op : src.operands()) + op = simplify_state_expr(op, address_taken, ns); + + // now the node itself + src = simplify_state_expr_node(src, address_taken, ns); + + return src; +} diff --git a/src/cprover/simplify_state_expr.h b/src/cprover/simplify_state_expr.h new file mode 100644 index 00000000000..e1071ce53e1 --- /dev/null +++ b/src/cprover/simplify_state_expr.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Simplify State Expression + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Simplify State Expression + +#ifndef CPROVER_CPROVER_SIMPLIFY_STATE_EXPR_H +#define CPROVER_CPROVER_SIMPLIFY_STATE_EXPR_H + +#include +#include + +#include + +exprt simplify_state_expr( + exprt, + const std::unordered_set &address_taken, + const namespacet &); + +exprt simplify_state_expr_node( + exprt, + const std::unordered_set &address_taken, + const namespacet &); + +#endif // CPROVER_CPROVER_PROPAGATE_H diff --git a/src/cprover/solver.cpp b/src/cprover/solver.cpp new file mode 100644 index 00000000000..5162fe1c18f --- /dev/null +++ b/src/cprover/solver.cpp @@ -0,0 +1,461 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Solver + +#include "solver.h" + +#include +#include +#include + +#include + +#include "address_taken.h" +#include "axioms.h" +#include "bv_pointers_wide.h" +#include "console.h" +#include "counterexample_found.h" +#include "free_symbols.h" +#include "propagate.h" +#include "report_properties.h" +#include "report_traces.h" +#include "solver_types.h" +#include "state.h" + +#include +#include +#include +#include + +frame_mapt build_frame_map(const std::vector &frames) +{ + frame_mapt frame_map; + + for(std::size_t i = 0; i < frames.size(); i++) + frame_map[frames[i].symbol] = frame_reft(i); + + return frame_map; +} + +void framet::add_invariant(exprt invariant) +{ + if(invariant.id() == ID_and) + { + for(const auto &conjunct : to_and_expr(invariant).operands()) + add_invariant(conjunct); + } + else + invariants.push_back(std::move(invariant)); +} + +void framet::add_auxiliary(exprt invariant) +{ + if(invariant.id() == ID_and) + { + for(const auto &conjunct : to_and_expr(invariant).operands()) + add_auxiliary(conjunct); + } + else + auxiliaries.push_back(std::move(invariant)); +} + +std::vector setup_frames(const std::vector &constraints) +{ + std::set states_set; + std::vector frames; + + for(auto &c : constraints) + { + auto &location = c.source_location(); + free_symbols(c, [&states_set, &location, &frames](const symbol_exprt &s) { + auto insert_result = states_set.insert(s); + if(insert_result.second) + frames.emplace_back(s, location, frame_reft(frames.size())); + }); + } + + return frames; +} + +void find_implications( + const std::vector &constraints, + std::vector &frames) +{ + const auto frame_map = build_frame_map(frames); + + for(const auto &c : constraints) + { + // look for ∀ ς : state . Sxx(ς) ⇒ Syy(...) + // and ∀ ς : state . Sxx(ς) ⇒ ... + if(c.id() == ID_forall && to_forall_expr(c).where().id() == ID_implies) + { + auto &implication = to_implies_expr(to_forall_expr(c).where()); + + if( + implication.rhs().id() == ID_function_application && + to_function_application_expr(implication.rhs()).function().id() == + ID_symbol) + { + auto &rhs_symbol = to_symbol_expr( + to_function_application_expr(implication.rhs()).function()); + auto s_it = frame_map.find(rhs_symbol); + if(s_it != frame_map.end()) + { + frames[s_it->second.index].implications.emplace_back( + implication.lhs(), to_function_application_expr(implication.rhs())); + } + } + } + } +} + +frame_reft +find_frame(const frame_mapt &frame_map, const symbol_exprt &frame_symbol) +{ + auto entry = frame_map.find(frame_symbol); + + if(entry == frame_map.end()) + PRECONDITION(false); + + return entry->second; +} + +std::vector find_properties( + const std::vector &constraints, + const std::vector &frames) +{ + const auto frame_map = build_frame_map(frames); + std::vector properties; + + for(const auto &c : constraints) + { + // look for ∀ ς : state . Sxx(ς) ⇒ ... + if(c.id() == ID_forall && to_forall_expr(c).where().id() == ID_implies) + { + auto &implication = to_implies_expr(to_forall_expr(c).where()); + + if( + implication.rhs().id() != ID_function_application && + implication.lhs().id() == ID_function_application && + to_function_application_expr(implication.lhs()).function().id() == + ID_symbol) + { + auto &lhs_symbol = to_symbol_expr( + to_function_application_expr(implication.lhs()).function()); + auto lhs_frame = find_frame(frame_map, lhs_symbol); + properties.emplace_back( + c.source_location(), lhs_frame, implication.rhs()); + } + } + } + + return properties; +} + +exprt property_predicate(const implies_exprt &src) +{ + // Sxx(ς) ⇒ p(ς) + return src.rhs(); +} + +void dump( + const std::vector &frames, + const propertyt &property, + bool values, + bool implications) +{ + for(const auto &f : frames) + { + std::cout << "FRAME: " << format(f.symbol) << '\n'; + + if(implications) + { + for(auto &c : f.implications) + { + std::cout << " implication: "; + std::cout << format(c.lhs) << " -> " << format(c.rhs); + std::cout << '\n'; + } + } + + if(values) + { + for(auto &i : f.invariants) + std::cout << " invariant: " << format(i) << '\n'; + + for(auto &i : f.auxiliaries) + std::cout << " auxiliary: " << format(i) << '\n'; + } + + if(property.frame == f.ref) + std::cout << " property: " << format(property.condition) << '\n'; + } +} + +bool is_subsumed( + const std::vector &a1, + const std::vector &a2, + const exprt &b, + const std::unordered_set &address_taken, + bool verbose, + const namespacet &ns) +{ + if(b.is_true()) + return true; // anything subsumes 'true' + + for(auto &a_conjunct : a1) + if(a_conjunct.is_false()) + return true; // 'false' subsumes anything + + for(auto &a_conjunct : a1) + if(a_conjunct == b) + return true; // b is subsumed by a conjunct in a + + cout_message_handlert message_handler; + message_handler.set_verbosity(verbose ? 10 : 1); + satcheckt satcheck(message_handler); + bv_pointers_widet solver(ns, satcheck, message_handler); + axiomst axioms(solver, address_taken, verbose, ns); + + // check if a => b is valid, + // or (!a || b) is valid, + // or (a && !b) is unsat + for(auto &a_conjunct : a1) + axioms << a_conjunct; + + for(auto &a_conjunct : a2) + axioms << a_conjunct; + + axioms.set_to_false(b); + + // instantiate our axioms + axioms.emit(); + + // now run solver + switch(solver()) + { + case decision_proceduret::resultt::D_SATISFIABLE: + if(verbose) + show_assignment(solver); + return false; + case decision_proceduret::resultt::D_UNSATISFIABLE: + return true; + case decision_proceduret::resultt::D_ERROR: + throw "error reported by solver"; + } + + UNREACHABLE; // to silence a warning +} + +std::size_t count_frame(const workt::patht &path, frame_reft f) +{ + return std::count_if(path.begin(), path.end(), [f](const frame_reft &frame) { + return f == frame; + }); +} + +class take_time_resourcet +{ +public: + explicit take_time_resourcet( + std::chrono::time_point &_dest) + : dest(_dest) + { + } + + ~take_time_resourcet() + { + dest = std::chrono::steady_clock::now(); + } + +protected: + std::chrono::time_point &dest; +}; + +void solver( + std::vector &frames, + const std::unordered_set &address_taken, + const solver_optionst &solver_options, + const namespacet &ns, + std::vector &properties, + std::size_t property_index) +{ + const auto frame_map = build_frame_map(frames); + auto &property = properties[property_index]; + + property.start = std::chrono::steady_clock::now(); + take_time_resourcet stop_time(property.stop); + + if(solver_options.verbose) + std::cout << "\nDoing " << format(property.condition) << '\n'; + + for(auto &frame : frames) + frame.reset(); + + // add properties proven so far as auxiliaries + for(std::size_t i = 0; i < property_index; i++) + { + const auto &p = properties[i]; + if(p.status == propertyt::PASS) + frames[p.frame.index].add_auxiliary(p.condition); + } + + std::vector queue; + std::vector dropped; + + auto propagator = + [&frames, + &frame_map, + &queue, + &dropped, + &address_taken, + &solver_options, + &ns]( + const symbol_exprt &symbol, exprt invariant, const workt::patht &path) { + auto frame_ref = find_frame(frame_map, symbol); + auto &f = frames[frame_ref.index]; + + if(solver_options.verbose) + { + std::cout << "F: " << format(symbol) << " <- " << format(invariant) + << '\n'; + } + + // check if already subsumed + if(is_subsumed( + f.invariants, + f.auxiliaries, + invariant, + address_taken, + solver_options.verbose, + ns)) + { + if(solver_options.verbose) + std::cout << "*** SUBSUMED\n"; + } + else if(count_frame(path, frame_ref) > solver_options.loop_limit) + { + // loop limit exceeded, drop it + if(solver_options.verbose) + std::cout << "*** DROPPED\n"; + dropped.push_back(workt(frame_ref, invariant, path)); + } + else + { + auto new_path = path; + new_path.push_back(frame_ref); + queue.emplace_back(f.ref, std::move(invariant), std::move(new_path)); + } + }; + + // we start with I = P + queue.emplace_back( + property.frame, property.condition, workt::patht{property.frame}); + + while(!queue.empty()) + { + auto work = queue.back(); + queue.pop_back(); + + frames[work.frame.index].add_invariant(work.invariant); + + if(solver_options.verbose) + { + dump(frames, property, true, true); + std::cout << '\n'; + } + + auto counterexample_found = ::counterexample_found( + frames, work, address_taken, solver_options.verbose, ns); + + if(counterexample_found) + { + property.status = propertyt::REFUTED; + property.trace = counterexample_found.value(); + return; + } + + propagate( + frames, work, address_taken, solver_options.verbose, ns, propagator); + } + + // did we drop anything? + if(dropped.empty()) + property.status = propertyt::PASS; + else + property.status = propertyt::DROPPED; +} + +void solver_progress(size_t i, size_t n, bool verbose) +{ + if(verbose) + { + } + else + { + if(i == n) + { + if(consolet::is_terminal()) + std::cout << "\x1b[1A\x1b[0K"; // clear the line + } + else + { + if(i != 0 && consolet::is_terminal()) + std::cout << "\x1b[1A"; + + std::cout << consolet::orange << "Doing property " << (i + 1) << '/' << n + << consolet::reset << '\n'; + } + } +} + +solver_resultt solver( + const std::vector &constraints, + const solver_optionst &solver_options, + const namespacet &ns) +{ + const auto address_taken = ::address_taken(constraints); + + if(solver_options.verbose) + { + for(auto &a : address_taken) + std::cout << "address_taken: " << format(a) << '\n'; + } + + auto frames = setup_frames(constraints); + + find_implications(constraints, frames); + + auto properties = find_properties(constraints, frames); + + if(properties.empty()) + { + std::cout << "\nThere are no properties to show.\n"; + return solver_resultt::ALL_PASS; + } + + // solve each property separately, in order of occurence + for(std::size_t i = 0; i < properties.size(); i++) + { + solver_progress(i, properties.size(), solver_options.verbose); + solver(frames, address_taken, solver_options, ns, properties, i); + } + + solver_progress(properties.size(), properties.size(), solver_options.verbose); + + // reporting + report_properties(properties); + + if(solver_options.trace) + report_traces(frames, properties, solver_options.verbose, ns); + + // overall outcome + return overall_outcome(properties); +} diff --git a/src/cprover/solver.h b/src/cprover/solver.h new file mode 100644 index 00000000000..707987aa288 --- /dev/null +++ b/src/cprover/solver.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Equality Propagation + +#ifndef CPROVER_CPROVER_SOLVER_H +#define CPROVER_CPROVER_SOLVER_H + +#include + +enum class solver_resultt +{ + ALL_PASS, + SOME_FAIL, + ERROR +}; + +class solver_optionst +{ +public: + bool trace; + bool verbose; + std::size_t loop_limit; +}; + +solver_resultt +solver(const std::vector &, const solver_optionst &, const namespacet &); + +#endif // CPROVER_CPROVER_SOLVER_H diff --git a/src/cprover/solver_types.h b/src/cprover/solver_types.h new file mode 100644 index 00000000000..1f882ee1e25 --- /dev/null +++ b/src/cprover/solver_types.h @@ -0,0 +1,159 @@ +/*******************************************************************\ + +Module: Solver + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Solver + +#ifndef CPROVER_CPROVER_SOLVER_TYPES_H +#define CPROVER_CPROVER_SOLVER_TYPES_H + +#include +#include + +#include +#include + +class frame_reft +{ +public: + frame_reft() : index(0) + { + } + explicit frame_reft(std::size_t __index) : index(__index) + { + } + std::size_t index; + friend bool operator==(const frame_reft &a, const frame_reft &b) + { + return a.index == b.index; + } +}; + +using frame_mapt = std::unordered_map; + +class framet +{ +public: + framet( + symbol_exprt __symbol, + source_locationt __source_location, + frame_reft __ref) + : symbol(std::move(__symbol)), + source_location(std::move(__source_location)), + ref(std::move(__ref)) + { + } + + symbol_exprt symbol; + + // our current hypothesis invariant + std::vector invariants; + + // auxiliary facts + std::vector auxiliaries; + + // formulas where this frame is on the rhs of ⇒ + struct implicationt + { + implicationt(exprt __lhs, function_application_exprt __rhs) + : lhs(std::move(__lhs)), rhs(std::move(__rhs)) + { + } + exprt lhs; + function_application_exprt rhs; + + implies_exprt as_expr() const + { + return implies_exprt(lhs, rhs); + } + }; + + std::vector implications; + + // tracking source code origin + source_locationt source_location = source_locationt::nil(); + + void add_invariant(exprt); + void add_auxiliary(exprt); + + void reset() + { + invariants.clear(); + auxiliaries.clear(); + } + + frame_reft ref; +}; + +class propertyt +{ +public: + propertyt( + source_locationt __source_location, + frame_reft __frame, + exprt __condition) + : source_location(std::move(__source_location)), + frame(std::move(__frame)), + condition(std::move(__condition)) + { + } + + irep_idt property_id() const + { + return source_location.get_property_id(); + } + + source_locationt source_location; + frame_reft frame; + exprt condition; + + using statust = enum { UNKNOWN, PASS, REFUTED, ERROR, DROPPED }; + statust status = UNKNOWN; + std::chrono::time_point start, stop; + + // trace information when REFUTED + struct trace_updatet + { + trace_updatet(exprt __address, exprt __value) + : address(std::move(__address)), value(std::move(__value)) + { + } + exprt address, value; + }; + + struct trace_statet + { + frame_reft frame; + std::vector updates; + }; + + using tracet = std::vector; + tracet trace; +}; + +struct workt +{ + using patht = std::vector; + + workt(frame_reft __frame, exprt __invariant, patht __path) + : frame(std::move(__frame)), + invariant(std::move(__invariant)), + path(std::move(__path)) + { + } + + // The frame where the invariant is to be established. + frame_reft frame; + exprt invariant; + + patht path; + + std::size_t nondet_counter = 0; +}; + +#endif // CPROVER_CPROVER_SOLVER_TYPES_H diff --git a/src/cprover/state.cpp b/src/cprover/state.cpp new file mode 100644 index 00000000000..b1690af04c3 --- /dev/null +++ b/src/cprover/state.cpp @@ -0,0 +1,11 @@ +/*******************************************************************\ + +Module: State Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "state.h" + +#include diff --git a/src/cprover/state.h b/src/cprover/state.h new file mode 100644 index 00000000000..81bb693a7ff --- /dev/null +++ b/src/cprover/state.h @@ -0,0 +1,1137 @@ +/*******************************************************************\ + +Module: State Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_STATE_H +#define CPROVER_CPROVER_STATE_H + +#include +#include + +class state_typet : public typet +{ +public: + state_typet() : typet(ID_state) + { + } +}; + +static inline mathematical_function_typet state_predicate_type() +{ + return mathematical_function_typet({state_typet()}, bool_typet()); +} + +static inline symbol_exprt state_expr() +{ + return symbol_exprt(u8"\u03c2", state_typet()); +} + +class initial_state_exprt : public unary_predicate_exprt +{ +public: + explicit initial_state_exprt(exprt state) + : unary_predicate_exprt(ID_initial_state, std::move(state)) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op(); + } + + exprt &state() + { + return op(); + } +}; + +/// \brief Cast an exprt to a \ref initial_state_exprt +/// +/// \a expr must be known to be \ref initial_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref initial_state_exprt +inline const initial_state_exprt &to_initial_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_initial_state); + const initial_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_initial_state_expr(const exprt &) +inline initial_state_exprt &to_initial_state_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_initial_state); + initial_state_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class evaluate_exprt : public binary_exprt +{ +public: + evaluate_exprt(exprt state, exprt address, typet type) + : binary_exprt( + std::move(state), + ID_evaluate, + std::move(address), + std::move(type)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + // convenience constructor + evaluate_exprt(exprt state, exprt address) + : evaluate_exprt( + std::move(state), + address, + to_pointer_type(address.type()).base_type()) + { + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + evaluate_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref evaluate_exprt +/// +/// \a expr must be known to be \ref evaluate_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref evaluate_exprt +inline const evaluate_exprt &to_evaluate_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_evaluate); + const evaluate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_evaluate_expr(const exprt &) +inline evaluate_exprt &to_evaluate_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_evaluate); + evaluate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class update_state_exprt : public ternary_exprt +{ +public: + update_state_exprt(exprt state, exprt address, exprt new_value) + : ternary_exprt( + ID_update_state, + std::move(state), + std::move(address), + std::move(new_value), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + PRECONDITION( + to_pointer_type(this->address().type()).base_type() == + this->new_value().type()); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + const exprt &new_value() const + { + return op2(); + } + + // helper + update_state_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref update_state_exprt +/// +/// \a expr must be known to be \ref update_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref update_state_exprt +inline const update_state_exprt &to_update_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_update_state); + const update_state_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_update_state_expr(const exprt &) +inline update_state_exprt &to_update_state_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_update_state); + update_state_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class allocate_exprt : public binary_exprt +{ +public: + allocate_exprt(exprt state, exprt size, pointer_typet type) + : binary_exprt( + std::move(state), + ID_allocate, + std::move(size), + std::move(type)) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &size() const + { + return op1(); + } + + // helper + allocate_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref allocate_exprt +/// +/// \a expr must be known to be \ref allocate_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref allocate_exprt +inline const allocate_exprt &to_allocate_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_allocate); + const allocate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class allocate_state_exprt : public binary_exprt +{ +public: + allocate_state_exprt(exprt state, exprt size) + : binary_exprt( + std::move(state), + ID_allocate_state, + std::move(size), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &size() const + { + return op1(); + } +}; + +/// \brief Cast an exprt to a \ref allocate_state_exprt +/// +/// \a expr must be known to be \ref allocate_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref allocate_state_exprt +inline const allocate_state_exprt &to_allocate_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_allocate_state); + const allocate_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class reallocate_exprt : public ternary_exprt +{ +public: + reallocate_exprt(exprt state, exprt address, exprt size, pointer_typet type) + : ternary_exprt( + ID_reallocate, + std::move(state), + std::move(address), + std::move(size), + std::move(type)) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + const exprt &size() const + { + return op2(); + } +}; + +/// \brief Cast an exprt to a \ref reallocate_exprt +/// +/// \a expr must be known to be \ref reallocate_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref allocate_exprt +inline const reallocate_exprt &to_reallocate_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_reallocate); + const reallocate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class reallocate_state_exprt : public ternary_exprt +{ +public: + reallocate_state_exprt(exprt state, exprt address, exprt size) + : ternary_exprt( + ID_allocate_state, + std::move(state), + std::move(address), + std::move(size), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + const exprt &size() const + { + return op1(); + } +}; + +/// \brief Cast an exprt to a \ref reallocate_state_exprt +/// +/// \a expr must be known to be \ref reallocate_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref reallocate_state_exprt +inline const reallocate_state_exprt &to_reallocate_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_reallocate_state); + const reallocate_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class deallocate_state_exprt : public binary_exprt +{ +public: + deallocate_state_exprt(exprt state, exprt address) + : binary_exprt( + std::move(state), + ID_deallocate_state, + std::move(address), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } +}; + +/// \brief Cast an exprt to a \ref deallocate_state_exprt +/// +/// \a expr must be known to be \ref deallocate_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref deallocate_state_exprt +inline const deallocate_state_exprt &to_deallocate_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_deallocate_state); + const deallocate_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_live_object_exprt : public binary_predicate_exprt +{ +public: + state_live_object_exprt(exprt state, exprt address) + : binary_predicate_exprt( + std::move(state), + ID_state_live_object, + std::move(address)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + state_live_object_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_live_object_exprt +/// +/// \a expr must be known to be \ref state_live_object_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_live_object_exprt +inline const state_live_object_exprt & +to_state_live_object_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_live_object); + const state_live_object_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_live_object_expr(const exprt &) +inline state_live_object_exprt &to_state_live_object_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_live_object); + state_live_object_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_writeable_object_exprt : public binary_predicate_exprt +{ +public: + state_writeable_object_exprt(exprt state, exprt address) + : binary_predicate_exprt( + std::move(state), + ID_state_writeable_object, + std::move(address)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } +}; + +/// \brief Cast an exprt to a \ref state_writeable_object_exprt +/// +/// \a expr must be known to be \ref state_writeable_object_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_writeable_object_exprt +inline const state_writeable_object_exprt & +to_state_writeable_object_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_writeable_object); + const state_writeable_object_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_writeable_object_expr(const exprt &) +inline state_writeable_object_exprt &to_state_writeable_object_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_writeable_object); + state_writeable_object_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_is_cstring_exprt : public binary_predicate_exprt +{ +public: + state_is_cstring_exprt(exprt state, exprt address) + : binary_predicate_exprt( + std::move(state), + ID_state_is_cstring, + std::move(address)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + state_is_cstring_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_is_cstring_exprt +/// +/// \a expr must be known to be \ref state_is_cstring_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_is_cstring_exprt +inline const state_is_cstring_exprt &to_state_is_cstring_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_cstring); + const state_is_cstring_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_is_cstring_expr(const exprt &) +inline state_is_cstring_exprt &to_state_is_cstring_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_cstring); + state_is_cstring_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_cstrlen_exprt : public binary_exprt +{ +public: + state_cstrlen_exprt(exprt state, exprt address, typet type) + : binary_exprt( + std::move(state), + ID_state_cstrlen, + std::move(address), + std::move(type)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + state_cstrlen_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_cstrlen_exprt +/// +/// \a expr must be known to be \ref state_cstrlen_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_cstrlen_exprt +inline const state_cstrlen_exprt &to_state_cstrlen_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_cstrlen); + const state_cstrlen_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_cstrlen_expr(const exprt &) +inline state_cstrlen_exprt &to_state_cstrlen_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_cstrlen); + state_cstrlen_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_is_dynamic_object_exprt : public binary_predicate_exprt +{ +public: + state_is_dynamic_object_exprt(exprt state, exprt address) + : binary_predicate_exprt( + std::move(state), + ID_state_is_dynamic_object, + std::move(address)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + state_is_dynamic_object_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_is_dynamic_object_exprt +/// +/// \a expr must be known to be \ref state_is_dynamic_object_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_is_dynamic_object_exprt +inline const state_is_dynamic_object_exprt & +to_state_is_dynamic_object_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_dynamic_object); + const state_is_dynamic_object_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_is_dynamic_object_expr(const exprt &) +inline state_is_dynamic_object_exprt & +to_state_is_dynamic_object_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_is_dynamic_object); + state_is_dynamic_object_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_object_size_exprt : public binary_exprt +{ +public: + state_object_size_exprt(exprt state, exprt address, typet type) + : binary_exprt( + std::move(state), + ID_state_object_size, + std::move(address), + std::move(type)) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + // helper + state_object_size_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_object_size_exprt +/// +/// \a expr must be known to be \ref state_object_size_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_object_size_exprt +inline const state_object_size_exprt & +to_state_object_size_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_object_size); + const state_object_size_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_object_size_expr(const exprt &) +inline state_object_size_exprt &to_state_object_size_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_object_size); + state_object_size_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_ok_exprt : public ternary_exprt +{ +public: + state_ok_exprt(irep_idt id, exprt state, exprt address, exprt size) + : ternary_exprt( + id, + std::move(state), + std::move(address), + std::move(size), + bool_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + exprt &address() + { + return op1(); + } + + const exprt &size() const + { + return op2(); + } + + exprt &size() + { + return op2(); + } + + // helper + state_ok_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_ok_exprt +/// +/// \a expr must be known to be \ref state_ok_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_ok_exprt +inline const state_ok_exprt &to_state_ok_expr(const exprt &expr) +{ + PRECONDITION( + expr.id() == ID_state_r_ok || expr.id() == ID_state_w_ok || + expr.id() == ID_state_rw_ok); + const state_ok_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_ok_expr(const exprt &) +inline state_ok_exprt &to_state_ok_expr(exprt &expr) +{ + PRECONDITION( + expr.id() == ID_state_r_ok || expr.id() == ID_state_w_ok || + expr.id() == ID_state_rw_ok); + state_ok_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class state_type_compatible_exprt : public binary_exprt +{ +public: + state_type_compatible_exprt(exprt state, exprt address) + : binary_exprt( + std::move(state), + ID_state_type_compatible, + std::move(address), + bool_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + exprt &address() + { + return op1(); + } + + const typet &access_type() const + { + return to_pointer_type(address().type()).base_type(); + } + + // helper + state_type_compatible_exprt with_state(exprt state) const + { + auto result = *this; // copy + result.state() = std::move(state); + return result; + } +}; + +/// \brief Cast an exprt to a \ref state_type_compatible_exprt +/// +/// \a expr must be known to be \ref state_type_compatible_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref state_type_compatible_exprt +inline const state_type_compatible_exprt & +to_state_type_compatible_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_type_compatible); + const state_type_compatible_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_state_type_compatible_expr(const exprt &) +inline state_type_compatible_exprt &to_state_type_compatible_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_state_type_compatible); + state_type_compatible_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +class enter_scope_state_exprt : public binary_exprt +{ +public: + enter_scope_state_exprt(exprt state, exprt address) + : binary_exprt( + std::move(state), + ID_enter_scope_state, + std::move(address), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + exprt &address() + { + return op1(); + } + + typet object_type() const + { + return to_pointer_type(address().type()).base_type(); + } + + symbol_exprt symbol() const + { + PRECONDITION(address().id() == ID_object_address); + return to_object_address_expr(address()).object_expr(); + } + +#if 0 + const exprt &size() const + { + return op2(); + } + + exprt &size() + { + return op2(); + } +#endif +}; + +/// \brief Cast an exprt to a \ref enter_scope_state_exprt +/// +/// \a expr must be known to be \ref enter_scope_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref enter_scope_state_exprt +inline const enter_scope_state_exprt & +to_enter_scope_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_enter_scope_state); + const enter_scope_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_enter_scope_state_expr(const exprt &) +inline enter_scope_state_exprt &to_enter_scope_state_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_enter_scope_state); + enter_scope_state_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +class exit_scope_state_exprt : public binary_exprt +{ +public: + exit_scope_state_exprt(exprt state, exprt address) + : binary_exprt( + std::move(state), + ID_exit_scope_state, + std::move(address), + state_typet()) + { + PRECONDITION(this->state().type().id() == ID_state); + PRECONDITION(this->address().type().id() == ID_pointer); + } + + const exprt &state() const + { + return op0(); + } + + exprt &state() + { + return op0(); + } + + const exprt &address() const + { + return op1(); + } + + exprt &address() + { + return op1(); + } + + symbol_exprt symbol() const + { + PRECONDITION(address().id() == ID_object_address); + return to_object_address_expr(address()).object_expr(); + } + +#if 0 + const exprt &size() const + { + return op2(); + } + + exprt &size() + { + return op2(); + } +#endif +}; + +/// \brief Cast an exprt to a \ref exit_scope_state_exprt +/// +/// \a expr must be known to be \ref exit_scope_state_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref exit_scope_state_exprt +inline const exit_scope_state_exprt &to_exit_scope_state_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_exit_scope_state); + const exit_scope_state_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_exit_scope_state_expr(const exprt &) +inline exit_scope_state_exprt &to_exit_scope_state_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_exit_scope_state); + exit_scope_state_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +#endif // CPROVER_CPROVER_STATE_H diff --git a/src/cprover/state_encoding.cpp b/src/cprover/state_encoding.cpp new file mode 100644 index 00000000000..d4a541a682c --- /dev/null +++ b/src/cprover/state_encoding.cpp @@ -0,0 +1,1255 @@ +/*******************************************************************\ + +Module: State Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "state_encoding.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "equality_propagation.h" +#include "instrument_contracts.h" +#include "sentinel_dll.h" +#include "solver.h" +#include "state.h" +#include "state_encoding_targets.h" +#include "variable_encoding.h" + +#include + +class state_encodingt +{ +public: + explicit state_encodingt(const goto_modelt &__goto_model) + : goto_model(__goto_model) + { + } + + void operator()( + const goto_functionst::function_mapt::const_iterator, + encoding_targett &); + + void encode( + const goto_functiont &, + const irep_idt function_identifier, + const std::string &state_prefix, + const std::vector &call_stack, + const std::string &annotation, + const symbol_exprt &entry_state, + const exprt &return_lhs, + encoding_targett &); + +protected: + using loct = goto_programt::const_targett; + const goto_modelt &goto_model; + + symbol_exprt out_state_expr(loct) const; + symbol_exprt state_expr_with_suffix(loct, const std::string &suffix) const; + symbol_exprt out_state_expr(loct, bool taken) const; + symbol_exprt in_state_expr(loct) const; + std::vector incoming_symbols(loct) const; + exprt evaluate_expr(loct, const exprt &, const exprt &) const; + exprt evaluate_expr_rec( + loct, + const exprt &, + const exprt &, + const std::unordered_set &) const; + exprt replace_nondet_rec( + loct, + const exprt &, + std::vector &nondet_symbols) const; + exprt evaluate_expr(loct, const exprt &) const; + exprt address_rec(loct, const exprt &, exprt) const; + static exprt state_lambda_expr(exprt); + exprt forall_states_expr(loct, exprt) const; + exprt forall_states_expr(loct, exprt, exprt) const; + void setup_incoming(const goto_functiont &); + exprt assignment_constraint(loct, exprt lhs, exprt rhs) const; + exprt assignment_constraint_rec( + loct, + exprt state, + exprt lhs, + exprt rhs, + std::vector &nondet_symbols) const; + void function_call(goto_programt::const_targett, encoding_targett &); + void function_call_symbol(goto_programt::const_targett, encoding_targett &); + + irep_idt function_identifier; + std::string state_prefix; + std::string annotation; + std::vector call_stack; + loct first_loc; + symbol_exprt entry_state = symbol_exprt(irep_idt(), typet()); + exprt return_lhs = nil_exprt(); + using incomingt = std::map>; + incomingt incoming; + + static symbol_exprt va_args(irep_idt function); +}; + +symbol_exprt state_encodingt::out_state_expr(loct loc) const +{ + return state_expr_with_suffix(loc, ""); +} + +symbol_exprt state_encodingt::state_expr_with_suffix( + loct loc, + const std::string &suffix) const +{ + irep_idt identifier = + state_prefix + std::to_string(loc->location_number) + suffix; + return symbol_exprt(identifier, state_predicate_type()); +} + +symbol_exprt state_encodingt::out_state_expr(loct loc, bool taken) const +{ + return state_expr_with_suffix(loc, taken ? "T" : ""); +} + +std::vector state_encodingt::incoming_symbols(loct loc) const +{ + auto incoming_it = incoming.find(loc); + + DATA_INVARIANT(incoming_it != incoming.end(), "incoming is complete"); + + std::vector symbols; + symbols.reserve(incoming_it->second.size()); + + for(auto &loc_in : incoming_it->second) + { + std::string suffix; + + // conditional jump from loc_in to loc? + if( + loc_in->is_goto() && !loc_in->condition().is_true() && + loc != std::next(loc_in)) + { + suffix = "T"; + } + + symbols.push_back(state_expr_with_suffix(loc_in, suffix)); + } + + return symbols; +} + +symbol_exprt state_encodingt::in_state_expr(loct loc) const +{ + if(loc == first_loc) + return entry_state; + + auto incoming_symbols = this->incoming_symbols(loc); + + if(incoming_symbols.size() == 1) + return incoming_symbols.front(); + else + return state_expr_with_suffix(loc, "in"); +} + +exprt state_encodingt::evaluate_expr( + loct loc, + const exprt &state, + const exprt &what) const +{ + return evaluate_expr_rec(loc, state, what, {}); +} + +exprt state_encodingt::replace_nondet_rec( + loct loc, + const exprt &what, + std::vector &nondet_symbols) const +{ + if(what.id() == ID_side_effect) + { + auto &side_effect = to_side_effect_expr(what); + auto statement = side_effect.get_statement(); + if(statement == ID_nondet) + { + irep_idt identifier = "nondet::" + state_prefix + + std::to_string(loc->location_number) + "-" + + std::to_string(nondet_symbols.size()); + auto symbol = symbol_exprt(identifier, side_effect.type()); + nondet_symbols.push_back(symbol); + return std::move(symbol); + } + else if(statement == ID_va_start) + { + // return address of va_args array + return typecast_exprt::conditional_cast( + evaluate_expr(loc, state_expr(), va_args(function_identifier)), + what.type()); + } + else + return what; // leave it + } + else + { + exprt tmp = what; + for(auto &op : tmp.operands()) + op = replace_nondet_rec(loc, op, nondet_symbols); + return tmp; + } +} + +exprt state_encodingt::evaluate_expr_rec( + loct loc, + const exprt &state, + const exprt &what, + const std::unordered_set &bound_symbols) const +{ + PRECONDITION(state.type().id() == ID_state); + + if(what.id() == ID_symbol) + { + const auto &symbol_expr = to_symbol_expr(what); + + if(symbol_expr.get_identifier() == CPROVER_PREFIX "return_value") + { + auto new_symbol = symbol_exprt("return_value", what.type()); + return evaluate_exprt( + state, address_rec(loc, state, new_symbol), what.type()); + } + else if(bound_symbols.find(symbol_expr) == bound_symbols.end()) + { + return evaluate_exprt(state, address_rec(loc, state, what), what.type()); + } + else + return what; // leave as is + } + else if( + what.id() == ID_dereference || what.id() == ID_member || + what.id() == ID_index) + { + return evaluate_exprt(state, address_rec(loc, state, what), what.type()); + } + else if(what.id() == ID_forall || what.id() == ID_exists) + { + auto new_quantifier_expr = to_quantifier_expr(what); // copy + auto new_bound_symbols = bound_symbols; // copy + + for(const auto &v : new_quantifier_expr.variables()) + new_bound_symbols.insert(v); + + new_quantifier_expr.where() = evaluate_expr_rec( + loc, state, new_quantifier_expr.where(), new_bound_symbols); + + return std::move(new_quantifier_expr); + } + else if(what.id() == ID_address_of) + { + const auto &object = to_address_of_expr(what).object(); + return address_rec(loc, state, object); + } + else if(what.id() == ID_live_object) + { + const auto &live_object_expr = to_live_object_expr(what); + auto pointer = + evaluate_expr_rec(loc, state, live_object_expr.pointer(), bound_symbols); + return state_live_object_exprt(state, pointer); + } + else if(what.id() == ID_writeable_object) + { + const auto &writeable_object_expr = to_writeable_object_expr(what); + auto pointer = evaluate_expr_rec( + loc, state, writeable_object_expr.pointer(), bound_symbols); + return state_writeable_object_exprt(state, pointer); + } + else if(what.id() == ID_is_dynamic_object) + { + const auto &is_dynamic_object_expr = to_is_dynamic_object_expr(what); + auto pointer = evaluate_expr_rec( + loc, state, is_dynamic_object_expr.address(), bound_symbols); + return state_is_dynamic_object_exprt(state, pointer); + } + else if(what.id() == ID_object_size) + { + const auto &object_size_expr = to_object_size_expr(what); + auto pointer = + evaluate_expr_rec(loc, state, object_size_expr.pointer(), bound_symbols); + return state_object_size_exprt(state, pointer, what.type()); + } + else if(what.id() == ID_r_ok || what.id() == ID_w_ok || what.id() == ID_rw_ok) + { + // we need to add the state + const auto &r_or_w_ok_expr = to_r_or_w_ok_expr(what); + auto pointer = + evaluate_expr_rec(loc, state, r_or_w_ok_expr.pointer(), bound_symbols); + auto size = + evaluate_expr_rec(loc, state, r_or_w_ok_expr.size(), bound_symbols); + auto new_id = what.id() == ID_r_ok ? ID_state_r_ok + : what.id() == ID_w_ok ? ID_state_w_ok + : ID_state_rw_ok; + return ternary_exprt(new_id, state, pointer, size, what.type()); + } + else if(what.id() == ID_is_cstring) + { + // we need to add the state + const auto &is_cstring_expr = to_unary_expr(what); + auto pointer = + evaluate_expr_rec(loc, state, is_cstring_expr.op(), bound_symbols); + return binary_predicate_exprt(state, ID_state_is_cstring, pointer); + } + else if(what.id() == ID_cstrlen) + { + // we need to add the state + const auto &cstrlen_expr = to_cstrlen_expr(what); + auto address = + evaluate_expr_rec(loc, state, cstrlen_expr.op(), bound_symbols); + return state_cstrlen_exprt(state, address, cstrlen_expr.type()); + } + else if(what.id() == ID_is_sentinel_dll) + { + // we need to add the state + if(what.operands().size() == 2) + { + const auto &is_sentinel_dll_expr = to_binary_expr(what); + auto head = evaluate_expr_rec( + loc, state, is_sentinel_dll_expr.op0(), bound_symbols); + auto tail = evaluate_expr_rec( + loc, state, is_sentinel_dll_expr.op1(), bound_symbols); + return state_is_sentinel_dll_exprt(state, head, tail); + } + else if(what.operands().size() == 3) + { + const auto &is_sentinel_dll_expr = to_ternary_expr(what); + auto head = evaluate_expr_rec( + loc, state, is_sentinel_dll_expr.op0(), bound_symbols); + auto tail = evaluate_expr_rec( + loc, state, is_sentinel_dll_expr.op1(), bound_symbols); + auto node = evaluate_expr_rec( + loc, state, is_sentinel_dll_expr.op2(), bound_symbols); + return state_is_sentinel_dll_exprt(state, head, tail, node); + } + else + DATA_INVARIANT(false, "is_sentinel_dll expressions have 2 or 3 operands"); + } + else if(what.id() == ID_side_effect) + { + // leave as is + return what; + } + else if( + (what.id() == ID_equal || what.id() == ID_notequal) && + to_binary_relation_expr(what).lhs().type().id() == ID_struct_tag) + { + const auto &lhs = to_binary_relation_expr(what).lhs(); + const auto &rhs = to_binary_relation_expr(what).rhs(); + + const auto &type = to_struct_tag_type(lhs.type()); + const namespacet ns(goto_model.symbol_table); + const auto &struct_type = ns.follow_tag(type); + + exprt::operandst conjuncts; + + for(auto &field : struct_type.components()) + { + exprt lhs_member = member_exprt(lhs, field.get_name(), field.type()); + exprt rhs_member = member_exprt(rhs, field.get_name(), field.type()); + auto equality = equal_exprt(lhs_member, rhs_member); + auto equality_evaluated = + evaluate_expr_rec(loc, state, equality, bound_symbols); + conjuncts.push_back(std::move(equality_evaluated)); + } + + if(what.id() == ID_equal) + return conjunction(conjuncts); + else + return not_exprt(conjunction(conjuncts)); + } + else + { + exprt tmp = what; + for(auto &op : tmp.operands()) + op = evaluate_expr_rec(loc, state, op, bound_symbols); + return tmp; + } +} + +exprt state_encodingt::evaluate_expr(loct loc, const exprt &what) const +{ + return evaluate_expr(loc, in_state_expr(loc), what); +} + +exprt state_encodingt::state_lambda_expr(exprt what) +{ + return lambda_exprt({state_expr()}, std::move(what)); +} + +exprt state_encodingt::forall_states_expr(loct loc, exprt what) const +{ + return forall_exprt( + {state_expr()}, + implies_exprt( + function_application_exprt(in_state_expr(loc), {state_expr()}), + std::move(what))); +} + +exprt state_encodingt::forall_states_expr(loct loc, exprt condition, exprt what) + const +{ + return forall_exprt( + {state_expr()}, + implies_exprt( + and_exprt( + function_application_exprt(in_state_expr(loc), {state_expr()}), + std::move(condition)), + std::move(what))); +} + +exprt state_encodingt::address_rec(loct loc, const exprt &state, exprt expr) + const +{ + if(expr.id() == ID_symbol) + { + return object_address_exprt(to_symbol_expr(expr)); + } + else if(expr.id() == ID_member) + { + auto compound = to_member_expr(expr).struct_op(); + auto compound_address = address_rec(loc, state, std::move(compound)); + auto component_name = to_member_expr(expr).get_component_name(); + + CHECK_RETURN(compound_address.type().id() == ID_pointer); + + if(expr.type().id() == ID_array) + { + const auto &element_type = to_array_type(expr.type()).element_type(); + return field_address_exprt( + std::move(compound_address), + component_name, + pointer_type(element_type)); + } + else + { + return field_address_exprt( + std::move(compound_address), component_name, pointer_type(expr.type())); + } + } + else if(expr.id() == ID_index) + { + auto array = to_index_expr(expr).array(); + auto index_evaluated = + evaluate_expr(loc, state, to_index_expr(expr).index()); + auto array_address = address_rec(loc, state, std::move(array)); + // The type of the element pointer may not be the type + // of the base pointer, which may be a pointer to an array. + return element_address_exprt( + std::move(array_address), + std::move(index_evaluated), + pointer_type(expr.type())); + } + else if(expr.id() == ID_dereference) + return evaluate_expr(loc, state, to_dereference_expr(expr).pointer()); + else if(expr.id() == ID_string_constant) + { + // we'll stick to 'address_of' here. + return address_of_exprt( + expr, pointer_type(to_array_type(expr.type()).element_type())); + } + else if(expr.id() == ID_array) + { + // TBD. + throw incorrect_goto_program_exceptiont( + "can't do array literals", expr.source_location()); + } + else if(expr.id() == ID_struct) + { + // TBD. + throw incorrect_goto_program_exceptiont( + "can't do struct literals", expr.source_location()); + } + else if(expr.id() == ID_union) + { + // TBD. + throw incorrect_goto_program_exceptiont( + "can't do union literals", expr.source_location()); + } + else if(expr.id() == ID_side_effect) + { + // Should have been removed elsewhere. + throw incorrect_goto_program_exceptiont( + "address of side effect " + + id2string(to_side_effect_expr(expr).get_statement()), + expr.source_location()); + } + else if(expr.id() == ID_typecast) + { + // TBD. + throw incorrect_goto_program_exceptiont( + "can't do assignments to typecasts", expr.source_location()); + } + else + { + // address of something we don't know + throw incorrect_goto_program_exceptiont( + "address of unknown object " + expr.id_string(), expr.source_location()); + } +} + +exprt state_encodingt::assignment_constraint_rec( + loct loc, + exprt state, + exprt lhs, + exprt rhs, + std::vector &nondet_symbols) const +{ + if(lhs.type().id() == ID_struct_tag) + { + // split up into fields, recursively + const namespacet ns(goto_model.symbol_table); + const auto &struct_type = ns.follow_tag(to_struct_tag_type(lhs.type())); + exprt new_state = state; + for(auto &field : struct_type.components()) + { + exprt lhs_member = member_exprt(lhs, field.get_name(), field.type()); + exprt rhs_member = member_exprt(rhs, field.get_name(), field.type()); + + if(rhs.id() == ID_struct) + rhs_member = simplify_expr(rhs_member, ns); + else if( + rhs.id() == ID_side_effect && + to_side_effect_expr(rhs).get_statement() == ID_nondet) + { + rhs_member = + side_effect_expr_nondett(rhs_member.type(), rhs.source_location()); + } + + new_state = assignment_constraint_rec( + loc, new_state, lhs_member, rhs_member, nondet_symbols); + } + + return new_state; + } + else if(lhs.type().id() == ID_array) + { + // split up into elements, recursively + const namespacet ns(goto_model.symbol_table); + auto &array_type = to_array_type(lhs.type()); + if(array_type.size().is_constant()) + { + auto size_int = + numeric_cast_v(to_constant_expr(array_type.size())); + exprt new_state = state; + + for(mp_integer i = 0; i < size_int; ++i) + { + auto i_expr = from_integer(i, array_type.index_type()); + exprt lhs_index = index_exprt(lhs, i_expr); + exprt rhs_index = index_exprt(rhs, i_expr); + + if(rhs.id() == ID_array) + rhs_index = simplify_expr(rhs_index, ns); + else if( + rhs.id() == ID_side_effect && + to_side_effect_expr(rhs).get_statement() == ID_nondet) + { + rhs_index = + side_effect_expr_nondett(rhs_index.type(), rhs.source_location()); + } + + new_state = assignment_constraint_rec( + loc, new_state, lhs_index, rhs_index, nondet_symbols); + } + + return new_state; + } + else + { + // TODO: quantifier? + return state; + } + } + else + { + auto s = state_expr(); + auto address = address_rec(loc, s, lhs); + + exprt rhs_evaluated = evaluate_expr(loc, s, rhs); + + exprt new_value = replace_nondet_rec(loc, rhs_evaluated, nondet_symbols); + + return update_state_exprt(state, address, new_value); + } +} + +exprt state_encodingt::assignment_constraint(loct loc, exprt lhs, exprt rhs) + const +{ + std::vector nondet_symbols; + + auto new_state = + assignment_constraint_rec(loc, state_expr(), lhs, rhs, nondet_symbols); + + forall_exprt::variablest binding = {state_expr()}; + binding.insert(binding.end(), nondet_symbols.begin(), nondet_symbols.end()); + + return forall_exprt( + std::move(binding), + implies_exprt( + function_application_exprt(in_state_expr(loc), {state_expr()}), + function_application_exprt(out_state_expr(loc), {new_state}))); +} + +void state_encodingt::setup_incoming(const goto_functiont &goto_function) +{ + forall_goto_program_instructions(it, goto_function.body) + incoming[it]; + + forall_goto_program_instructions(it, goto_function.body) + { + if(it->is_goto()) + incoming[it->get_target()].push_back(it); + } + + forall_goto_program_instructions(it, goto_function.body) + { + auto next = std::next(it); + if(it->is_goto() && it->condition().is_true()) + { + } + else if(next != goto_function.body.instructions.end()) + { + incoming[next].push_back(it); + } + } +} + +static exprt simplifying_not(exprt src) +{ + if(src.id() == ID_not) + return to_not_expr(src).op(); + else + return not_exprt(src); +} + +symbol_exprt state_encodingt::va_args(irep_idt function) +{ + return symbol_exprt( + "va_args::" + id2string(function), + pointer_type(pointer_type(empty_typet()))); +} + +std::set no_body_warnings; + +void state_encodingt::function_call_symbol( + goto_programt::const_targett loc, + encoding_targett &dest) +{ + const auto &function = to_symbol_expr(loc->call_function()); + const auto &type = to_code_type(function.type()); + auto identifier = function.get_identifier(); + + auto new_annotation = annotation + u8" \u2192 " + id2string(identifier); + dest.annotation(new_annotation); + + // malloc is special-cased + if(identifier == "malloc") + { + auto state = state_expr(); + PRECONDITION(loc->call_arguments().size() == 1); + auto size_evaluated = evaluate_expr(loc, state, loc->call_arguments()[0]); + + auto lhs_address = address_rec(loc, state, loc->call_lhs()); + auto lhs_type = to_pointer_type(loc->call_lhs().type()); + exprt new_state = update_state_exprt( + state, lhs_address, allocate_exprt(state, size_evaluated, lhs_type)); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {new_state})); + return; + } + + // malloc is special-cased + if(identifier == "posix_memalign") + { + // int posix_memalign(void **memptr, size_t alignment, size_t size); + auto state = state_expr(); + PRECONDITION(loc->call_arguments().size() == 3); + auto memptr_evaluated = evaluate_expr(loc, state, loc->call_arguments()[0]); + auto size_evaluated = evaluate_expr(loc, state, loc->call_arguments()[2]); + auto lhs_type = + to_pointer_type(to_pointer_type(memptr_evaluated.type()).base_type()); + exprt new_state = update_state_exprt( + state, memptr_evaluated, allocate_exprt(state, size_evaluated, lhs_type)); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {new_state})); + return; + } + + // realloc is special-cased + if(identifier == "realloc") + { + auto state = state_expr(); + PRECONDITION(loc->call_arguments().size() == 2); + auto pointer_evaluated = + evaluate_expr(loc, state, loc->call_arguments()[0]); + auto size_evaluated = evaluate_expr(loc, state, loc->call_arguments()[1]); + + auto lhs_address = address_rec(loc, state, loc->call_lhs()); + auto lhs_type = to_pointer_type(loc->call_lhs().type()); + exprt new_state = update_state_exprt( + state, + lhs_address, + reallocate_exprt(state, pointer_evaluated, size_evaluated, lhs_type)); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {new_state})); + return; + } + + // free is special-cased + if(identifier == "free") + { + auto state = state_expr(); + PRECONDITION(loc->call_arguments().size() == 1); + auto address_evaluated = + evaluate_expr(loc, state, loc->call_arguments()[0]); + + exprt new_state = deallocate_state_exprt(state, address_evaluated); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {new_state})); + return; + } + + // Find the function + auto f = goto_model.goto_functions.function_map.find(identifier); + if(f == goto_model.goto_functions.function_map.end()) + DATA_INVARIANT(false, "failed find function in function_map"); + + // Do we have a function body? + if(!f->second.body_available()) + { + // no function body -- do LHS assignment nondeterministically, if any + if(loc->call_lhs().is_not_nil()) + { + auto rhs = side_effect_expr_nondett( + loc->call_lhs().type(), loc->source_location()); + dest << assignment_constraint(loc, loc->call_lhs(), std::move(rhs)); + } + else + { + // This is a SKIP. + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + + // issue a warning, but only once + if(no_body_warnings.insert(identifier).second) + std::cout << "**** WARNING: no body for function " << identifier << '\n'; + } + else + { + // Yes, we've got a body. Check whether this is recursive. + if( + std::find(call_stack.begin(), call_stack.end(), identifier) != + call_stack.end()) + { + // ignore + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + return; + } + + // Evaluate the arguments of the call in the 'in state' + exprt arguments_state = state_expr(); + const auto &arguments = loc->call_arguments(); + + // regular parameters + for(std::size_t i = 0; i < type.parameters().size(); i++) + { + auto address = object_address_exprt(symbol_exprt( + f->second.parameter_identifiers[i], type.parameters()[i].type())); + auto value = evaluate_expr(loc, state_expr(), arguments[i]); + arguments_state = update_state_exprt(arguments_state, address, value); + } + + // extra arguments, i.e., va_arg + if(arguments.size() > type.parameters().size()) + { + std::vector va_args_elements; + auto void_ptr = pointer_type(empty_typet()); + + for(std::size_t i = type.parameters().size(); i < arguments.size(); i++) + { + auto index = i - type.parameters().size(); + auto id = "va_arg::" + state_prefix + + std::to_string(loc->location_number) + + "::" + std::to_string(index); + auto address = + object_address_exprt(symbol_exprt(id, arguments[i].type())); + auto value = evaluate_expr(loc, state_expr(), arguments[i]); + va_args_elements.push_back( + typecast_exprt::conditional_cast(address, void_ptr)); + arguments_state = update_state_exprt(arguments_state, address, value); + } + + // assign these to an array + auto va_count = va_args_elements.size(); + auto array_type = array_typet( + pointer_type(empty_typet()), from_integer(va_count, size_type())); + auto array_identifier = + "va_arg_array::" + state_prefix + std::to_string(loc->location_number); + auto array_symbol = symbol_exprt(array_identifier, array_type); + + for(std::size_t i = 0; i < va_count; i++) + { + auto address = element_address_exprt( + object_address_exprt(array_symbol), + from_integer(i, array_type.index_type()), + pointer_type(void_ptr)); + auto value = va_args_elements[i]; + arguments_state = update_state_exprt(arguments_state, address, value); + } + + // now make va_args point to the beginning of that array + auto address = object_address_exprt(va_args(identifier)); + auto value = element_address_exprt( + object_address_exprt(array_symbol), + from_integer(0, array_type.index_type()), + pointer_type(void_ptr)); + arguments_state = update_state_exprt(arguments_state, address, value); + } + + // Now assign all the arguments to the parameters + auto function_entry_state = state_expr_with_suffix(loc, "Entry"); + dest << forall_states_expr( + loc, function_application_exprt(function_entry_state, {arguments_state})); + + // now do the body, recursively + state_encodingt body_state_encoding(goto_model); + auto new_state_prefix = + state_prefix + std::to_string(loc->location_number) + "."; + auto new_call_stack = call_stack; + new_call_stack.push_back(function_identifier); + body_state_encoding.encode( + f->second, + identifier, + new_state_prefix, + new_call_stack, + new_annotation, + function_entry_state, + nil_exprt(), + dest); + + // exit state of called function + auto exit_loc = std::prev(f->second.body.instructions.end()); + irep_idt exit_state_identifier = + new_state_prefix + std::to_string(exit_loc->location_number); + auto exit_state = + symbol_exprt(exit_state_identifier, state_predicate_type()); + + // done with function, reset source location to call site + dest.set_source_location(loc->source_location()); + + // now assign the return value, if any + if(loc->call_lhs().is_not_nil()) + { + auto rhs = symbol_exprt("return_value", loc->call_lhs().type()); + auto state = state_expr(); + auto address = address_rec(exit_loc, state, loc->call_lhs()); + auto rhs_evaluated = evaluate_expr(exit_loc, state, rhs); + exprt new_state = update_state_exprt(state, address, rhs_evaluated); + dest << forall_exprt( + {state_expr()}, + implies_exprt( + function_application_exprt(std::move(exit_state), {state_expr()}), + function_application_exprt(out_state_expr(loc), {new_state}))); + } + else + { + // link up return state to exit state + dest << equal_exprt(out_state_expr(loc), std::move(exit_state)); + } + } +} + +void state_encodingt::function_call( + goto_programt::const_targett loc, + encoding_targett &dest) +{ + // Function pointer? + const auto &function = loc->call_function(); + if(function.id() == ID_dereference) + { + // TBD. + throw incorrect_goto_program_exceptiont( + "can't do function pointers", loc->source_location()); + } + else if(function.id() == ID_symbol) + { + function_call_symbol(loc, dest); + } + else + { + DATA_INVARIANT( + false, "got function that's neither a symbol nor a function pointer"); + } +} + +void state_encodingt::operator()( + goto_functionst::function_mapt::const_iterator f_entry, + encoding_targett &dest) +{ + const auto &goto_function = f_entry->second; + + if(goto_function.body.instructions.empty()) + return; + + // initial state + auto in_state = symbol_exprt("SInitial", state_predicate_type()); + + dest << forall_exprt( + {state_expr()}, + implies_exprt( + initial_state_exprt(state_expr()), + function_application_exprt(in_state, {state_expr()}))); + + auto annotation = id2string(f_entry->first); + + encode( + goto_function, + f_entry->first, + "S", + {}, + annotation, + in_state, + nil_exprt(), + dest); +} + +void state_encodingt::encode( + const goto_functiont &goto_function, + const irep_idt function_identifier, + const std::string &state_prefix, + const std::vector &call_stack, + const std::string &annotation, + const symbol_exprt &entry_state, + const exprt &return_lhs, + encoding_targett &dest) +{ + first_loc = goto_function.body.instructions.begin(); + this->function_identifier = function_identifier; + this->state_prefix = state_prefix; + this->call_stack = call_stack; + this->annotation = annotation; + this->entry_state = entry_state; + this->return_lhs = return_lhs; + + setup_incoming(goto_function); + + // constraints for each instruction + forall_goto_program_instructions(loc, goto_function.body) + { + // pass on the source code location + dest.set_source_location(loc->source_location()); + + // constraints on the incoming state + { + auto incoming_symbols = this->incoming_symbols(loc); + + if(incoming_symbols.size() >= 2) + { + auto s = state_expr(); + for(auto incoming_symbol : incoming_symbols) + { + dest << forall_exprt( + {s}, + implies_exprt( + function_application_exprt(incoming_symbol, {s}), + function_application_exprt(in_state_expr(loc), {s}))); + } + } + } + + if(loc->is_assign()) + { + auto &lhs = loc->assign_lhs(); + auto &rhs = loc->assign_rhs(); + + DATA_INVARIANT(lhs.type() == rhs.type(), "assignment type consistency"); + + if( + lhs.id() == ID_symbol && + has_prefix( + id2string(to_symbol_expr(lhs).get_identifier()), CPROVER_PREFIX) && + to_symbol_expr(lhs).get_identifier() != CPROVER_PREFIX "rounding_mode") + { + // skip for now + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else if( + lhs.id() == ID_symbol && + to_symbol_expr(lhs).get_identifier() == "_DefaultRuneLocale") + { + // /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/runetype.h + // skip for now + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else + dest << assignment_constraint(loc, lhs, rhs); + } + else if(loc->is_assume()) + { + // we produce ∅ when the assumption is false + auto state = state_expr(); + auto condition_evaluated = evaluate_expr(loc, state, loc->condition()); + + dest << forall_states_expr( + loc, + condition_evaluated, + function_application_exprt(out_state_expr(loc), {state})); + } + else if(loc->is_goto()) + { + // We produce ∅ when the 'other' branch is taken. Get the condition. + const auto &condition = loc->condition(); + + if(condition.is_true()) + { + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else + { + auto state = state_expr(); + auto condition_evaluated = evaluate_expr(loc, state, condition); + + dest << forall_states_expr( + loc, + condition_evaluated, + function_application_exprt(out_state_expr(loc, true), {state})); + + dest << forall_states_expr( + loc, + simplifying_not(condition_evaluated), + function_application_exprt(out_state_expr(loc, false), {state})); + } + } + else if(loc->is_assert()) + { + // all assertions need to hold + dest << forall_states_expr( + loc, evaluate_expr(loc, state_expr(), loc->condition())); + + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else if( + loc->is_skip() || loc->is_assert() || loc->is_location() || + loc->is_end_function()) + { + // these do not change the state + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else if(loc->is_atomic_begin() || loc->is_atomic_end()) + { + // no concurrency yet + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else if(loc->is_other()) + { + auto &code = loc->code(); + auto &statement = code.get_statement(); + if(statement == ID_array_set) + { + DATA_INVARIANT( + code.operands().size() == 2, "array_set has two operands"); + // op0 must be an array + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + else + { + // ought to print a warning + dest << equal_exprt(out_state_expr(loc), in_state_expr(loc)); + } + } + else if(loc->is_decl()) + { + auto s_in = state_expr(); + auto s_out = enter_scope_state_exprt( + s_in, address_rec(loc, s_in, loc->decl_symbol())); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {s_out})); + } + else if(loc->is_dead()) + { + auto s = state_expr(); + auto s_in = state_expr(); + auto s_out = exit_scope_state_exprt( + s_in, address_rec(loc, s_in, loc->dead_symbol())); + dest << forall_states_expr( + loc, function_application_exprt(out_state_expr(loc), {s_out})); + } + else if(loc->is_function_call()) + { + function_call(loc, dest); + } + else if(loc->is_set_return_value()) + { + const auto &rhs = loc->return_value(); + + if(return_lhs.is_nil()) + { + // treat these as assignments to a special symbol named 'return_value' + auto lhs = symbol_exprt("return_value", rhs.type()); + dest << assignment_constraint(loc, std::move(lhs), std::move(rhs)); + } + else + { + } + } + else + { + std::cout << "X: " << loc->type() << '\n'; + DATA_INVARIANT(false, "unexpected GOTO instruction"); + } + } +} + +void state_encoding( + const goto_modelt &goto_model, + bool program_is_inlined, + optionalt contract, + encoding_targett &dest) +{ + if(program_is_inlined) + { + auto f_entry = goto_model.goto_functions.function_map.find( + goto_functionst::entry_point()); + + if(f_entry == goto_model.goto_functions.function_map.end()) + throw incorrect_goto_program_exceptiont("The program has no entry point"); + + dest.annotation("function " + id2string(f_entry->first)); + + state_encodingt{goto_model}(f_entry, dest); + } + else if(contract.has_value()) + { + // check given contract + const namespacet ns(goto_model.symbol_table); + const symbolt *symbol; + if(ns.lookup(*contract, symbol)) + throw invalid_command_line_argument_exceptiont( + "The given function was not found", "contract"); + + if(!get_contract(*contract, ns).has_value()) + throw invalid_command_line_argument_exceptiont( + "The given function has no contract", "contract"); + + const auto f = goto_model.goto_functions.function_map.find(symbol->name); + CHECK_RETURN(f != goto_model.goto_functions.function_map.end()); + + dest.annotation(""); + dest.annotation("function " + id2string(symbol->name)); + state_encodingt{goto_model}(f, dest); + } + else + { + // sort alphabetically + const auto sorted = goto_model.goto_functions.sorted(); + const namespacet ns(goto_model.symbol_table); + bool found = false; + for(auto &f : sorted) + { + if( + f->first == goto_functionst::entry_point() || + get_contract(f->first, ns).has_value()) + { + dest.annotation(""); + dest.annotation("function " + id2string(f->first)); + state_encodingt{goto_model}(f, dest); + found = true; + } + } + + if(!found) + throw incorrect_goto_program_exceptiont("The program has no entry point"); + } +} + +void state_encoding( + const goto_modelt &goto_model, + state_encoding_formatt state_encoding_format, + bool program_is_inlined, + optionalt contract, + std::ostream &out) +{ + switch(state_encoding_format) + { + case state_encoding_formatt::ASCII: + { + ascii_encoding_targett dest(out); + state_encoding(goto_model, program_is_inlined, contract, dest); + } + break; + + case state_encoding_formatt::SMT2: + { + const namespacet ns(goto_model.symbol_table); + smt2_encoding_targett dest(ns, out); + state_encoding(goto_model, program_is_inlined, contract, dest); + } + break; + } +} + +void variable_encoding( + const goto_modelt &goto_model, + state_encoding_formatt state_encoding_format, + std::ostream &out) +{ + const namespacet ns(goto_model.symbol_table); + + container_encoding_targett container; + state_encoding(goto_model, true, {}, container); + + equality_propagation(container.constraints); + + variable_encoding(container.constraints); + + switch(state_encoding_format) + { + case state_encoding_formatt::ASCII: + { + ascii_encoding_targett dest(out); + dest << container; + } + break; + + case state_encoding_formatt::SMT2: + { + smt2_encoding_targett dest(ns, out); + dest << container; + } + break; + } +} + +solver_resultt state_encoding_solver( + const goto_modelt &goto_model, + bool program_is_inlined, + optionalt contract, + const solver_optionst &solver_options) +{ + const namespacet ns(goto_model.symbol_table); + + container_encoding_targett container; + state_encoding(goto_model, program_is_inlined, contract, container); + + equality_propagation(container.constraints); + + if(solver_options.verbose) + { + ascii_encoding_targett dest(std::cout); + dest << container; + } + + return solver(container.constraints, solver_options, ns); +} diff --git a/src/cprover/state_encoding.h b/src/cprover/state_encoding.h new file mode 100644 index 00000000000..89b082f1201 --- /dev/null +++ b/src/cprover/state_encoding.h @@ -0,0 +1,45 @@ +/*******************************************************************\ + +Module: State Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// State Encoding + +#ifndef CPROVER_CPROVER_STATE_ENCODING_H +#define CPROVER_CPROVER_STATE_ENCODING_H + +#include "solver.h" + +#include + +class goto_modelt; + +enum class state_encoding_formatt +{ + ASCII, + SMT2 +}; + +void state_encoding( + const goto_modelt &, + state_encoding_formatt, + bool program_is_inlined, + optionalt contract, + std::ostream &out); + +solver_resultt state_encoding_solver( + const goto_modelt &, + bool program_is_inlined, + optionalt contract, + const solver_optionst &); + +void variable_encoding( + const goto_modelt &, + state_encoding_formatt, + std::ostream &out); + +#endif // CPROVER_CPROVER_STATE_ENCODING_H diff --git a/src/cprover/state_encoding_targets.cpp b/src/cprover/state_encoding_targets.cpp new file mode 100644 index 00000000000..21d2f75c675 --- /dev/null +++ b/src/cprover/state_encoding_targets.cpp @@ -0,0 +1,185 @@ +/*******************************************************************\ + +Module: State Encoding Targets + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include "state_encoding_targets.h" + +#include +#include + +#include + +void ascii_encoding_targett::set_to_true(source_locationt, exprt expr) +{ + counter++; + if(counter < 10) + out << ' '; + out << '(' << counter << ')' << ' '; + out << format(expr) << '\n'; +} + +void state_encoding_smt2_convt::add_converters() +{ +#if 0 + auto make_unary = [this](const char *f) { + return [this, f](const exprt &expr) { + const auto &unary_expr = to_unary_expr(expr); + out << '(' << f << ' '; + convert_expr(unary_expr.op()); + out << ')'; + }; + }; +#endif + + auto make_binary = [this](const char *f) { + return [this, f](const exprt &expr) { + const auto &binary_expr = to_binary_expr(expr); + out << '(' << f << ' '; + convert_expr(binary_expr.op0()); + out << ' '; + convert_expr(binary_expr.op1()); + out << ')'; + }; + }; + + auto make_ternary = [this](const char *f) { + return [this, f](const exprt &expr) { + const auto &ternary_expr = to_ternary_expr(expr); + out << '(' << f << ' '; + convert_expr(ternary_expr.op0()); + out << ' '; + convert_expr(ternary_expr.op1()); + out << ' '; + convert_expr(ternary_expr.op2()); + out << ')'; + }; + }; + + set_converter(ID_update_state, [this](const exprt &expr) { + out << "(update-state-" << type2id(to_ternary_expr(expr).op2().type()) + << ' '; + convert_expr(to_ternary_expr(expr).op0()); + out << ' '; + convert_expr(to_ternary_expr(expr).op1()); + out << ' '; + convert_expr(to_ternary_expr(expr).op2()); + out << ')'; + }); + + set_converter(ID_enter_scope_state, [this](const exprt &expr) { + out << "(enter-scope-state-" << type2id(to_binary_expr(expr).op1().type()) + << ' '; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ' '; + auto size_opt = size_of_expr( + to_pointer_type(to_binary_expr(expr).op1().type()).base_type(), ns); + CHECK_RETURN(size_opt.has_value()); + convert_expr(*size_opt); + out << ')'; + }); + + set_converter(ID_exit_scope_state, [this](const exprt &expr) { + out << "(exit-scope-state-" << type2id(to_binary_expr(expr).op1().type()) + << ' '; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ')'; + }); + + set_converter(ID_allocate, [this](const exprt &expr) { + out << "(allocate "; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ')'; + }); + + set_converter(ID_allocate_state, [this](const exprt &expr) { + out << "(allocate "; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ')'; + }); + + set_converter(ID_reallocate, [this](const exprt &expr) { + out << "(reallocate "; + convert_expr(to_ternary_expr(expr).op0()); + out << ' '; + convert_expr(to_ternary_expr(expr).op1()); + out << ' '; + convert_expr(to_ternary_expr(expr).op2()); + out << ')'; + }); + + set_converter(ID_deallocate_state, [this](const exprt &expr) { + out << "(deallocate "; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ')'; + }); + + set_converter(ID_evaluate, [this](const exprt &expr) { + out << "(evaluate-" << type2id(expr.type()) << ' '; + convert_expr(to_binary_expr(expr).op0()); + out << ' '; + convert_expr(to_binary_expr(expr).op1()); + out << ')'; + }); + + set_converter(ID_state_is_cstring, make_binary("state-is-cstring")); + + set_converter(ID_initial_state, [this](const exprt &expr) { out << "true"; }); + + set_converter( + ID_state_is_dynamic_object, make_binary("state-is-dynamic-object")); + + set_converter(ID_state_live_object, make_binary("state-live-object")); + + set_converter( + ID_state_writeable_object, make_binary("state-writeable-object")); + + set_converter(ID_state_is_sentinel_dll, [this](const exprt &expr) { + if(expr.operands().size() == 3) + { + out << "(state-is-sentinel-dll "; + convert_expr(to_multi_ary_expr(expr).op0()); + out << ' '; + convert_expr(to_multi_ary_expr(expr).op1()); + out << ' '; + convert_expr(to_multi_ary_expr(expr).op2()); + out << ')'; + } + else + { + out << "(state-is-sentinel-dll-with-node "; + convert_expr(to_multi_ary_expr(expr).op0()); + out << ' '; + convert_expr(to_multi_ary_expr(expr).op1()); + out << ' '; + convert_expr(to_multi_ary_expr(expr).op2()); + out << ' '; + convert_expr(to_multi_ary_expr(expr).op3()); + out << ')'; + } + }); + + set_converter(ID_state_live_object, make_binary("state-live-object")); + + set_converter( + ID_state_writeable_object, make_binary("state-writeable-object")); + + set_converter(ID_state_r_ok, make_ternary("state-r-ok")); + + set_converter(ID_state_w_ok, make_ternary("state-w-ok")); + + set_converter(ID_state_rw_ok, make_ternary("state-rw-ok")); +} diff --git a/src/cprover/state_encoding_targets.h b/src/cprover/state_encoding_targets.h new file mode 100644 index 00000000000..baebf4c19eb --- /dev/null +++ b/src/cprover/state_encoding_targets.h @@ -0,0 +1,141 @@ +/*******************************************************************\ + +Module: State Encoding Targets + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_CPROVER_STATE_ENCODING_TARGETS_H +#define CPROVER_CPROVER_STATE_ENCODING_TARGETS_H + +#include + +#include + +class encoding_targett +{ +public: + virtual void annotation(const std::string &) + { + } + + virtual void set_to_true(source_locationt, exprt) = 0; + + void set_to_true(exprt expr) + { + set_to_true(source_location, std::move(expr)); + } + + void set_source_location(source_locationt __source_location) + { + source_location = std::move(__source_location); + } + + virtual ~encoding_targett() = default; + +protected: + source_locationt source_location = source_locationt::nil(); +}; + +class container_encoding_targett : public encoding_targett +{ +public: + container_encoding_targett() = default; + + using constraintst = std::vector; + constraintst constraints; + + void set_to_true(source_locationt source_location, exprt expr) override + { + if(source_location.is_not_nil()) + expr.add_source_location() = source_location; + + constraints.emplace_back(std::move(expr)); + } + +protected: + source_locationt last_source_location = source_locationt::nil(); +}; + +static inline void operator<<(encoding_targett &target, exprt constraint) +{ + target.set_to_true(std::move(constraint)); +} + +static inline void +operator<<(encoding_targett &target, const container_encoding_targett &src) +{ + for(const auto &constraint : src.constraints) + target << constraint; +} + +class state_encoding_smt2_convt : public smt2_convt +{ +public: + state_encoding_smt2_convt(const namespacet &ns, std::ostream &_out) + : smt2_convt(ns, "", "cprover", "", smt2_convt::solvert::GENERIC, _out) + + { + use_array_of_bool = true; + use_as_const = true; + add_converters(); + } + + void add_converters(); +}; + +class smt2_encoding_targett : public encoding_targett +{ +public: + smt2_encoding_targett(const namespacet &ns, std::ostream &_out) + : out(_out), smt2_conv(ns, _out) + { + } + + ~smt2_encoding_targett() + { + // finish the conversion to SMT-LIB2 + smt2_conv(); + } + + void set_to_true(source_locationt, exprt expr) override + { + smt2_conv.set_to_true(std::move(expr)); + } + + void annotation(const std::string &text) override + { + if(text.empty()) + out << '\n'; + else + out << ';' << ' ' << text << '\n'; + } + +protected: + std::ostream &out; + state_encoding_smt2_convt smt2_conv; + + void add_converters(); +}; + +class ascii_encoding_targett : public encoding_targett +{ +public: + explicit ascii_encoding_targett(std::ostream &_out) : out(_out) + { + } + + void set_to_true(source_locationt, exprt) override; + + void annotation(const std::string &text) override + { + out << text << '\n'; + } + +protected: + std::ostream &out; + std::size_t counter = 0; +}; + +#endif // CPROVER_CPROVER_STATE_ENCODING_TARGETS_H diff --git a/src/cprover/variable_encoding.cpp b/src/cprover/variable_encoding.cpp new file mode 100644 index 00000000000..54b2a372efd --- /dev/null +++ b/src/cprover/variable_encoding.cpp @@ -0,0 +1,124 @@ +/*******************************************************************\ + +Module: Variable Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Variable Encoding + +#include "variable_encoding.h" + +#include +#include +#include +#include + +#include "find_variables.h" +#include "state.h" + +#include + +exprt variable_encoding(exprt src, const binding_exprt::variablest &variables) +{ + // operands first + for(auto &op : src.operands()) + op = variable_encoding(op, variables); + + if(src.id() == ID_forall) + { + auto &forall_expr = to_forall_expr(src); + if( + forall_expr.variables().size() == 1 && + forall_expr.symbol().type().id() == ID_state) + { + forall_expr + .variables() = variables; + return std::move(forall_expr); + } + } + else if(src.id() == ID_function_application) + { + auto &function_application = to_function_application_expr(src); + if( + function_application.arguments().size() == 1 && + function_application.arguments().front().type().id() == ID_state) + { + if(function_application.arguments().front().id() == ID_symbol) + { + exprt::operandst new_arguments; + for(auto &v : variables) + new_arguments.push_back(v); + function_application.arguments() = new_arguments; + } + else if(function_application.arguments().front().id() == ID_tuple) + { + DATA_INVARIANT( + function_application.arguments().front().operands().size() == + variables.size(), + "tuple size must match"); + auto operands = function_application.arguments().front().operands(); + function_application.arguments() = operands; + } + else + throw analysis_exceptiont("can't convert " + format_to_string(src)); + } + else + throw analysis_exceptiont("can't convert " + format_to_string(src)); + } + else if(src.id() == ID_evaluate) + { + auto &evaluate_expr = to_evaluate_expr(src); + if(evaluate_expr.address().id() == ID_object_address) + return symbol_exprt( + to_object_address_expr(evaluate_expr.address()).object_expr()); + else + throw analysis_exceptiont("can't convert " + format_to_string(src)); + } + else if(src.id() == ID_update_state) + { + auto &update_state_expr = to_update_state_expr(src); + if(update_state_expr.address().id() == ID_object_address) + { + auto lhs_symbol = + to_object_address_expr(update_state_expr.address()).object_expr(); + exprt::operandst operands; + for(auto &v : variables) + { + if(v == lhs_symbol) + operands.push_back(update_state_expr.new_value()); + else + operands.push_back(v); + } + return tuple_exprt(operands, typet(ID_state)); + } + else + throw analysis_exceptiont("can't convert " + format_to_string(src)); + } + + return src; +} + +void variable_encoding(std::vector &constraints) +{ + binding_exprt::variablest variables; + + for(auto &v : find_variables(constraints)) + variables.push_back(v); + + if(variables.empty()) + throw analysis_exceptiont("no variables found"); + + // sort alphabetically + std::sort( + variables.begin(), + variables.end(), + [](const symbol_exprt &a, const symbol_exprt &b) { + return id2string(a.get_identifier()) < id2string(b.get_identifier()); + }); + + for(auto &c : constraints) + c = variable_encoding(c, variables); +} diff --git a/src/cprover/variable_encoding.h b/src/cprover/variable_encoding.h new file mode 100644 index 00000000000..9e7d7ba14d4 --- /dev/null +++ b/src/cprover/variable_encoding.h @@ -0,0 +1,19 @@ +/*******************************************************************\ + +Module: Variable Encoding + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file +/// Variable Encoding + +#ifndef CPROVER_CPROVER_VARIABLE_ENCODING_H +#define CPROVER_CPROVER_VARIABLE_ENCODING_H + +#include + +void variable_encoding(std::vector &); + +#endif // CPROVER_CPROVER_VARIABLE_ENDCODING_H diff --git a/src/cprover/wcwidth.c b/src/cprover/wcwidth.c new file mode 100644 index 00000000000..61e822ad679 --- /dev/null +++ b/src/cprover/wcwidth.c @@ -0,0 +1,309 @@ +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +#include + +struct interval { + int first; + int last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(wchar_t ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +int mk_wcwidth(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + + +int mk_wcswidth(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, + { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, + { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} diff --git a/src/solvers/flattening/bv_pointers.cpp b/src/solvers/flattening/bv_pointers.cpp index 6e2be1ded4f..a6d79541494 100644 --- a/src/solvers/flattening/bv_pointers.cpp +++ b/src/solvers/flattening/bv_pointers.cpp @@ -410,6 +410,11 @@ bvt bv_pointerst::convert_pointer_type(const exprt &expr) CHECK_RETURN(bv_opt->size() == bits); return *bv_opt; } + else if(expr.id() == ID_object_address) + { + const auto &object_address_expr = to_object_address_expr(expr); + return add_addr(object_address_expr.object_expr()); + } else if(expr.id()==ID_constant) { const constant_exprt &c = to_constant_expr(expr); @@ -734,11 +739,6 @@ bvt bv_pointerst::convert_bitvector(const exprt &expr) return bv_utils.zero_extension(op0, width); } - else if(expr.id() == ID_object_address) - { - const auto &object_address_expr = to_object_address_expr(expr); - return add_addr(object_address_expr.object_expr()); - } return SUB::convert_bitvector(expr); } diff --git a/src/solvers/smt2/smt2_conv.cpp b/src/solvers/smt2/smt2_conv.cpp index e7c6d3b1ee3..7bbca285054 100644 --- a/src/solvers/smt2/smt2_conv.cpp +++ b/src/solvers/smt2/smt2_conv.cpp @@ -996,6 +996,14 @@ std::string smt2_convt::type2id(const typet &type) const else return "S" + std::to_string(boolbv_width(type)); } + else if(type.id() == ID_union_tag) + { + return "U" + std::to_string(boolbv_width(type)); + } + else if(type.id() == ID_array) + { + return "A" + type2id(to_array_type(type).element_type()); + } else { UNREACHABLE; @@ -1043,8 +1051,29 @@ void smt2_convt::convert_floatbv(const exprt &expr) out << ')'; } +void smt2_convt::convert_string_literal(const std::string &s) +{ + out << '"'; + for(auto ch : s) + { + // " is escaped by double-quoting + if(ch == '"') + out << '"'; + out << ch; + } + out << '"'; +} + void smt2_convt::convert_expr(const exprt &expr) { + // try hash table first + auto converter_result = converters.find(expr.id()); + if(converter_result != converters.end()) + { + converter_result->second(expr); + return; // done + } + // huge monster case split over expression id if(expr.id()==ID_symbol) { @@ -1621,7 +1650,42 @@ void smt2_convt::convert_expr(const exprt &expr) } else if(expr.id()==ID_update) { - convert_update(expr); + convert_update(to_update_expr(expr)); + } + else if(expr.id() == ID_object_address) + { + out << "(object-address "; + convert_string_literal( + id2string(to_object_address_expr(expr).object_identifier())); + out << ')'; + } + else if(expr.id() == ID_element_address) + { + // We turn this binary expression into a ternary expression + // by adding the size of the array elements as third argument. + const auto &element_address_expr = to_element_address_expr(expr); + + auto element_size_expr_opt = + ::size_of_expr(element_address_expr.element_type(), ns); + CHECK_RETURN(element_size_expr_opt.has_value()); + + out << "(element-address-" << type2id(expr.type()) << ' '; + convert_expr(element_address_expr.base()); + out << ' '; + convert_expr(element_address_expr.index()); + out << ' '; + convert_expr(typecast_exprt::conditional_cast( + *element_size_expr_opt, element_address_expr.index().type())); + out << ')'; + } + else if(expr.id() == ID_field_address) + { + const auto &field_address_expr = to_field_address_expr(expr); + out << "(field-address-" << type2id(expr.type()) << ' '; + convert_expr(field_address_expr.base()); + out << ' '; + convert_string_literal(id2string(field_address_expr.component_name())); + out << ')'; } else if(expr.id()==ID_member) { @@ -4248,7 +4312,7 @@ void smt2_convt::convert_with(const with_exprt &expr) expr.type().id_string()); } -void smt2_convt::convert_update(const exprt &expr) +void smt2_convt::convert_update(const update_exprt &expr) { PRECONDITION(expr.operands().size() == 3); @@ -4663,49 +4727,47 @@ void smt2_convt::set_to(const exprt &expr, bool value) smt2_identifiers.insert(smt2_identifier); out << "; set_to true (equal)\n"; - out << "(define-fun " << smt2_identifier; if(equal_expr.lhs().type().id() == ID_mathematical_function) { + // We avoid define-fun, since it has been reported to cause + // trouble with Z3's parser. + out << "(declare-fun " << smt2_identifier; + auto &mathematical_function_type = to_mathematical_function_type(equal_expr.lhs().type()); + out << " ("; bool first = true; - for(std::size_t p_nr = 0; - p_nr < mathematical_function_type.domain().size(); - p_nr++) + for(auto &t : mathematical_function_type.domain()) { if(first) first = false; else out << ' '; - out << '(' << 'p' << (p_nr + 1) << ' '; - convert_type(mathematical_function_type.domain()[p_nr]); - out << ')'; + convert_type(t); } out << ") "; convert_type(mathematical_function_type.codomain()); + out << ")\n"; - out << ' ' << '('; + out << "(assert (= " << smt2_identifier << ' '; convert_expr(prepared_rhs); - for(std::size_t p_nr = 0; - p_nr < mathematical_function_type.domain().size(); - p_nr++) - out << ' ' << 'p' << (p_nr + 1); - out << ')'; + out << ')' << ')' << '\n'; } else { + out << "(define-fun " << smt2_identifier; out << " () "; convert_type(equal_expr.lhs().type()); out << ' '; convert_expr(prepared_rhs); + out << ')' << '\n'; } - out << ')' << '\n'; return; // done } } @@ -5098,6 +5160,219 @@ void smt2_convt::find_symbols(const exprt &expr) out << ")\n"; // define-fun } } + else if(expr.id() == ID_initial_state) + { + irep_idt function = "initial-state"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_unary_expr(expr).op().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_evaluate) + { + irep_idt function = "evaluate-" + type2id(expr.type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if( + expr.id() == ID_state_is_cstring || + expr.id() == ID_state_is_dynamic_object || + expr.id() == ID_state_live_object || expr.id() == ID_state_writeable_object) + { + irep_idt function = + expr.id() == ID_state_is_cstring ? "state-is-cstring" + : expr.id() == ID_state_is_dynamic_object ? "state-is-dynamic-object" + : expr.id() == ID_state_live_object ? "state-live-object" + : "state-writeable-object"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if( + expr.id() == ID_state_r_ok || expr.id() == ID_state_w_ok || + expr.id() == ID_state_rw_ok) + { + irep_idt function = expr.id() == ID_state_r_ok ? "state-r-ok" + : expr.id() == ID_state_w_ok ? "state-w-ok" + : "state-rw-ok"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_ternary_expr(expr).op0().type()); + out << ' '; + convert_type(to_ternary_expr(expr).op1().type()); + out << ' '; + convert_type(to_ternary_expr(expr).op2().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_update_state) + { + irep_idt function = + "update-state-" + type2id(to_multi_ary_expr(expr).op2().type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_multi_ary_expr(expr).op0().type()); + out << ' '; + convert_type(to_multi_ary_expr(expr).op1().type()); + out << ' '; + convert_type(to_multi_ary_expr(expr).op2().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_enter_scope_state) + { + irep_idt function = + "enter-scope-state-" + type2id(to_binary_expr(expr).op1().type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ' '; + convert_type(size_type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_exit_scope_state) + { + irep_idt function = + "exit-scope-state-" + type2id(to_binary_expr(expr).op1().type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_allocate) + { + irep_idt function = "allocate"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_reallocate) + { + irep_idt function = "reallocate"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_ternary_expr(expr).op0().type()); + out << ' '; + convert_type(to_ternary_expr(expr).op1().type()); + out << ' '; + convert_type(to_ternary_expr(expr).op2().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_deallocate_state) + { + irep_idt function = "deallocate"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_binary_expr(expr).op0().type()); + out << ' '; + convert_type(to_binary_expr(expr).op1().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_object_address) + { + irep_idt function = "object-address"; + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " (String) "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_field_address) + { + irep_idt function = "field-address-" + type2id(expr.type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_field_address_expr(expr).op().type()); + out << ' '; + out << "String"; + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } + else if(expr.id() == ID_element_address) + { + irep_idt function = "element-address-" + type2id(expr.type()); + + if(state_fkt_declared.insert(function).second) + { + out << "(declare-fun " << function << " ("; + convert_type(to_element_address_expr(expr).base().type()); + out << ' '; + convert_type(to_element_address_expr(expr).index().type()); + out << ' '; // repeat, for the element size + convert_type(to_element_address_expr(expr).index().type()); + out << ") "; + convert_type(expr.type()); // return type + out << ")\n"; // declare-fun + } + } } bool smt2_convt::use_array_theory(const exprt &expr) @@ -5248,6 +5523,10 @@ void smt2_convt::convert_type(const typet &type) { convert_type(c_bit_field_replacement_type(to_c_bit_field_type(type), ns)); } + else if(type.id() == ID_state) + { + out << "state"; + } else { UNEXPECTEDCASE("unsupported type: "+type.id_string()); @@ -5464,6 +5743,14 @@ void smt2_convt::find_symbols_rec( find_symbols_rec(ns.follow_tag(union_tag), recstack); } } + else if(type.id() == ID_state) + { + if(datatype_map.find(type) == datatype_map.end()) + { + datatype_map[type] = "state"; + out << "(declare-sort state 0)\n"; + } + } else if(type.id() == ID_mathematical_function) { const auto &mathematical_function_type = diff --git a/src/solvers/smt2/smt2_conv.h b/src/solvers/smt2/smt2_conv.h index 5d78e834c95..06163d1baf3 100644 --- a/src/solvers/smt2/smt2_conv.h +++ b/src/solvers/smt2/smt2_conv.h @@ -85,11 +85,19 @@ class smt2_convt : public stack_decision_proceduret static std::string convert_identifier(const irep_idt &identifier); + void set_converter(irep_idt id, std::function converter) + { + converters[id] = std::move(converter); + } + protected: const namespacet &ns; std::ostream &out; std::string benchmark, notes, logic; solvert solver; + using converterst = std:: + unordered_map, irep_id_hash>; + converterst converters; std::vector assumptions; boolbv_widtht boolbv_width; @@ -138,11 +146,12 @@ class smt2_convt : public stack_decision_proceduret void convert_member(const member_exprt &expr); void convert_with(const with_exprt &expr); - void convert_update(const exprt &expr); + void convert_update(const update_exprt &); void convert_expr(const exprt &); void convert_type(const typet &); void convert_literal(const literalt); + void convert_string_literal(const std::string &); literalt convert(const exprt &expr); tvt l_get(literalt l) const; @@ -185,6 +194,10 @@ class smt2_convt : public stack_decision_proceduret std::string floatbv_suffix(const exprt &) const; std::set bvfp_set; // already converted + // conversion for ID_evaluate, ID_update_state, + // ID_object_address, ID_field_address, ID_element_address + std::set state_fkt_declared; + class smt2_symbolt : public nullary_exprt { public: diff --git a/src/util/Makefile b/src/util/Makefile index 431cdb15f0d..fbd81627abf 100644 --- a/src/util/Makefile +++ b/src/util/Makefile @@ -92,6 +92,7 @@ SRC = arith_tools.cpp \ string_hash.cpp \ string_utils.cpp \ structured_data.cpp \ + substitute_symbols.cpp \ symbol.cpp \ symbol_table_base.cpp \ symbol_table.cpp \ diff --git a/src/util/format_expr.cpp b/src/util/format_expr.cpp index d1be319123a..bdd8bccf8e9 100644 --- a/src/util/format_expr.cpp +++ b/src/util/format_expr.cpp @@ -616,6 +616,11 @@ format_expr_configt::find_formatter(const exprt &expr) format_expr_configt format_expr_config; +void add_format_hook(irep_idt id, format_expr_configt::formattert formatter) +{ + format_expr_config.expr_map[id] = std::move(formatter); +} + std::ostream &format_rec(std::ostream &os, const exprt &expr) { auto &formatter = format_expr_config.find_formatter(expr); diff --git a/src/util/format_expr.h b/src/util/format_expr.h index 6862c710f33..458ade7db21 100644 --- a/src/util/format_expr.h +++ b/src/util/format_expr.h @@ -10,6 +10,9 @@ Author: Daniel Kroening, kroening@kroening.com #define CPROVER_UTIL_FORMAT_EXPR_H #include "format.h" +#include "irep.h" + +#include class exprt; @@ -17,4 +20,11 @@ class exprt; //! that is inspired by C/C++/Java, and is meant for debugging std::ostream &format_rec(std::ostream &, const exprt &); +/// Adds a formatter for expressions with the given ID at runtime. +/// The formatter is given as a function that receives the output +/// stream and the expression as arguments. +void add_format_hook( + irep_idt, + std::function); + #endif // CPROVER_UTIL_FORMAT_EXPR_H diff --git a/src/util/irep_ids.def b/src/util/irep_ids.def index 2c94928a59e..b0754e1121a 100644 --- a/src/util/irep_ids.def +++ b/src/util/irep_ids.def @@ -158,6 +158,7 @@ IREP_ID_ONE(is_cstring) IREP_ID_TWO(C_string_constant, #string_constant) IREP_ID_ONE(string_constant) IREP_ID_ONE(cstrlen) +IREP_ID_ONE(is_sentinel_dll) IREP_ID_ONE(width) IREP_ID_ONE(components) IREP_ID_ONE(bv) @@ -199,6 +200,7 @@ IREP_ID_ONE(destination) IREP_ID_ONE(main) IREP_ID_ONE(expression) IREP_ID_ONE(allocate) +IREP_ID_ONE(reallocate) IREP_ID_TWO(C_cxx_alloc_type, #cxx_alloc_type) IREP_ID_ONE(cpp_new) IREP_ID_ONE(cpp_delete) @@ -456,7 +458,9 @@ IREP_ID_ONE(object_descriptor) IREP_ID_ONE(is_dynamic_object) IREP_ID_ONE(dynamic_object) IREP_ID_TWO(C_dynamic, #dynamic) +IREP_ID_ONE(pointer_in_range) IREP_ID_ONE(object_size) +IREP_ID_ONE(separate) IREP_ID_ONE(good_pointer) IREP_ID_ONE(live_object) IREP_ID_ONE(writeable_object) @@ -876,8 +880,25 @@ IREP_ID_ONE(saturating_plus) IREP_ID_ONE(annotated_pointer_constant) IREP_ID_TWO(C_flexible_array_member, #flexible_array_member) IREP_ID_ONE(state) +IREP_ID_ONE(initial_state) IREP_ID_ONE(evaluate) +IREP_ID_ONE(allocate_state) +IREP_ID_ONE(reallocate_state) +IREP_ID_ONE(deallocate_state) IREP_ID_ONE(update_state) +IREP_ID_ONE(enter_scope_state) +IREP_ID_ONE(exit_scope_state) +IREP_ID_ONE(state_r_ok) +IREP_ID_ONE(state_w_ok) +IREP_ID_ONE(state_rw_ok) +IREP_ID_ONE(state_type_compatible) +IREP_ID_ONE(state_cstrlen) +IREP_ID_ONE(state_is_cstring) +IREP_ID_ONE(state_is_dynamic_object) +IREP_ID_ONE(state_is_sentinel_dll) +IREP_ID_ONE(state_live_object) +IREP_ID_ONE(state_writeable_object) +IREP_ID_ONE(state_object_size) IREP_ID_TWO(overflow_result_plus, overflow_result-+) IREP_ID_TWO(overflow_result_minus, overflow_result--) IREP_ID_TWO(overflow_result_mult, overflow_result-*) diff --git a/src/util/pointer_expr.cpp b/src/util/pointer_expr.cpp index 6369201a8d5..38a3af19039 100644 --- a/src/util/pointer_expr.cpp +++ b/src/util/pointer_expr.cpp @@ -13,6 +13,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "c_types.h" #include "expr_util.h" #include "pointer_offset_size.h" +#include "pointer_predicates.h" #include "simplify_expr.h" void dynamic_object_exprt::set_instance(unsigned int instance) @@ -155,9 +156,8 @@ field_address_exprt::field_address_exprt( } element_address_exprt::element_address_exprt(const exprt &base, exprt index) - : binary_exprt( + : element_address_exprt( base, - ID_element_address, std::move(index), pointer_typet( to_pointer_type(base.type()).base_type(), @@ -165,6 +165,18 @@ element_address_exprt::element_address_exprt(const exprt &base, exprt index) { } +element_address_exprt::element_address_exprt( + exprt base, + exprt index, + pointer_typet type) + : binary_exprt( + std::move(base), + ID_element_address, + std::move(index), + std::move(type)) +{ +} + const exprt &object_descriptor_exprt::root_object(const exprt &expr) { const exprt *p = &expr; @@ -214,3 +226,15 @@ void dereference_exprt::validate( "dereference expression's type must match the base type of the type of its " "operand"); } + +exprt pointer_in_range_exprt::lower() const +{ + return and_exprt( + {same_object(op0(), op1()), + same_object(op1(), op2()), + r_ok_exprt( + op0(), minus_exprt(pointer_offset(op2()), pointer_offset(op0()))), + binary_relation_exprt(pointer_offset(op0()), ID_le, pointer_offset(op1())), + binary_relation_exprt( + pointer_offset(op1()), ID_le, pointer_offset(op2()))}); +} diff --git a/src/util/pointer_expr.h b/src/util/pointer_expr.h index 48ad5b71597..7db9140a17b 100644 --- a/src/util/pointer_expr.h +++ b/src/util/pointer_expr.h @@ -366,6 +366,58 @@ inline is_dynamic_object_exprt &to_is_dynamic_object_expr(exprt &expr) return static_cast(expr); } +/// pointer_in_range(a, b, c) evaluates to true iff +/// same_object(a, b, c) ∧ r_ok(a, offset(c)-offset(a)) ∧ a<=b ∧ b<=c +/// Note that the last inequality is weak, i.e., b may be equal to c. +class pointer_in_range_exprt : public ternary_exprt +{ +public: + explicit pointer_in_range_exprt(exprt a, exprt b, exprt c) + : ternary_exprt( + ID_pointer_in_range, + std::move(a), + std::move(b), + std::move(c), + bool_typet()) + { + PRECONDITION(op0().type().id() == ID_pointer); + PRECONDITION(op1().type().id() == ID_pointer); + PRECONDITION(op2().type().id() == ID_pointer); + } + + // translate into equivalent conjunction + exprt lower() const; +}; + +template <> +inline bool can_cast_expr(const exprt &base) +{ + return base.id() == ID_pointer_in_range; +} + +inline void validate_expr(const pointer_in_range_exprt &value) +{ + validate_operands(value, 3, "pointer_in_range must have three operands"); +} + +inline const pointer_in_range_exprt &to_pointer_in_range_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_pointer_in_range); + DATA_INVARIANT( + expr.operands().size() == 3, "pointer_in_range must have three operands"); + return static_cast(expr); +} + +/// \copydoc to_pointer_in_range_expr(const exprt &) +/// \ingroup gr_std_expr +inline pointer_in_range_exprt &to_pointer_in_range_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_pointer_in_range); + DATA_INVARIANT( + expr.operands().size() == 3, "pointer_in_range must have one operand"); + return static_cast(expr); +} + /// \brief Operator to return the address of an object class address_of_exprt : public unary_exprt { @@ -583,6 +635,11 @@ class element_address_exprt : public binary_exprt /// The index is expected to have an integer type. element_address_exprt(const exprt &base, exprt index); + /// constructor for element addresses. + /// The base address must be a pointer to an element. + /// The index is expected to have an integer type. + element_address_exprt(exprt base, exprt index, pointer_typet); + const pointer_typet &type() const { return static_cast(exprt::type()); @@ -1287,4 +1344,56 @@ inline writeable_object_exprt &to_writeable_object_expr(exprt &expr) return ret; } +/// A predicate that indicates that the objects pointed to are distinct +class separate_exprt : public multi_ary_exprt +{ +public: + explicit separate_exprt(exprt::operandst __operands) + : multi_ary_exprt(ID_separate, std::move(__operands), bool_typet()) + { + } + + separate_exprt(exprt __op0, exprt __op1) + : multi_ary_exprt( + std::move(__op0), + ID_separate, + std::move(__op1), + bool_typet()) + { + } +}; + +template <> +inline bool can_cast_expr(const exprt &base) +{ + return base.id() == ID_separate; +} + +inline void validate_expr(const separate_exprt &value) +{ +} + +/// \brief Cast an exprt to a \ref separate_exprt +/// +/// \a expr must be known to be \ref separate_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref separate_exprt +inline const separate_exprt &to_separate_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_separate); + const separate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_separate_expr(const exprt &) +inline separate_exprt &to_separate_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_separate); + separate_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} + #endif // CPROVER_UTIL_POINTER_EXPR_H diff --git a/src/util/simplify_expr_pointer.cpp b/src/util/simplify_expr_pointer.cpp index 3f330bcb366..797e2d5bdf6 100644 --- a/src/util/simplify_expr_pointer.cpp +++ b/src/util/simplify_expr_pointer.cpp @@ -358,9 +358,8 @@ simplify_exprt::simplify_pointer_offset(const pointer_offset_exprt &expr) if(!element_size.has_value()) return unchanged(expr); - // this might change the type of the pointer! exprt pointer_offset_expr = simplify_pointer_offset( - to_pointer_offset_expr(pointer_offset(ptr_expr.front()))); + pointer_offset_exprt(ptr_expr.front(), expr.type())); exprt sum; diff --git a/src/util/std_expr.cpp b/src/util/std_expr.cpp index 765b8a6abc4..9eda7364a8f 100644 --- a/src/util/std_expr.cpp +++ b/src/util/std_expr.cpp @@ -11,6 +11,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "namespace.h" #include "pointer_expr.h" #include "range.h" +#include "substitute_symbols.h" #include @@ -165,100 +166,6 @@ void let_exprt::validate(const exprt &expr, const validation_modet vm) } } -static optionalt substitute_symbols_rec( - const std::map &substitutions, - exprt src) -{ - if(src.id() == ID_symbol) - { - auto s_it = substitutions.find(to_symbol_expr(src).get_identifier()); - if(s_it == substitutions.end()) - return {}; - else - return s_it->second; - } - else if( - src.id() == ID_forall || src.id() == ID_exists || src.id() == ID_lambda) - { - const auto &binding_expr = to_binding_expr(src); - - // bindings may be nested, - // which may hide some of our substitutions - auto new_substitutions = substitutions; - for(const auto &variable : binding_expr.variables()) - new_substitutions.erase(variable.get_identifier()); - - auto op_result = - substitute_symbols_rec(new_substitutions, binding_expr.where()); - if(op_result.has_value()) - return binding_exprt( - src.id(), - binding_expr.variables(), - op_result.value(), - binding_expr.type()); - else - return {}; - } - else if(src.id() == ID_let) - { - auto new_let_expr = to_let_expr(src); // copy - const auto &binding_expr = to_let_expr(src).binding(); - - // bindings may be nested, - // which may hide some of our substitutions - auto new_substitutions = substitutions; - for(const auto &variable : binding_expr.variables()) - new_substitutions.erase(variable.get_identifier()); - - bool op_changed = false; - - for(auto &op : new_let_expr.values()) - { - auto op_result = substitute_symbols_rec(new_substitutions, op); - - if(op_result.has_value()) - { - op = op_result.value(); - op_changed = true; - } - } - - auto op_result = - substitute_symbols_rec(new_substitutions, binding_expr.where()); - if(op_result.has_value()) - { - new_let_expr.where() = op_result.value(); - op_changed = true; - } - - if(op_changed) - return std::move(new_let_expr); - else - return {}; - } - - if(!src.has_operands()) - return {}; - - bool op_changed = false; - - for(auto &op : src.operands()) - { - auto op_result = substitute_symbols_rec(substitutions, op); - - if(op_result.has_value()) - { - op = op_result.value(); - op_changed = true; - } - } - - if(op_changed) - return src; - else - return {}; -} - exprt binding_exprt::instantiate(const operandst &values) const { // number of values must match the number of bound variables @@ -281,7 +188,7 @@ exprt binding_exprt::instantiate(const operandst &values) const substitutions[variables[i].get_identifier()] = values[i]; // now recurse downwards and substitute in 'where' - auto substitute_result = substitute_symbols_rec(substitutions, where()); + auto substitute_result = substitute_symbols(substitutions, where()); if(substitute_result.has_value()) return substitute_result.value(); diff --git a/src/util/substitute_symbols.cpp b/src/util/substitute_symbols.cpp new file mode 100644 index 00000000000..f28ac87c8e1 --- /dev/null +++ b/src/util/substitute_symbols.cpp @@ -0,0 +1,114 @@ +/*******************************************************************\ + +Module: Symbol Substitution + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +/// \file util/substitute_symbols.cpp +/// Symbol Substitution + +#include "substitute_symbols.h" + +#include "std_expr.h" + +static optionalt substitute_symbols_rec( + const std::map &substitutions, + exprt src) +{ + if(src.id() == ID_symbol) + { + auto s_it = substitutions.find(to_symbol_expr(src).get_identifier()); + if(s_it == substitutions.end()) + return {}; + else + return s_it->second; + } + else if( + src.id() == ID_forall || src.id() == ID_exists || src.id() == ID_lambda) + { + const auto &binding_expr = to_binding_expr(src); + + // bindings may be nested, + // which may hide some of our substitutions + auto new_substitutions = substitutions; + for(const auto &variable : binding_expr.variables()) + new_substitutions.erase(variable.get_identifier()); + + auto op_result = + substitute_symbols_rec(new_substitutions, binding_expr.where()); + if(op_result.has_value()) + { + auto new_binding_expr = binding_expr; // copy + new_binding_expr.where() = std::move(op_result.value()); + return std::move(new_binding_expr); + } + else + return {}; + } + else if(src.id() == ID_let) + { + auto new_let_expr = to_let_expr(src); // copy + const auto &binding_expr = to_let_expr(src).binding(); + + // bindings may be nested, + // which may hide some of our substitutions + auto new_substitutions = substitutions; + for(const auto &variable : binding_expr.variables()) + new_substitutions.erase(variable.get_identifier()); + + bool op_changed = false; + + for(auto &op : new_let_expr.values()) + { + auto op_result = substitute_symbols_rec(new_substitutions, op); + + if(op_result.has_value()) + { + op = op_result.value(); + op_changed = true; + } + } + + auto op_result = + substitute_symbols_rec(new_substitutions, binding_expr.where()); + if(op_result.has_value()) + { + new_let_expr.where() = op_result.value(); + op_changed = true; + } + + if(op_changed) + return std::move(new_let_expr); + else + return {}; + } + + if(!src.has_operands()) + return {}; + + bool op_changed = false; + + for(auto &op : src.operands()) + { + auto op_result = substitute_symbols_rec(substitutions, op); + + if(op_result.has_value()) + { + op = op_result.value(); + op_changed = true; + } + } + + if(op_changed) + return src; + else + return {}; +} + +optionalt +substitute_symbols(const std::map &substitutions, exprt src) +{ + return substitute_symbols_rec(substitutions, src); +} diff --git a/src/util/substitute_symbols.h b/src/util/substitute_symbols.h new file mode 100644 index 00000000000..e4701e3dd48 --- /dev/null +++ b/src/util/substitute_symbols.h @@ -0,0 +1,28 @@ +/*******************************************************************\ + +Module: Symbol Substitution + +Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#ifndef CPROVER_UTIL_SUBSTITUTE_SYMBOLS_H +#define CPROVER_UTIL_SUBSTITUTE_SYMBOLS_H + +/// \file util/substitute_symbols.h +/// Symbol Substitution + +#include "expr.h" + +#include + +/// Substitute free occurrences of the variables given +/// by their identifiers in the keys of the map in the +/// given expression. Only symbol_exprt expressions are +/// substituted. +/// \returns expression after substitution, +/// or {} when no substitution took place +optionalt +substitute_symbols(const std::map &substitutions, exprt); + +#endif // CPROVER_UTIL_SUBSTITUTE_SYMBOLS_H diff --git a/unit/Makefile b/unit/Makefile index 10a80eb1e74..1fe1db9c52e 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -135,6 +135,7 @@ SRC += analyses/ai/ai.cpp \ util/expr_iterator.cpp \ util/file_util.cpp \ util/format.cpp \ + util/format_expr.cpp \ util/format_number_range.cpp \ util/get_base_name.cpp \ util/graph.cpp \ diff --git a/unit/util/format_expr.cpp b/unit/util/format_expr.cpp new file mode 100644 index 00000000000..b39f642b6b3 --- /dev/null +++ b/unit/util/format_expr.cpp @@ -0,0 +1,26 @@ +/*******************************************************************\ + + Module: Unit tests for `format` function on expressions + + Author: Daniel Kroening, dkr@amazon.com + +\*******************************************************************/ + +#include +#include +#include + +#include + +TEST_CASE( + "Format an expression with a format hook", + "[core][util][format_hook]") +{ + irep_idt custom_id("custom_id"); + add_format_hook( + custom_id, [](std::ostream &out, const exprt &expr) -> std::ostream & { + out << "output"; + return out; + }); + REQUIRE(format_to_string(exprt{custom_id}) == "output"); +}