Skip to content

Commit 2743821

Browse files
committed
JBMC: Support for synchronized methods
This commit adds support for synchronized methods by instrumenting all methods marked with the synchronization flag with calls to 'monitorenter' and 'monitorexit'. These two methods are located in the java models library and implement a critical section. To this end the following changes are made: 1. New irep_id, 'is_synchronized', to represent the synchronized keyword and appropriate changes to 'java_byecode_convert_method.cpp' to set this flag when a synchronized method is encountered. 2. Setting of the 'is_static' flag when the method in question is static. 3. Functions to find and instrument synchronized methods. Specifically, calls to "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V" and "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V" are respectively instrumented at the start and end of a synchronized method . Note, the former is instrumented at every point where the execution of the synchronized methods terminates. Specifically out of order return statements and exceptions. Note: instrumentation of synchronized methods is only triggered if the '--java-threading' command line option is specified. Note': instrumentation of synchronized methods requires the use of the java core models library as the locking mechanism is implemented in the model 'java.long.Object'. Note'': static synchronized methods are not supported yet.
1 parent 7f5b37c commit 2743821

File tree

6 files changed

+213
-11
lines changed

6 files changed

+213
-11
lines changed

jbmc/src/java_bytecode/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SRC = bytecode_info.cpp \
88
jar_file.cpp \
99
java_bytecode_convert_class.cpp \
1010
java_bytecode_convert_method.cpp \
11-
java_bytecode_convert_threadblock.cpp \
11+
java_bytecode_concurrency_instrumentation.cpp \
1212
java_bytecode_instrument.cpp \
1313
java_bytecode_internal_additions.cpp \
1414
java_bytecode_language.cpp \

jbmc/src/java_bytecode/java_bytecode_convert_threadblock.cpp renamed to jbmc/src/java_bytecode/java_bytecode_concurrency_instrumentation.cpp

Lines changed: 197 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ Author: Kurt Degiogrio, kurt.degiorgio@diffblue.com
66
77
\*******************************************************************/
88

9-
#include "java_bytecode_convert_threadblock.h"
9+
#include "java_bytecode_concurrency_instrumentation.h"
1010
#include "expr2java.h"
1111
#include "java_types.h"
1212

1313
#include <util/expr_iterator.h>
1414
#include <util/namespace.h>
1515
#include <util/cprover_prefix.h>
1616
#include <util/std_types.h>
17+
#include <util/fresh_symbol.h>
1718
#include <util/arith_tools.h>
1819

