Skip to content

Commit 518ca3f

Browse files
committed
Java instanceof: avoid dereferencing null pointer
Previously our code was of the form tmp = x->@class_identifier is_instanceof = x != null && tmp == "A" || tmp == "B" || ... This was harmless as the value read from a null pointer was never used if it was null, but would present a spurious dereference of a possibly-null pointer, introducing false uncertainty about the vallue of `tmp`. Therefore we now generate: if x == null: is_instanceof = false else: tmp = x->@class_identifier is_instanceof = tmp == "A" || tmp == "B" || ...
1 parent 6882da6 commit 518ca3f

File tree

1 file changed

+89
-36
lines changed

1 file changed

+89
-36
lines changed

jbmc/src/java_bytecode/remove_instanceof.cpp

+89-36
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Chris Smowton, [email protected]
1313

1414
#include <goto-programs/class_hierarchy.h>
1515
#include <goto-programs/class_identifier.h>
16+
#include <goto-programs/goto_convert.h>
1617

1718
#include <util/fresh_symbol.h>
1819
#include <java_bytecode/java_types.h>
@@ -39,7 +40,7 @@ class remove_instanceoft
3940
namespacet ns;
4041
class_hierarchyt class_hierarchy;
4142

42-
std::size_t lower_instanceof(
43+
bool lower_instanceof(
4344
exprt &, goto_programt &, goto_programt::targett);
4445
};
4546

