Skip to content

Commit 569c854

Browse files
author
Joel Allred
authored
Merge pull request #2489 from diffblue/fix-empty-indexset
[TG-3912] Sanitize string_constraintt bounds
2 parents 993735b + e5e5e9e commit 569c854

11 files changed

+146
-67
lines changed

src/solvers/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ SRC = $(BOOLEFORCE_SRC) \
166166
refinement/string_builtin_function.cpp \
167167
refinement/string_refinement.cpp \
168168
refinement/string_refinement_util.cpp \
169+
refinement/string_constraint.cpp \
169170
refinement/string_constraint_generator_code_points.cpp \
170171
refinement/string_constraint_generator_comparison.cpp \
171172
refinement/string_constraint_generator_concat.cpp \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*******************************************************************\
2+
3+
Module: String solver
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include "string_constraint.h"
10+
11+
#include <solvers/sat/satcheck.h>
12+
#include <util/symbol_table.h>
13+
14+
/// Runs a solver instance to verify whether an expression can only be
15+
// non-negative.
16+
/// \param expr: the expression to check for negativity
17+
/// \return true if `expr < 0` is unsatisfiable, false otherwise
18+
static bool cannot_be_neg(const exprt &expr)
19+
{
20+
satcheck_no_simplifiert sat_check;
21+
symbol_tablet symbol_table;
22+
namespacet ns(symbol_table);
23+
boolbvt solver(ns, sat_check);
24+
const exprt zero = from_integer(0, expr.type());
25+
const binary_relation_exprt non_neg(expr, ID_lt, zero);
26+
solver << non_neg;
27+
return solver() == decision_proceduret::resultt::D_UNSATISFIABLE;
28+
}
29+
30+
string_constraintt::string_constraintt(
31+
const symbol_exprt &_univ_var,
32+
const exprt &lower_bound,
33+
const exprt &upper_bound,
34+
const exprt &body)
35+
: univ_var(_univ_var),
36+
lower_bound(lower_bound),
37+
upper_bound(upper_bound),
38+
body(body)
39+
{
40+
INVARIANT(
41+
cannot_be_neg(lower_bound),
42+
"String constraints must have non-negative lower bound.\n" +
43+
lower_bound.pretty());
44+
INVARIANT(
45+
cannot_be_neg(upper_bound),
46+
"String constraints must have non-negative upper bound.\n" +
47+
upper_bound.pretty());
48+
}

src/solvers/refinement/string_constraint.h

+4-10
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,10 @@ class string_constraintt final
6868
string_constraintt() = delete;
6969

7070
string_constraintt(
71-
symbol_exprt _univ_var,
72-
exprt lower_bound,
73-
exprt upper_bound,
74-
exprt body)
75-
: univ_var(_univ_var),
76-
lower_bound(lower_bound),
77-
upper_bound(upper_bound),
78-
body(body)
79-
{
80-
}
71+
const symbol_exprt &_univ_var,
72+
const exprt &lower_bound,
73+
const exprt &upper_bound,
74+
const exprt &body);
8175

8276
// Default bound inferior is 0
8377
string_constraintt(symbol_exprt univ_var, exprt upper_bound, exprt body)

src/solvers/refinement/string_constraint_generator.h

+1
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,5 @@ exprt length_constraint_for_insert(
458458
const array_string_exprt &s1,
459459
const array_string_exprt &s2);
460460

461+
exprt zero_if_negative(const exprt &expr);
461462
#endif

src/solvers/refinement/string_constraint_generator_comparison.cpp

