Skip to content

Commit 4e7f03b

Browse files
committed
Add null check on virtual function calls after its subexpressions
For example, for "x->y . f()" we should `assert(x != null); assert(x->y != null);`, not the other way around.
1 parent c752d9d commit 4e7f03b

File tree

7 files changed

+124
-3
lines changed

7 files changed

+124
-3
lines changed

jbmc/src/java_bytecode/java_bytecode_instrument.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ void java_bytecode_instrumentt::instrument_code(codet &code)
445445
const java_method_typet &function_type =
446446
to_java_method_type(code_function_call.function().type());
447447

448+
for(const auto &arg : code_function_call.arguments())
449+
add_expr_instrumentation(block, arg);
450+
448451
// Check for a null this-argument of a virtual call:
449452
if(function_type.has_this())
450453
{
@@ -453,9 +456,6 @@ void java_bytecode_instrumentt::instrument_code(codet &code)
453456
code_function_call.source_location()));
454457
}
455458

456-
for(const auto &arg : code_function_call.arguments())
457-
add_expr_instrumentation(block, arg);
458-
459459
prepend_instrumentation(code, block);
460460
}
461461

jbmc/unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SRC += java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp \
1919
java_bytecode/java_bytecode_convert_method/convert_initalizers.cpp \
2020
java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp \
2121
java_bytecode/java_bytecode_convert_method/convert_method.cpp \
22+
java_bytecode/java_bytecode_instrument/virtual_call_null_checks.cpp \
2223
java_bytecode/java_bytecode_parse_generics/parse_bounded_generic_inner_classes.cpp \
2324
java_bytecode/java_bytecode_parse_generics/parse_derived_generic_class.cpp \
2425
java_bytecode/java_bytecode_parse_generics/parse_functions_with_generics.cpp \
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
public class VirtualCallNullChecks {
2+
3+
public static void main() {
4+
5+
A a = new A();
6+
a.field.virtualmethod();
7+
8+
}
9+
10+
}
11+
12+
class A {
13+
14+
public B field;
15+
16+
public A() {
17+
field = new B();
18+
}
19+
20+
}
21+
22+
class B {
23+
24+
public void virtualmethod() { }
25+
26+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*******************************************************************\
2+
3+
Module: Unit test to check Java virtual calls via a pointer
4+
yield a correct sequence of not-null assumptions.
5+
6+
Author: Diffblue Limited.
7+
8+
\*******************************************************************/
9+
10+
#include <testing-utils/catch.hpp>
11+
#include <testing-utils/message.h>
12+
#include <java-testing-utils/load_java_class.h>
13+
14+
#include <analyses/local_safe_pointers.h>
15+
#include <goto-programs/goto_convert_functions.h>
16+
#include <java_bytecode/java_bytecode_language.h>
17+
#include <util/expr_iterator.h>
18+
19+
SCENARIO(
20+
"java_bytecode_virtual_call_null_checks",
21+
"[core][java_bytecode][java_bytecode_instrument]")
22+
{
23+
GIVEN("A class that makes a virtual call via a pointer")
24+
{
25+
symbol_tablet symbol_table = load_java_class(
26+
"VirtualCallNullChecks", "./java_bytecode/java_bytecode_instrument");
27+
namespacet ns(symbol_table);
28+
goto_functionst goto_functions;
29+
goto_convert(symbol_table, goto_functions, null_message_handler);
30+
31+
WHEN("The virtual call is converted")
32+
{
33+
const auto &main_function =
34+
goto_functions.function_map.at("java::VirtualCallNullChecks.main:()V");
35+
36+
THEN("The callee should be of the form some_ptr->field")
37+
{
38+
bool found_virtual_call = false;
39+
40+
for(const auto &instruction : main_function.body.instructions)
41+
{
42+
if(instruction.type == FUNCTION_CALL)
43+
{
44+
const auto &virtual_call = to_code_function_call(instruction.code);
45+
if(virtual_call.function().id() == ID_virtual_function)
46+
{
47+
REQUIRE(virtual_call.arguments().size() == 1);
48+
const auto &this_argument = virtual_call.arguments()[0];
49+
REQUIRE(this_argument.id() == ID_member);
50+
REQUIRE(this_argument.op0().id() == ID_dereference);
51+
found_virtual_call = true;
52+
}
53+
}
54+
}
55+
56+
REQUIRE(found_virtual_call);
57+
}
58+
THEN("All pointer usages should be safe")
59+
{
60+
local_safe_pointerst safe_pointers(ns);
61+
safe_pointers(main_function.body);
62+
63+
for(auto instrit = main_function.body.instructions.begin(),
64+
instrend = main_function.body.instructions.end();
65+
instrit != instrend; ++instrit)
66+
{
67+
for(auto it = instrit->code.depth_begin(),
68+
itend = instrit->code.depth_end();
69+
it != itend; ++it)
70+
{
71+
if(it->id() == ID_dereference)
72+
{
73+
const auto &deref = to_dereference_expr(*it);
74+
REQUIRE(
75+
safe_pointers.is_safe_dereference(deref, instrit));
76+
}
77+
}
78+
79+
for(auto it = instrit->guard.depth_begin(),
80+
itend = instrit->guard.depth_end();
81+
it != itend; ++it)
82+
{
83+
if(it->id() == ID_dereference)
84+
{
85+
const auto &deref = to_dereference_expr(*it);
86+
REQUIRE(
87+
safe_pointers.is_safe_dereference(deref, instrit));
88+
}
89+
}
90+
}
91+
}
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)