Skip to content

Commit 62ec461

Browse files
authored
Merge pull request diffblue#2360 from smowton/smowton/fix/dont-deref-null-for-class-identifier-v2
SEC-472 Java instanceof: avoid dereferencing null pointer
2 parents dca5b76 + 2369df3 commit 62ec461

File tree

8 files changed

+155
-67
lines changed

8 files changed

+155
-67
lines changed

jbmc/src/janalyzer/janalyzer_parse_options.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,9 @@ bool janalyzer_parse_optionst::process_goto_program(const optionst &options)
670670
remove_virtual_functions(goto_model);
671671
// remove Java throw and catch
672672
// This introduces instanceof, so order is important:
673-
remove_exceptions(goto_model);
673+
remove_exceptions(goto_model, get_message_handler());
674674
// remove rtti
675-
remove_instanceof(goto_model);
675+
remove_instanceof(goto_model, get_message_handler());
676676

677677
// do partial inlining
678678
status() << "Partial Inlining" << eom;

jbmc/src/java_bytecode/remove_exceptions.cpp

+19-7
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,12 @@ class remove_exceptionst
8888
explicit remove_exceptionst(
8989
symbol_table_baset &_symbol_table,
9090
function_may_throwt _function_may_throw,
91-
bool remove_added_instanceof)
91+
bool remove_added_instanceof,
92+
message_handlert &message_handler)
9293
: symbol_table(_symbol_table),
9394
function_may_throw(_function_may_throw),
94-
remove_added_instanceof(remove_added_instanceof)
95+
remove_added_instanceof(remove_added_instanceof),
96+
message_handler(message_handler)
9597
{
9698
}
9799

@@ -102,6 +104,7 @@ class remove_exceptionst
102104
symbol_table_baset &symbol_table;
103105
function_may_throwt function_may_throw;
104106
bool remove_added_instanceof;
107+
message_handlert &message_handler;
105108

106109
symbol_exprt get_inflight_exception_global();
107110

@@ -342,7 +345,7 @@ void remove_exceptionst::add_exception_dispatch_sequence(
342345
t_exc->guard=check;
343346

344347
if(remove_added_instanceof)
345-
remove_instanceof(t_exc, goto_program, symbol_table);
348+
remove_instanceof(t_exc, goto_program, symbol_table, message_handler);
346349
}
347350
}
348351
}
@@ -574,6 +577,7 @@ void remove_exceptionst::operator()(goto_programt &goto_program)
574577
void remove_exceptions(
575578
symbol_table_baset &symbol_table,
576579
goto_functionst &goto_functions,
580+
message_handlert &message_handler,
577581
remove_exceptions_typest type)
578582
{
579583
const namespacet ns(symbol_table);
@@ -586,7 +590,8 @@ void remove_exceptions(
586590
remove_exceptionst remove_exceptions(
587591
symbol_table,
588592
function_may_throw,
589-
type == remove_exceptions_typest::REMOVE_ADDED_INSTANCEOF);
593+
type == remove_exceptions_typest::REMOVE_ADDED_INSTANCEOF,
594+
message_handler);
590595
remove_exceptions(goto_functions);
591596
}
592597