@@ -50,17 +51,17 @@ class remove_instanceoft
5051
/// \param goto_program: program the expression belongs to
5152
/// \param this_inst: instruction the expression is found at
5253
/// \return number of instanceof expressions that have been replaced
53-
std::size_t remove_instanceoft::lower_instanceof(
54+
bool remove_instanceoft::lower_instanceof(
5455
exprt &expr,
5556
goto_programt &goto_program,
5657
goto_programt::targett this_inst)
5758
{
5859
if(expr.id()!=ID_java_instanceof)
5960
{
60-
std::size_t replacements=0;
61+
bool changed = false;
6162
Forall_operands(it, expr)
62-
replacements+=lower_instanceof(*it, goto_program, this_inst);
63-
return replacements;
63+
changed |= lower_instanceof(*it, goto_program, this_inst);
64+
return changed;
6465
}
6566

6667
INVARIANT(
@@ -94,46 +95,93 @@ std::size_t remove_instanceoft::lower_instanceof(
9495
return a.compare(b) < 0;
9596
});
9697

97-
// Insert an instruction before the new check that assigns the clsid we're
98-
// checking for to a temporary, as GOTO program if-expressions should
99-
// not contain derefs.
100-
// We actually insert the assignment instruction after the existing one.
101-
// This will briefly be ill-formed (use before def of instanceof_tmp) but the
102-
// two will subsequently switch places. This makes sure that the inserted
103-
// assignement doesn't end up before any labels pointing at this instruction.
98+
// Make temporaries to store the class identifier (avoids repeated derefs) and
99+
// the instanceof result:
100+
104101
symbol_typet jlo=to_symbol_type(java_lang_object_type().subtype());
105-
exprt object_clsid=get_class_identifier_field(check_ptr, jlo, ns);
102+
exprt object_clsid = get_class_identifier_field(check_ptr, jlo, ns);
106103

107-
symbolt &newsym = get_fresh_aux_symbol(
104+
symbolt &clsid_tmp_sym = get_fresh_aux_symbol(
108105
object_clsid.type(),
109106
id2string(this_inst->function),
110-
"instanceof_tmp",
107+
"class_identifier_tmp",
108+
source_locationt(),
109+
ID_java,
110+
symbol_table);
111+
112+
symbolt &instanceof_result_sym = get_fresh_aux_symbol(
113+
bool_typet(),
114+
id2string(this_inst->function),
115+
"instanceof_result_tmp",
111116
source_locationt(),
112117
ID_java,
113118
symbol_table);
114119

115-
auto newinst=goto_program.insert_after(this_inst);
116-
newinst->make_assignment();
117-
newinst->code=code_assignt(newsym.symbol_expr(), object_clsid);
118-
newinst->source_location=this_inst->source_location;
119-
newinst->function=this_inst->function;
120+
// Create
121+
// if(expr == null)
122+
// instanceof_result = false;
123+
// else
124+
// string clsid = expr->@class_identifier
125+
// instanceof_result = clsid == "A" || clsid == "B" || ...
120126

121-
// Replace the instanceof construct with a conjunction of non-null and the
122-
// disjunction of all possible object types. According to the Java
123-
// specification, null instanceof T is false for all possible values of T.
127+
// According to the Java specification, null instanceof T is false for all
128+
// possible values of T.
124129
// (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2)
125-
notequal_exprt non_null_expr(
126-
check_ptr, null_pointer_exprt(to_pointer_type(check_ptr.type())));
130+
131+
code_ifthenelset is_null_branch;
132+
is_null_branch.cond() =
133+
equal_exprt(
134+
check_ptr, null_pointer_exprt(to_pointer_type(check_ptr.type())));
135+
is_null_branch.then_case() =
136+
code_assignt(instanceof_result_sym.symbol_expr(), false_exprt());
137+
138+
code_blockt else_block;
139+
else_block.add(code_declt(clsid_tmp_sym.symbol_expr()));
140+
else_block.add(code_assignt(clsid_tmp_sym.symbol_expr(), object_clsid));
141+
127142
exprt::operandst or_ops;
128143
for(const auto &clsname : children)
129144
{
130145
constant_exprt clsexpr(clsname, string_typet());
131-
equal_exprt test(newsym.symbol_expr(), clsexpr);
146+
equal_exprt test(clsid_tmp_sym.symbol_expr(), clsexpr);
132147
or_ops.push_back(test);
133148
}
134-
expr = and_exprt(non_null_expr, disjunction(or_ops));
149+
else_block.add(
150+
code_assignt(instanceof_result_sym.symbol_expr(), disjunction(or_ops)));
151+
152+
is_null_branch.else_case() = std::move(else_block);
153+
154+
// Replace the instanceof construct with instanceof_result:
155+
expr = instanceof_result_sym.symbol_expr();
156+
157+
null_message_handlert replaceme;
158+
159+
// Insert the new test block before it:
160+
goto_programt new_check_program;
161+
goto_convert(
162+
is_null_branch,
163+
symbol_table,
164+
new_check_program,
165+
replaceme,
166+
ID_java);
167+
168+
goto_program.destructive_insert(this_inst, new_check_program);
135169

136-
return 1;
170+
return true;
171+
}
172+
173+
static bool contains_instanceof(const exprt &e)
174+
{
175+
if(e.id() == ID_java_instanceof)
176+
return true;
177+
178+
for(const exprt &subexpr : e.operands())
179+
{
180+
if(contains_instanceof(subexpr))
181+
return true;
182+
}
183+
184+
return false;
137185
}
138186

139187
/// Replaces expressions like e instanceof A with e.\@class_identifier == "A"
@@ -146,15 +194,20 @@ bool remove_instanceoft::lower_instanceof(
146194
goto_programt &goto_program,
147195
goto_programt::targett target)
148196
{
149-
std::size_t replacements=
150-
lower_instanceof(target->code, goto_program, target)+
151-
lower_instanceof(target->guard, goto_program, target);
197+
if(target->is_target() &&
198+
(contains_instanceof(target->code) || contains_instanceof(target->guard)))
199+
{
200+
// If this is a branch target, add a skip beforehand so we can splice new
201+
// GOTO programs before the target instruction without inserting into the
202+
// wrong basic block.
203+
goto_program.insert_before_swap(target);
204+
target->make_skip();
205+
// Actually alter the now-moved instruction:
206+
++target;
207+
}
152208

153-
if(replacements==0)
154-
return false;
155-
// Swap the original instruction with the last assignment added after it
156-
target->swap(*std::next(target, replacements));
157-
return true;
209+
return lower_instanceof(target->code, goto_program, target) |
210+
lower_instanceof(target->guard, goto_program, target);
158211
}
159212

160213
/// Replace every instanceof in the passed function body with an explicit

0 commit comments

Comments
 (0)