+35-23
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,33 @@ exprt string_constraint_generatort::add_axioms_for_equals(
3939

4040
typet index_type=s1.length().type();
4141

42-
43-
implies_exprt a1(eq, equal_exprt(s1.length(), s2.length()));
44-
lemmas.push_back(a1);
45-
46-
symbol_exprt qvar=fresh_univ_index("QA_equal", index_type);
47-
string_constraintt a2(
48-
qvar, s1.length(), implies_exprt(eq, equal_exprt(s1[qvar], s2[qvar])));
49-
constraints.push_back(a2);
50-
51-
symbol_exprt witness=fresh_exist_index("witness_unequal", index_type);
52-
exprt zero=from_integer(0, index_type);
53-
and_exprt bound_witness(
54-
binary_relation_exprt(witness, ID_lt, s1.length()),
55-
binary_relation_exprt(witness, ID_ge, zero));
56-
and_exprt witnessing(bound_witness, notequal_exprt(s1[witness], s2[witness]));
57-
and_exprt diff_length(
58-
notequal_exprt(s1.length(), s2.length()),
59-
equal_exprt(witness, from_integer(-1, index_type)));
60-
implies_exprt a3(not_exprt(eq), or_exprt(diff_length, witnessing));
61-
lemmas.push_back(a3);
42+
// Axiom 1.
43+
lemmas.push_back(implies_exprt(eq, equal_exprt(s1.length(), s2.length())));
44+
45+
// Axiom 2.
46+
constraints.push_back([&] {
47+
const symbol_exprt qvar = fresh_univ_index("QA_equal", index_type);
48+
return string_constraintt(
49+
qvar,
50+
zero_if_negative(s1.length()),
51+
implies_exprt(eq, equal_exprt(s1[qvar], s2[qvar])));
52+
}());
53+
54+
// Axiom 3.
55+
lemmas.push_back([&] {
56+
const symbol_exprt witness =
57+
fresh_exist_index("witness_unequal", index_type);
58+
const exprt zero = from_integer(0, index_type);
59+
const and_exprt bound_witness(
60+
binary_relation_exprt(witness, ID_lt, s1.length()),
61+
binary_relation_exprt(witness, ID_ge, zero));
62+
const and_exprt witnessing(
63+
bound_witness, notequal_exprt(s1[witness], s2[witness]));
64+
const and_exprt diff_length(
65+
notequal_exprt(s1.length(), s2.length()),
66+
equal_exprt(witness, from_integer(-1, index_type)));
67+
return implies_exprt(not_exprt(eq), or_exprt(diff_length, witnessing));
68+
}());
6269

6370
return tc_eq;
6471
}
@@ -132,7 +139,8 @@ exprt string_constraint_generatort::add_axioms_for_equals_ignore_case(
132139
fresh_univ_index("QA_equal_ignore_case", index_type);
133140
const exprt constr2 =
134141
character_equals_ignore_case(s1[qvar], s2[qvar], char_a, char_A, char_Z);
135-
const string_constraintt a2(qvar, s1.length(), implies_exprt(eq, constr2));
142+
const string_constraintt a2(
143+
qvar, zero_if_negative(s1.length()), implies_exprt(eq, constr2));
136144
constraints.push_back(a2);
137145

138146
const symbol_exprt witness =
@@ -226,7 +234,9 @@ exprt string_constraint_generatort::add_axioms_for_compare_to(
226234

227235
const symbol_exprt i = fresh_univ_index("QA_compare_to", index_type);
228236
const string_constraintt a2(
229-
i, s1.length(), implies_exprt(res_null, equal_exprt(s1[i], s2[i])));
237+
i,
238+
zero_if_negative(s1.length()),
239+
implies_exprt(res_null, equal_exprt(s1[i], s2[i])));
230240
constraints.push_back(a2);
231241

232242
const symbol_exprt x = fresh_exist_index("index_compare_to", index_type);
@@ -257,7 +267,9 @@ exprt string_constraint_generatort::add_axioms_for_compare_to(
257267

258268
const symbol_exprt i2 = fresh_univ_index("QA_compare_to", index_type);
259269
const string_constraintt a4(
260-
i2, x, implies_exprt(not_exprt(res_null), equal_exprt(s1[i2], s2[i2])));
270+
i2,
271+
zero_if_negative(x),
272+
implies_exprt(not_exprt(res_null), equal_exprt(s1[i2], s2[i2])));
261273
constraints.push_back(a4);
262274

263275
return res;

src/solvers/refinement/string_constraint_generator_concat.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,21 @@ exprt string_constraint_generatort::add_axioms_for_concat_substr(
5050
length_constraint_for_concat_substr(res, s1, s2, start_index, end_index));
5151

5252
// Axiom 2.
53-
constraints.push_back([&] { // NOLINT
53+
constraints.push_back([&] {
5454
const symbol_exprt idx =
5555
fresh_univ_index("QA_index_concat", res.length().type());
56-
return string_constraintt(idx, s1.length(), equal_exprt(s1[idx], res[idx]));
56+
return string_constraintt(
57+
idx, zero_if_negative(s1.length()), equal_exprt(s1[idx], res[idx]));
5758
}());
5859

5960
// Axiom 3.
60-
constraints.push_back([&] { // NOLINT
61+
constraints.push_back([&] {
6162
const symbol_exprt idx2 =
6263
fresh_univ_index("QA_index_concat2", res.length().type());
6364
const equal_exprt res_eq(
6465
res[plus_exprt(idx2, s1.length())], s2[plus_exprt(start1, idx2)]);
6566
const minus_exprt upper_bound(res.length(), s1.length());
66-
return string_constraintt(idx2, upper_bound, res_eq);
67+
return string_constraintt(idx2, zero_if_negative(upper_bound), res_eq);
6768
}());
6869

6970
return from_integer(0, get_return_code_type());
@@ -120,7 +121,8 @@ exprt string_constraint_generatort::add_axioms_for_concat_char(
120121
lemmas.push_back(length_constraint_for_concat_char(res, s1));
121122

122123
symbol_exprt idx = fresh_univ_index("QA_index_concat_char", index_type);
123-
string_constraintt a2(idx, s1.length(), equal_exprt(s1[idx], res[idx]));
124+
string_constraintt a2(
125+
idx, zero_if_negative(s1.length()), equal_exprt(s1[idx], res[idx]));
124126
constraints.push_back(a2);
125127

126128
equal_exprt a3(res[s1.length()], c);

src/solvers/refinement/string_constraint_generator_indexof.cpp

+11-10
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,21 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
5959
equal_exprt(str[index], c)));
6060
lemmas.push_back(a3);
6161

62-
const auto zero = from_integer(0, index_type);
63-
const if_exprt lower_bound(
64-
binary_relation_exprt(from_index, ID_le, zero), zero, from_index);
62+
const exprt lower_bound(zero_if_negative(from_index));
6563

6664
symbol_exprt n=fresh_univ_index("QA_index_of", index_type);
6765
string_constraintt a4(
68-
n, lower_bound, index, implies_exprt(contains, notequal_exprt(str[n], c)));
66+
n,
67+
lower_bound,
68+
zero_if_negative(index),
69+
implies_exprt(contains, notequal_exprt(str[n], c)));
6970
constraints.push_back(a4);
7071

7172
symbol_exprt m=fresh_univ_index("QA_index_of", index_type);
7273
string_constraintt a5(
7374
m,
7475
lower_bound,
75-
str.length(),
76+
zero_if_negative(str.length()),
7677
implies_exprt(not_exprt(contains), not_exprt(equal_exprt(str[m], c))));
7778
constraints.push_back(a5);
7879

@@ -128,7 +129,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string(
128129
symbol_exprt qvar=fresh_univ_index("QA_index_of_string", index_type);
129130
string_constraintt a3(
130131
qvar,
131-
needle.length(),
132+
zero_if_negative(needle.length()),
132133
implies_exprt(
133134
contains, equal_exprt(haystack[plus_exprt(qvar, offset)], needle[qvar])));
134135
constraints.push_back(a3);
@@ -221,7 +222,7 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of_string(
221222
symbol_exprt qvar=fresh_univ_index("QA_index_of_string", index_type);
222223
equal_exprt constr3(haystack[plus_exprt(qvar, offset)], needle[qvar]);
223224
const string_constraintt a3(
224-
qvar, needle.length(), implies_exprt(contains, constr3));
225+
qvar, zero_if_negative(needle.length()), implies_exprt(contains, constr3));
225226
constraints.push_back(a3);
226227

227228
// end_index is min(from_index, |str| - |substring|)
@@ -361,15 +362,15 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
361362
const symbol_exprt n = fresh_univ_index("QA_last_index_of1", index_type);
362363
const string_constraintt a4(
363364
n,
364-
plus_exprt(index, index1),
365-
end_index,
365+
zero_if_negative(plus_exprt(index, index1)),
366+
zero_if_negative(end_index),
366367
implies_exprt(contains, notequal_exprt(str[n], c)));
367368
constraints.push_back(a4);
368369

369370
const symbol_exprt m = fresh_univ_index("QA_last_index_of2", index_type);
370371
const string_constraintt a5(
371372
m,
372-
end_index,
373+
zero_if_negative(end_index),
373374
implies_exprt(not_exprt(contains), notequal_exprt(str[m], c)));
374375
constraints.push_back(a5);
375376

src/solvers/refinement/string_constraint_generator_insert.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ exprt string_constraint_generatort::add_axioms_for_insert(
5353
constraints.push_back([&] { // NOLINT
5454
const symbol_exprt i = fresh_symbol("QA_insert2", index_type);
5555
return string_constraintt(
56-
i, s2.length(), equal_exprt(res[plus_exprt(i, offset1)], s2[i]));
56+
i,
57+
zero_if_negative(s2.length()),
58+
equal_exprt(res[plus_exprt(i, offset1)], s2[i]));
5759
}());
5860

5961
// Axiom 4.
@@ -62,7 +64,7 @@ exprt string_constraint_generatort::add_axioms_for_insert(
6264
return string_constraintt(
6365
i,
6466
offset1,
65-
s1.length(),
67+
zero_if_negative(s1.length()),
6668
equal_exprt(res[plus_exprt(i, s2.length())], s1[i]));
6769
}());
6870

src/solvers/refinement/string_constraint_generator_main.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ void string_constraint_generatort::add_constraint_on_characters(
324324
const and_exprt char_in_set(
325325
binary_relation_exprt(chr, ID_ge, from_integer(low_char, chr.type())),
326326
binary_relation_exprt(chr, ID_le, from_integer(high_char, chr.type())));
327-
const string_constraintt sc(qvar, start, end, char_in_set);
327+
const string_constraintt sc(
328+
qvar, zero_if_negative(start), zero_if_negative(end), char_in_set);
328329
constraints.push_back(sc);
329330
}
330331

@@ -627,3 +628,11 @@ exprt maximum(const exprt &a, const exprt &b)
627628
{
628629
return if_exprt(binary_relation_exprt(a, ID_le, b), b, a);
629630
}
631+
632+
/// Returns a non-negative version of the argument.
633+
/// \param expr: expression of which we want a non-negative version
634+
/// \return `max(0, expr)`
635+
exprt zero_if_negative(const exprt &expr)
636+
{
637+
return maximum(from_integer(0, expr.type()), expr);
638+
}

src/solvers/refinement/string_constraint_generator_testing.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ exprt string_constraint_generatort::add_axioms_for_is_prefix(
5454
const symbol_exprt qvar = fresh_univ_index("QA_isprefix", index_type);
5555
const exprt body = implies_exprt(
5656
isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar]));
57-
return string_constraintt(qvar, prefix.length(), body);
57+
return string_constraintt(
58+
qvar, maximum(from_integer(0, index_type), prefix.length()), body);
5859
}());
5960

6061
// Axiom 3.
@@ -169,7 +170,7 @@ exprt string_constraint_generatort::add_axioms_for_is_suffix(
169170
const plus_exprt qvar_shifted(qvar, minus_exprt(s1.length(), s0.length()));
170171
string_constraintt a2(
171172
qvar,
172-
s0.length(),
173+
zero_if_negative(s0.length()),
173174
implies_exprt(issuffix, equal_exprt(s0[qvar], s1[qvar_shifted])));
174175
constraints.push_back(a2);
175176

@@ -239,7 +240,7 @@ exprt string_constraint_generatort::add_axioms_for_contains(
239240
const plus_exprt qvar_shifted(qvar, startpos);
240241
string_constraintt a4(
241242
qvar,
242-
s1.length(),
243+
zero_if_negative(s1.length()),
243244
implies_exprt(contains, equal_exprt(s1[qvar], s0[qvar_shifted])));
244245
constraints.push_back(a4);
245246

0 commit comments

Comments
 (0)