@@ -600,12 +605,14 @@ void remove_exceptions(
600605
/// \param symbol_table: global symbol table. The `@inflight_exception` global
601606
/// may be added if not already present. It will not be initialised; that is
602607
/// the caller's responsibility.
608+
/// \param message_handler: logging output
603609
/// \param type: specifies whether instanceof operations generated by this pass
604610
/// should be lowered to class-identifier comparisons (using
605611
/// remove_instanceof).
606612
void remove_exceptions(
607613
goto_programt &goto_program,
608614
symbol_table_baset &symbol_table,
615+
message_handlert &message_handler,
609616
remove_exceptions_typest type)
610617
{
611618
remove_exceptionst::function_may_throwt any_function_may_throw =
@@ -614,12 +621,17 @@ void remove_exceptions(
614621
remove_exceptionst remove_exceptions(
615622
symbol_table,
616623
any_function_may_throw,
617-
type == remove_exceptions_typest::REMOVE_ADDED_INSTANCEOF);
624+
type == remove_exceptions_typest::REMOVE_ADDED_INSTANCEOF,
625+
message_handler);
618626
remove_exceptions(goto_program);
619627
}
620628

621629
/// removes throws/CATCH-POP/CATCH-PUSH
622-
void remove_exceptions(goto_modelt &goto_model, remove_exceptions_typest type)
630+
void remove_exceptions(
631+
goto_modelt &goto_model,
632+
message_handlert &message_handler,
633+
remove_exceptions_typest type)
623634
{
624-
remove_exceptions(goto_model.symbol_table, goto_model.goto_functions, type);
635+
remove_exceptions(
636+
goto_model.symbol_table, goto_model.goto_functions, message_handler, type);
625637
}

jbmc/src/java_bytecode/remove_exceptions.h

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Date: December 2016
1616

1717
#include <goto-programs/goto_model.h>
1818

19+
#include <util/message.h>
20+
1921
#define INFLIGHT_EXCEPTION_VARIABLE_BASENAME "@inflight_exception"
2022
#define INFLIGHT_EXCEPTION_VARIABLE_NAME \
2123
"java::" INFLIGHT_EXCEPTION_VARIABLE_BASENAME
@@ -32,11 +34,13 @@ enum class remove_exceptions_typest
3234
void remove_exceptions(
3335
goto_programt &goto_program,
3436
symbol_table_baset &symbol_table,
37+
message_handlert &message_handler,
3538
remove_exceptions_typest type =
3639
remove_exceptions_typest::DONT_REMOVE_INSTANCEOF);
3740

3841
void remove_exceptions(
3942
goto_modelt &goto_model,
43+
message_handlert &message_handler,
4044
remove_exceptions_typest type =
4145
remove_exceptions_typest::DONT_REMOVE_INSTANCEOF);
4246

jbmc/src/java_bytecode/remove_instanceof.cpp

+112-47
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>
@@ -22,8 +23,11 @@ Author: Chris Smowton, [email protected]
2223
class remove_instanceoft
2324
{
2425
public:
25-
explicit remove_instanceoft(symbol_table_baset &symbol_table)
26-
: symbol_table(symbol_table), ns(symbol_table)
26+
remove_instanceoft(
27+
symbol_table_baset &symbol_table, message_handlert &message_handler)
28+
: symbol_table(symbol_table)
29+
, ns(symbol_table)
30+
, message_handler(message_handler)
2731
{
2832
class_hierarchy(symbol_table);
2933
}
@@ -38,8 +42,9 @@ class remove_instanceoft
3842
symbol_table_baset &symbol_table;
3943
namespacet ns;
4044
class_hierarchyt class_hierarchy;
45+
message_handlert &message_handler;
4146

42-
std::size_t lower_instanceof(
47+
bool lower_instanceof(
4348
exprt &, goto_programt &, goto_programt::targett);
4449
};
4550

@@ -49,18 +54,18 @@ class remove_instanceoft
4954
/// \param expr: Expression to lower (the code or the guard of an instruction)
5055
/// \param goto_program: program the expression belongs to
5156
/// \param this_inst: instruction the expression is found at
52-
/// \return number of instanceof expressions that have been replaced
53-
std::size_t remove_instanceoft::lower_instanceof(
57+
/// \return true if any instanceof instructionw was replaced
58+
bool remove_instanceoft::lower_instanceof(
5459
exprt &expr,
5560
goto_programt &goto_program,
5661
goto_programt::targett this_inst)
5762
{
5863
if(expr.id()!=ID_java_instanceof)
5964
{
60-
std::size_t replacements=0;
65+
bool changed = false;
6166
Forall_operands(it, expr)
62-
replacements+=lower_instanceof(*it, goto_program, this_inst);
63-
return replacements;
67+
changed |= lower_instanceof(*it, goto_program, this_inst);
68+
return changed;
6469
}
6570

6671
INVARIANT(
@@ -94,46 +99,91 @@ std::size_t remove_instanceoft::lower_instanceof(
9499
return a.compare(b) < 0;
95100
});
96101

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.
102+
// Make temporaries to store the class identifier (avoids repeated derefs) and
103+
// the instanceof result:
104+
104105
symbol_typet jlo=to_symbol_type(java_lang_object_type().subtype());
105-
exprt object_clsid=get_class_identifier_field(check_ptr, jlo, ns);
106+
exprt object_clsid = get_class_identifier_field(check_ptr, jlo, ns);
106107

107-
symbolt &newsym = get_fresh_aux_symbol(
108+
symbolt &clsid_tmp_sym = get_fresh_aux_symbol(
108109
object_clsid.type(),
109110
id2string(this_inst->function),
110-
"instanceof_tmp",
111+
"class_identifier_tmp",
112+
source_locationt(),
113+
ID_java,
114+
symbol_table);
115+
116+
symbolt &instanceof_result_sym = get_fresh_aux_symbol(
117+
bool_typet(),
118+
id2string(this_inst->function),
119+
"instanceof_result_tmp",
111120
source_locationt(),
112121
ID_java,
113122
symbol_table);
114123

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;
124+
// Create
125+
// if(expr == null)
126+
// instanceof_result = false;
127+
// else
128+
// string clsid = expr->@class_identifier
129+
// instanceof_result = clsid == "A" || clsid == "B" || ...
120130

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.
131+
// According to the Java specification, null instanceof T is false for all
132+
// possible values of T.
124133
// (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())));
134+
135+
code_ifthenelset is_null_branch;
136+
is_null_branch.cond() =
137+
equal_exprt(
138+
check_ptr, null_pointer_exprt(to_pointer_type(check_ptr.type())));
139+
is_null_branch.then_case() =
140+
code_assignt(instanceof_result_sym.symbol_expr(), false_exprt());
141+
142+
code_blockt else_block;
143+
else_block.add(code_declt(clsid_tmp_sym.symbol_expr()));
144+
else_block.add(code_assignt(clsid_tmp_sym.symbol_expr(), object_clsid));
145+
127146
exprt::operandst or_ops;
128147
for(const auto &clsname : children)
129148
{
130149
constant_exprt clsexpr(clsname, string_typet());
131-
equal_exprt test(newsym.symbol_expr(), clsexpr);
150+
equal_exprt test(clsid_tmp_sym.symbol_expr(), clsexpr);
132151
or_ops.push_back(test);
133152
}
134-
expr = and_exprt(non_null_expr, disjunction(or_ops));
153+
else_block.add(
154+
code_assignt(instanceof_result_sym.symbol_expr(), disjunction(or_ops)));
155+
156+
is_null_branch.else_case() = std::move(else_block);
157+
158+
// Replace the instanceof construct with instanceof_result:
159+
expr = instanceof_result_sym.symbol_expr();
135160

136-
return 1;
161+
// Insert the new test block before it:
162+
goto_programt new_check_program;
163+
goto_convert(
164+
is_null_branch,
165+
symbol_table,
166+
new_check_program,
167+
message_handler,
168+
ID_java);
169+
170+
goto_program.destructive_insert(this_inst, new_check_program);
171+
172+
return true;
173+
}
174+
175+
static bool contains_instanceof(const exprt &e)
176+
{
177+
if(e.id() == ID_java_instanceof)
178+
return true;
179+
180+
for(const exprt &subexpr : e.operands())
181+
{
182+
if(contains_instanceof(subexpr))
183+
return true;
184+
}
185+
186+
return false;
137187
}
138188

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

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;
211+
return lower_instanceof(target->code, goto_program, target) |
212+
lower_instanceof(target->guard, goto_program, target);
158213
}
159214

160215
/// Replace every instanceof in the passed function body with an explicit
@@ -185,12 +240,14 @@ bool remove_instanceoft::lower_instanceof(goto_programt &goto_program)
185240
/// \param target: The instruction to work on.
186241
/// \param goto_program: The function body containing the instruction.
187242
/// \param symbol_table: The symbol table to add symbols to.
243+
/// \param message_handler: logging output
188244
void remove_instanceof(
189245
goto_programt::targett target,
190246
goto_programt &goto_program,
191-
symbol_table_baset &symbol_table)
247+
symbol_table_baset &symbol_table,
248+
message_handlert &message_handler)
192249
{
193-
remove_instanceoft rem(symbol_table);
250+
remove_instanceoft rem(symbol_table, message_handler);
194251
rem.lower_instanceof(goto_program, target);
195252
}
196253

@@ -199,11 +256,13 @@ void remove_instanceof(
199256
/// \remarks Extra auxiliary variables may be introduced into symbol_table.
200257
/// \param function: The function to work on.
201258
/// \param symbol_table: The symbol table to add symbols to.
259+
/// \param message_handler: logging output
202260
void remove_instanceof(
203261
goto_functionst::goto_functiont &function,
204-
symbol_table_baset &symbol_table)
262+
symbol_table_baset &symbol_table,
263+
message_handlert &message_handler)
205264
{
206-
remove_instanceoft rem(symbol_table);
265+
remove_instanceoft rem(symbol_table, message_handler);
207266
rem.lower_instanceof(function.body);
208267
}
209268

@@ -212,11 +271,13 @@ void remove_instanceof(
212271
/// \remarks Extra auxiliary variables may be introduced into symbol_table.
213272
/// \param goto_functions: The functions to work on.
214273
/// \param symbol_table: The symbol table to add symbols to.
274+
/// \param message_handler: logging output
215275
void remove_instanceof(
216276
goto_functionst &goto_functions,
217-
symbol_table_baset &symbol_table)
277+
symbol_table_baset &symbol_table,
278+
message_handlert &message_handler)
218279
{
219-
remove_instanceoft rem(symbol_table);
280+
remove_instanceoft rem(symbol_table, message_handler);
220281
bool changed=false;
221282
for(auto &f : goto_functions.function_map)
222283
changed=rem.lower_instanceof(f.second.body) || changed;
@@ -228,8 +289,12 @@ void remove_instanceof(
228289
/// class-identifier test.
229290
/// \remarks Extra auxiliary variables may be introduced into symbol_table.
230291
/// \param goto_model: The functions to work on and the symbol table to add
292+
/// \param message_handler: logging output
231293
/// symbols to.
232-
void remove_instanceof(goto_modelt &goto_model)
294+
void remove_instanceof(
295+
goto_modelt &goto_model,
296+
message_handlert &message_handler)
233297
{
234-
remove_instanceof(goto_model.goto_functions, goto_model.symbol_table);
298+
remove_instanceof(
299+
goto_model.goto_functions, goto_model.symbol_table, message_handler);
235300
}

0 commit comments

Comments
 (0)