Skip to content

Commit 6763669

Browse files
Correct starts_with for offset out of bound case
The builtin function was not clearly specified for the case where the offset argument is negative, or exceeding the size of the string, and was not resilient to overflows. We now enforce that if the offset is out of bounds then the return value is false. The axioms added were not correct in the case of offset
1 parent 71f80e1 commit 6763669

File tree

1 file changed

+38
-29
lines changed

1 file changed

+38
-29
lines changed

src/solvers/refinement/string_constraint_generator_testing.cpp

+38-29
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@ Author: Romain Brenguier, [email protected]
1515
#include <util/deprecate.h>
1616

1717
/// Add axioms stating that the returned expression is true exactly when the
18-
/// first string is a prefix of the second one, starting at position offset.
18+
/// offset is greater or equal to 0 and the first string is a prefix of the
19+
/// second one, starting at position offset.
1920
///
2021
/// These axioms are:
21-
/// 1. \f$ {\tt isprefix} \Rightarrow |str| \ge |{\tt prefix}|+offset \f$
22+
/// 1. \f$ {\tt isprefix} \Rightarrow {\tt offset_within_bounds}\f$
2223
/// 2. \f$ \forall 0 \le qvar<|{\tt prefix}|.\ {\tt isprefix}
2324
/// \Rightarrow s0[witness+{\tt offset}]=s2[witness] \f$
24-
/// 3. \f$ !{\tt isprefix} \Rightarrow |{\tt str}|<|{\tt prefix}|+{\tt offset}
25+
/// 3. \f$ (\lnot {\tt isprefix} \Rightarrow
26+
/// \lnot {\tt offset_within_bounds}
2527
/// \lor (0 \le witness<|{\tt prefix}|
26-
/// \land {\tt str}[witness+{\tt offset}] \ne {\tt prefix}[witness])\f$
27-
///
28+
/// \land {\tt str}[witness+{\tt offset}] \ne {\tt prefix}[witness])\f$
29+
/// where \f$ {\tt offset_within_bounds} \f$ is
30+
/// \f$ offset \ge 0 \land offset < |str| \land
31+
/// |str| - |{\tt prefix}| \ge offset \f$
2832
/// \param prefix: an array of characters
2933
/// \param str: an array of characters
3034
/// \param offset: an integer
@@ -34,34 +38,39 @@ exprt string_constraint_generatort::add_axioms_for_is_prefix(
3438
const array_string_exprt &str,
3539
const exprt &offset)
3640
{
37-
symbol_exprt isprefix=fresh_boolean("isprefix");
38-
const typet &index_type=str.length().type();
41+
const symbol_exprt isprefix = fresh_boolean("isprefix");
42+
const typet &index_type = str.length().type();
43+
const exprt offset_within_bounds = and_exprt(
44+
binary_relation_exprt(offset, ID_ge, from_integer(0, offset.type())),
45+
binary_relation_exprt(offset, ID_le, str.length()),
46+
binary_relation_exprt(
47+
minus_exprt(str.length(), offset), ID_ge, prefix.length()));
3948

4049
// Axiom 1.
41-
lemmas.push_back(
42-
implies_exprt(
43-
isprefix, str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))));
44-
45-
symbol_exprt qvar=fresh_univ_index("QA_isprefix", index_type);
46-
string_constraintt a2(
47-
qvar,
48-
prefix.length(),
49-
implies_exprt(
50-
isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar])));
51-
constraints.push_back(a2);
52-
53-
symbol_exprt witness=fresh_exist_index("witness_not_isprefix", index_type);
54-
and_exprt witness_diff(
55-
axiom_for_is_positive_index(witness),
56-
and_exprt(
50+
lemmas.push_back(implies_exprt(isprefix, offset_within_bounds));
51+
52+
// Axiom 2.
53+
constraints.push_back([&] {
54+
const symbol_exprt qvar = fresh_univ_index("QA_isprefix", index_type);
55+
const exprt body = implies_exprt(
56+
isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar]));
57+
return string_constraintt(qvar, prefix.length(), body);
58+
}());
59+
60+
// Axiom 3.
61+
lemmas.push_back([&] {
62+
const exprt witness = fresh_exist_index("witness_not_isprefix", index_type);
63+
const exprt strings_differ_at_witness = and_exprt(
64+
axiom_for_is_positive_index(witness),
5765
prefix.axiom_for_length_gt(witness),
58-
notequal_exprt(str[plus_exprt(witness, offset)], prefix[witness])));
59-
or_exprt s0_notpref_s1(
60-
not_exprt(str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))),
61-
witness_diff);
66+
notequal_exprt(str[plus_exprt(witness, offset)], prefix[witness]));
67+
const exprt s1_does_not_start_with_s0 = or_exprt(
68+
not_exprt(offset_within_bounds),
69+
not_exprt(str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))),
70+
strings_differ_at_witness);
71+
return implies_exprt(not_exprt(isprefix), s1_does_not_start_with_s0);
72+
}());
6273

63-
implies_exprt a3(not_exprt(isprefix), s0_notpref_s1);
64-
lemmas.push_back(a3);
6574
return isprefix;
6675
}
6776

0 commit comments

Comments
 (0)