Skip to content

Commit 54a3a1b

Browse files
author
owen-jones-diffblue
authored
Merge pull request diffblue#326 from diffblue/owen-jones-diffblue/fix-precise-evs-array-crash
SEC-203: Fix crash with precise array access
2 parents 667cb0f + 387334d commit 54a3a1b

File tree

6 files changed

+91
-52
lines changed

6 files changed

+91
-52
lines changed
Binary file not shown.
Binary file not shown.
271 Bytes
Binary file not shown.

regression/LVSA/TestPreciseAccessPaths/Test.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public static class A {
1212
Object object;
1313
}
1414

15-
// Precise access paths should work fine
1615
public void field_setter(A parameter_a) {
1716
output = parameter_a.object;
1817
}
@@ -24,7 +23,6 @@ public void apply_field_setter() {
2423
output_a = dynamic_object_a;
2524
}
2625

27-
// Precise access paths should work fine
2826
public Object field_getter() {
2927
return a1.object;
3028
}
@@ -33,7 +31,6 @@ public Object apply_field_getter() {
3331
return field_getter();
3432
}
3533

36-
// Precise access paths should work fine
3734
public void set_field_of_external_object(
3835
A parameter_a, Object parameter_object) {
3936
parameter_a.object = parameter_object;
@@ -44,7 +41,6 @@ public void apply_set_field_of_external_object(
4441
set_field_of_external_object(parameter_a, parameter_object);
4542
}
4643

47-
// Precise access paths should work fine
4844
public void conditionally_set_field_of_external_object(
4945
A parameter_a, Object parameter_object, boolean b) {
5046
if (b) {
@@ -58,13 +54,11 @@ public void apply_conditionally_set_field_of_external_object(
5854
parameter_a, parameter_object, b);
5955
}
6056

61-
// Precise access paths should work fine
6257
public void read_field_before_writing_to_aliasable_field() {
6358
output = a2.object;
6459
a1.object = new Object();
6560
}
6661

67-
// Precise access paths should work fine
6862
public void apply_read_field_before_writing_to_aliasable_field() {
6963
read_field_before_writing_to_aliasable_field();
7064
}
@@ -86,7 +80,6 @@ public void allocate_new_object(A input_A) {
8680
input_A.object = new Object();
8781
}
8882

89-
// Precise access paths should work fine
9083
public void call_function_to_allocate_new_object(A param_A) {
9184
allocate_new_object(param_A);
9285
output = param_A.object;
@@ -119,13 +112,20 @@ public static class B {
119112
A a;
120113
}
121114

122-
// Precise access paths should work fine
123-
public void two_derefs(B parameter_b) {
115+
public void two_field_derefs(B parameter_b) {
124116
output = parameter_b.a.object;
125117
}
126118

127-
public void apply_two_derefs(B parameter_b) {
128-
two_derefs(parameter_b);
119+
public void apply_two_field_derefs(B parameter_b) {
120+
two_field_derefs(parameter_b);
121+
}
122+
123+
public void array_deref(Object object_array[]) {
124+
output = object_array[0];
125+
}
126+
127+
public void apply_array_deref(Object object_array[]) {
128+
array_deref(object_array);
129129
}
130130

131131
public void simple_function_should_export_precise_access_paths(A param_a) {

regression/LVSA/TestPreciseAccessPaths/test_precise_access_paths.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ def test_apply_get_list_node(tmpdir):
206206
value_set_expectation.check_contains_per_field_evs()
207207

208208

209-
def test_two_derefs(tmpdir):
210-
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('two_derefs')
209+
def test_two_field_derefs(tmpdir):
210+
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('two_field_derefs')
211211
lvsa_expectation = lvsa_driver.run()
212212

213213
value_set_expectation = lvsa_expectation.get_value_set_for_output()
@@ -217,8 +217,8 @@ def test_two_derefs(tmpdir):
217217
value_set_expectation.check_contains_per_field_evs(access_path=['.object'])
218218

219219

220-
def test_apply_two_derefs(tmpdir):
221-
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('apply_two_derefs')
220+
def test_apply_two_field_derefs(tmpdir):
221+
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('apply_two_field_derefs')
222222
lvsa_expectation = lvsa_driver.run()
223223

224224
value_set_expectation = lvsa_expectation.get_value_set_for_output()
@@ -227,6 +227,27 @@ def test_apply_two_derefs(tmpdir):
227227
value_set_expectation.check_contains_per_field_evs(access_path=['.object'])
228228

229229

230+
def test_array_deref(tmpdir):
231+
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('array_deref')
232+
lvsa_expectation = lvsa_driver.run()
233+
234+
value_set_expectation = lvsa_expectation.get_value_set_for_output()
235+
236+
value_set_expectation.check_number_of_values(2)
237+
value_set_expectation.check_contains_precise_evs(access_path=['.data', '[]'])
238+
value_set_expectation.check_contains_per_field_evs(access_path=['[]'])
239+
240+
241+
def test_apply_array_deref(tmpdir):
242+
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('apply_array_deref')
243+
lvsa_expectation = lvsa_driver.run()
244+
245+
value_set_expectation = lvsa_expectation.get_value_set_for_output()
246+
247+
value_set_expectation.check_number_of_values(1)
248+
value_set_expectation.check_contains_per_field_evs(access_path=['[]'])
249+
250+
230251
def test_check_using_precise_access_paths(tmpdir):
231252
lvsa_driver = LvsaDriver(tmpdir, folder_name).with_test_function('check_using_precise_access_paths')
232253
lvsa_expectation = lvsa_driver.run()

src/pointer-analysis/local_value_set_analysis.cpp

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <pointer-analysis/dynamic_object_name.h>
1919
#include <util/infix.h>
20+
#include <java_bytecode/java_types.h>
2021

2122
// Non-static functions are documented in the header file,
2223
// local_value_set_analysis.h
@@ -202,56 +203,73 @@ static const exprt get_expr_from_root_object(
202203
}
203204

204205
/// Second, follow access path (if non-empty)
205-
for(const auto &access_path_entry : access_path_entries)
206+
for(external_value_set_exprt::access_path_entriest::const_iterator it =
207+
access_path_entries.begin();
208+
it != access_path_entries.end();
209+
++it)
206210
{
207-
std::string access_path_label = id2string(access_path_entry.label());
211+
std::string access_path_label = id2string(it->label());
208212
/// If expr is of type Z and Z <: Y <: X <: ... <: A and we are
209213
/// accessing a field of A then access_path_label will be
210-
/// ".@Y.@X.@W. ... [email protected]_name"
214+
/// ".@Y.@X.@W. ... [email protected]_name". For an array dereference, the access
215+
/// path will have the label "[]", and the access path entry before
216+
/// it will have the label ".data".
211217
DATA_INVARIANT(
212218
access_path_label.size() >= 2,
213219
"Access path label must have length at least 2");
214220

215-
expr=dereference_exprt(expr);
216-
const struct_typet &struct_type=to_struct_type(ns.follow(expr.type()));
217-
218-
/// First deal with any superclass identifiers
219-
while(access_path_label[1] == '@')
221+
if(access_path_label == "[]")
220222
{
221223
DATA_INVARIANT(
222-
access_path_label[0] == '.',
223-
"the label of an access path entry is malformed");
224-
access_path_label = access_path_label.substr(1);
225-
INVARIANT(
226-
!has_prefix(access_path_label, "@lock") &&
224+
std::prev(it)->label() == ".data",
225+
"Access path entry '[]' must be preceded by access path entry '.data'");
226+
expr = dereference_exprt(
227+
plus_exprt(
228+
expr, side_effect_expr_nondett(java_int_type()), expr.type()));
229+
}
230+
else
231+
{
232+
expr=dereference_exprt(expr);
233+
const struct_typet &struct_type=to_struct_type(ns.follow(expr.type()));
234+
235+
/// First deal with any superclass identifiers
236+
while(access_path_label[1]=='@')
237+
{
238+
DATA_INVARIANT(
239+
access_path_label[0]=='.',
240+
"the label of an access path entry is malformed");
241+
access_path_label=access_path_label.substr(1);
242+
INVARIANT(
243+
!has_prefix(access_path_label, "@lock") &&
227244
!has_prefix(access_path_label, "@class_identifier"),
228-
"Special fields @lock and @class_identifier should not be "
229-
"appearing in function summary");
245+
"Special fields @lock and @class_identifier should not be "
246+
"appearing in function summary");
247+
248+
const auto expr_components=struct_type.components();
249+
DATA_INVARIANT(
250+
!expr_components.empty(),
251+
"Cannot follow access path if expression has no components");
252+
const auto &superclass_component=expr_components[0];
253+
const std::string &superclass_name=
254+
id2string(superclass_component.get_name());
255+
DATA_INVARIANT(
256+
has_prefix(access_path_label, superclass_name),
257+
"Access path label starting with @ must be followed by superclass "
258+
"name");
259+
expr=member_exprt(expr, superclass_component);
260+
access_path_label=access_path_label.substr(superclass_name.size());
261+
}
230262

231-
const auto expr_components = struct_type.components();
263+
/// Now we are just left with a field name
232264
DATA_INVARIANT(
233-
!expr_components.empty(),
234-
"Cannot follow access path if expression has no components");
235-
const auto &superclass_component = expr_components[0];
236-
const std::string &superclass_name =
237-
id2string(superclass_component.get_name());
238-
DATA_INVARIANT(
239-
has_prefix(access_path_label, superclass_name),
240-
"Access path label starting with @ must be followed by superclass "
241-
"name");
242-
expr = member_exprt(expr, superclass_component);
243-
access_path_label = access_path_label.substr(superclass_name.size());
265+
access_path_label[0]=='.',
266+
"the label of an access path entry is malformed");
267+
access_path_label=access_path_label.substr(1);
268+
auto &component=struct_type.get_component(access_path_label);
269+
INVARIANT(
270+
component.get_name()!=ID_nil, "Could not find field from access path");
271+
expr=member_exprt(expr, component);
244272
}
245-
246-
/// Now we are just left with a field name
247-
DATA_INVARIANT(
248-
access_path_label[0] == '.',
249-
"the label of an access path entry is malformed");
250-
access_path_label = access_path_label.substr(1);
251-
auto &component = struct_type.get_component(access_path_label);
252-
INVARIANT(
253-
component.get_name() != ID_nil, "Could not find field from access path");
254-
expr = member_exprt(expr, component);
255273
}
256274

257275
return expr;

0 commit comments

Comments
 (0)