1920
// Disable linter to allow an std::string constant.
@@ -38,7 +39,7 @@ static symbolt add_or_get_symbol(
3839
const bool is_thread_local,
3940
const bool is_static_lifetime)
4041
{
41-
const symbolt* psymbol = nullptr;
42+
const symbolt *psymbol = nullptr;
4243
namespacet ns(symbol_table);
4344
ns.lookup(name, psymbol);
4445
if(psymbol != nullptr)
@@ -89,6 +90,141 @@ static const std::string get_thread_block_identifier(
8990
return integer2string(lbl_id);
9091
}
9192

93+
/// Creates a monitorenter/monitorexit code_function_callt for
94+
/// the given synchronization object.
95+
/// \param symbol_table: a symbol table
96+
/// \param is_enter: indicates whether we are creating a monitorenter or
97+
/// monitorexit.
98+
/// \param object: expression representing a 'java.Lang.Object'. This object is
99+
/// used to achieve object-level locking by calling monitoroenter/monitorexit.
100+
static codet get_monitor_call(
101+
const symbol_tablet &symbol_table,
102+
bool is_enter,
103+
const exprt &object)
104+
{
105+
const std::string symbol =
106+
is_enter ? "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V"
107+
: "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V";
108+
109+
auto it = symbol_table.symbols.find(symbol);
110+
111+
// If the 'java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V' or
112+
// 'java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V' method is not
113+
// found symex will rightfully complain as it cannot find the symbol
114+
// associated with the method in question. To avoid this and thereby ensuring
115+
// JBMC works both with and without the models, we replace the aforementioned
116+
// methods with skips when we cannot find them.
117+
//
118+
// Note: this can only happen if the java-core-models library is not loaded.
119+
//
120+
// Note': we explicitly prevent JBMC from stubbing these methods.
121+
if(it == symbol_table.symbols.end())
122+
return code_skipt();
123+
124+
// Otherwise we create a function call
125+
code_function_callt call;
126+
call.function() = symbol_exprt(symbol, it->second.type);
127+
call.lhs().make_nil();
128+
call.arguments().push_back(object);
129+
return call;
130+
}
131+
132+
/// Introduces a monitorexit before every return recursively
133+
/// \param [out] code: current element to modify
134+
/// \param monitorexit: codet to insert before the return
135+
static void monitor_exits(codet &code, const codet &monitorexit)
136+
{
137+
const irep_idt &statement = code.get_statement();
138+
if(statement == ID_return)
139+
{
140+
// Replace the return with a monitor exit plus return block
141+
code_blockt return_block;
142+
return_block.add(monitorexit);
143+
return_block.move_to_operands(code);
144+
code = return_block;
145+
}
146+
else if(
147+
statement == ID_label || statement == ID_block ||
148+
statement == ID_decl_block)
149+
{
150+
// If label or block found, explore the code inside the block
151+
Forall_operands(it, code)
152+
{
153+
codet &sub_code = to_code(*it);
154+
monitor_exits(sub_code, monitorexit);
155+
}
156+
}
157+
}
158+
159+
/// Transforms the codet stored in in the value field of \p symbol. This
160+
/// expected to be a codet that needs to be synchronized, the transformation
161+
/// makes it thread-safe as described in the documentation of function
162+
/// convert_synchronized_methods
163+
///
164+
/// The resulting thread-safe codet is stored in the value field of \p symbol.
165+
///
166+
/// \param symbol_table: a symbol_table
167+
/// \param symbol: writeable symbol hosting code to synchronized
168+
/// \param sync_object: object to use as a lock
169+
static void instrument_synchronized_code(
170+
symbol_tablet &symbol_table,
171+
symbolt &symbol,
172+
const exprt &sync_object)
173+
{
174+
codet &code = to_code(symbol.value);
175+
176+
// Get interposition code for the monitor lock/unlock
177+
codet monitorenter = get_monitor_call(symbol_table, true, sync_object);
178+
codet monitorexit = get_monitor_call(symbol_table, false, sync_object);
179+
180+
// Create a unique catch label and empty throw type (i.e any)
181+
// and catch-push them at the beginning of the code (i.e begin try)
182+
code_push_catcht catch_push;
183+
irep_idt handler("pc-synchronized-catch");
184+
irep_idt exception_id;
185+
code_push_catcht::exception_listt &exception_list =
186+
catch_push.exception_list();
187+
exception_list.push_back(
188+
code_push_catcht::exception_list_entryt(exception_id, handler));
189+
190+
// Create a catch-pop to indicate the end of the try block
191+
code_pop_catcht catch_pop;
192+
193+
// Create the finally block with the same label targeted in the catch-push
194+
const symbolt &tmp_symbol = get_fresh_aux_symbol(
195+
java_reference_type(symbol_typet("java::java.lang.Throwable")),
196+
id2string(symbol.name),
197+
"caught-synchronized-exception",
198+
code.source_location(),
199+
ID_java,
200+
symbol_table);
201+
symbol_exprt catch_var(tmp_symbol.name, tmp_symbol.type);
202+
catch_var.set(ID_C_base_name, tmp_symbol.base_name);
203+
code_landingpadt catch_statement(catch_var);
204+
codet catch_instruction = catch_statement;
205+
code_labelt catch_label(handler, code_blockt());
206+
code_blockt &catch_block = to_code_block(catch_label.code());
207+
catch_block.add(catch_instruction);
208+
catch_block.add(monitorexit);
209+
210+
// Re-throw exception
211+
side_effect_expr_throwt throw_expr;
212+
throw_expr.copy_to_operands(catch_var);
213+
catch_block.add(code_expressiont(throw_expr));
214+
215+
// Write a monitorexit before every return
216+
monitor_exits(code, monitorexit);
217+
218+
// Wrap the code into a try finally
219+
code_blockt try_block;
220+
try_block.move_to_operands(monitorenter);
221+
try_block.move_to_operands(catch_push);
222+
try_block.move_to_operands(code);
223+
try_block.move_to_operands(catch_pop);
224+
try_block.move_to_operands(catch_label);
225+
code = try_block;
226+
}
227+
92228
/// Transforms the codet stored in in \p f_code, which is a call to function
93229
/// CProver.startThread:(I)V into a block of code as described by the
94230
/// documentation of function convert_thread_block
@@ -284,20 +420,20 @@ static void instrument_getCurrentThreadID(
284420
///
285421
/// Note': the ID of the thread is assigned after the thread is created, this
286422
/// creates bogus interleavings. Currently, it's not possible to
287-
/// assign the thread ID before the creation of the thead, due to a bug in
423+
/// assign the thread ID before the creation of the thread, due to a bug in
288424
/// symex. See https://github.com/diffblue/cbmc/issues/1630/for more details.
289425
///
290426
/// \param symbol_table: a symbol table
291427
void convert_threadblock(symbol_tablet &symbol_table)
292428
{
293429
using instrument_callbackt =
294-
std::function<void(const code_function_callt&, codet&, symbol_tablet&)>;
430+
std::function<void(const code_function_callt&, codet&, symbol_tablet&)>;
295431
using expr_replacement_mapt =
296-
std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
432+
std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
297433

298434
namespacet ns(symbol_table);
299435

300-
for(auto entry : symbol_table)
436+
for(const auto &entry : symbol_table)
301437
{
302438
expr_replacement_mapt expr_replacement_map;
303439
const symbolt &symbol = entry.second;
@@ -361,3 +497,58 @@ void convert_threadblock(symbol_tablet &symbol_table)
361497
}
362498
}
363499
}
500+
501+
/// Iterate through the symbol table to find and appropriately instrument
502+
/// synchronized methods.
503+
///
504+
/// A synchronized method is a method with the synchronization keyword. Any
505+
/// code inside of such methods has to be thread-safe. To this end, a call to
506+
/// "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V" and
507+
/// "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V" is instrumented
508+
/// at the start and end of the method. Note, that the former is instrumented
509+
/// at every statement where the execution can exit the method in question.
510+
/// Specifically, out of order return statements and exceptions. The latter
511+
/// is dealt with by instrumenting a try catch block.
512+
///
513+
/// Note: current version cannot handle static methods.
514+
///
515+
/// Note': This method requires the availability of
516+
/// "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V" and
517+
/// "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V".
518+
/// (java-library-models)
519+
///
520+
/// \param symbol_table: a symbol table
521+
void convert_synchronized_methods(symbol_tablet &symbol_table)
522+
{
523+
for(const auto &entry : symbol_table)
524+
{
525+
const symbolt &symbol = entry.second;
526+
527+
if(symbol.type.id() != ID_code)
528+
continue;
529+
if(symbol.value.is_nil())
530+
continue;
531+
if(symbol.type.get(ID_is_synchronized) != "1")
532+
continue;
533+
534+
bool is_static = (symbol.type.get(ID_is_static) == "1");
535+
if(is_static)
536+
// FIXME: handle static synchronized methods
537+
continue;
538+
539+
// find the object to synchronize on
540+
const irep_idt this_id(id2string(symbol.name) + "::this");
541+
exprt this_expr(this_id);
542+
543+
auto it = symbol_table.symbols.find(this_id);
544+
545+
if(it == symbol_table.symbols.end())
546+
// failed to find object to synchronize on
547+
continue;
548+
549+
// get writeable reference and instrument the method
550+
symbolt &w_symbol = symbol_table.get_writeable_ref(entry.first);
551+
instrument_synchronized_code(
552+
symbol_table, w_symbol, it->second.symbol_expr());
553+
}
554+
}

jbmc/src/java_bytecode/java_bytecode_convert_threadblock.h renamed to jbmc/src/java_bytecode/java_bytecode_concurrency_instrumentation.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ Module: Java Convert Thread blocks
55
Author: Kurt Degiogrio, kurt.degiorgio@diffblue.com
66
77
\*******************************************************************/
8-
#ifndef CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONVERT_THREADBLOCK_H
9-
#define CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONVERT_THREADBLOCK_H
8+
#ifndef CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONCURRENCY_INSTRUMENTATION_H
9+
#define CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONCURRENCY_INSTRUMENTATION_H
1010

1111
#include <util/symbol_table.h>
1212

1313
void convert_threadblock(symbol_tablet &symbol_table);
1414

15+
void convert_synchronized_methods(symbol_tablet &symbol_table);
16+
1517
#endif

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ void java_bytecode_convert_method_lazy(
363363
else
364364
member_type.set_access(ID_default);
365365

366+
if(m.is_synchronized)
367+
member_type.set(ID_is_synchronized, true);
368+
if(m.is_static)
369+
member_type.set(ID_is_static, true);
370+
366371
// do we need to add 'this' as a parameter?
367372
if(!m.is_static)
368373
{

jbmc/src/java_bytecode/java_bytecode_language.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Author: Daniel Kroening, kroening@kroening.com
2222

2323
#include <goto-programs/class_hierarchy.h>
2424

25+
#include "java_bytecode_concurrency_instrumentation.h"
2526
#include "java_bytecode_convert_class.h"
2627
#include "java_bytecode_convert_method.h"
27-
#include "java_bytecode_convert_threadblock.h"
2828
#include "java_bytecode_internal_additions.h"
2929
#include "java_bytecode_instrument.h"
3030
#include "java_bytecode_typecheck.h"
@@ -755,9 +755,12 @@ bool java_bytecode_languaget::typecheck(
755755
bool res = java_bytecode_typecheck(
756756
symbol_table, get_message_handler(), string_refinement_enabled);
757757

758-
// now instrument thread-blocks
758+
// now instrument thread-blocks and synchronized methods.
759759
if(threading_support)
760+
{
760761
convert_threadblock(symbol_table);
762+
convert_synchronized_methods(symbol_table);
763+
}
761764

762765
return res;
763766
}

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ IREP_ID_TWO(C_is_anonymous, #is_anonymous)
384384
IREP_ID_ONE(is_enum_constant)
385385
IREP_ID_ONE(is_inline)
386386
IREP_ID_ONE(is_extern)
387+
IREP_ID_ONE(is_synchronized)
387388
IREP_ID_ONE(is_global)
388389
IREP_ID_ONE(is_thread_local)
389390
IREP_ID_ONE(is_parameter)

0 commit comments

Comments
 (0)