@@ -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,186 @@ 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 irep_idt 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+ // / Note: this breaks sharing on \p code
134+ // / \param [out] code: current element to modify
135+ // / \param monitorexit: codet to insert before the return
136+ static void monitor_exits (codet &code, const codet &monitorexit)
137+ {
138+ const irep_idt &statement = code.get_statement ();
139+ if (statement == ID_return)
140+ {
141+ // Replace the return with a monitor exit plus return block
142+ code_blockt return_block;
143+ return_block.add (monitorexit);
144+ return_block.move_to_operands (code);
145+ code = return_block;
146+ }
147+ else if (
148+ statement == ID_label || statement == ID_block ||
149+ statement == ID_decl_block)
150+ {
151+ // If label or block found, explore the code inside the block
152+ Forall_operands (it, code)
153+ {
154+ codet &sub_code = to_code (*it);
155+ monitor_exits (sub_code, monitorexit);
156+ }
157+ }
158+ }
159+
160+ // / Transforms the codet stored in \c symbol.value. The \p symbol is expected to
161+ // / be a Java synchronized method. Java synchronized methods do not have
162+ // / explicit calls to the instructions monitorenter and monitorexit, the JVM
163+ // / interprets the synchronized flag in a method and uses monitor of the object
164+ // / to implement locking and unlocking. Therefore JBMC has to emulate this same
165+ // / behavior. We insert a call to our model of monitorenter at the beginning of
166+ // / the method and calls to our model of monitorexit at each return instruction.
167+ // / We wrap the entire body of the method with an exception handler that will
168+ // / call our model of monitorexit if the method returns exceptionally.
169+ // /
170+ // / Example:
171+ // /
172+ // / \code
173+ // / synchronized int amethod(int p)
174+ // / {
175+ // / int x = 0;
176+ // / if(p == 0)
177+ // / return -1;
178+ // / x = p / 10
179+ // / return x
180+ // / }
181+ // / \endcode
182+ // /
183+ // / Is transformed into the codet equivalent of:
184+ // /
185+ // / \code
186+ // / synchronized int amethod(int p)
187+ // / {
188+ // / try
189+ // / {
190+ // / java::java.lang.Object.monitorenter(this);
191+ // / int x = 0;
192+ // / if(p == 0)
193+ // / {
194+ // / java::java.lang.Object.monitorexit(this);
195+ // / return -1;
196+ // / }
197+ // / java::java.lang.Object.monitorexit(this);
198+ // / return x
199+ // / }
200+ // / catch(java::java.lang.Throwable e)
201+ // / {
202+ // / // catch every exception, including errors!
203+ // / java::java.lang.Object.monitorexit(this);
204+ // / throw e;
205+ // / }
206+ // / }
207+ // / \endcode
208+ // /
209+ // / \param symbol_table: a symbol_table
210+ // / \param symbol: writeable symbol hosting code to synchronize
211+ // / \param sync_object: object to use as a lock
212+ static void instrument_synchronized_code (
213+ symbol_tablet &symbol_table,
214+ symbolt &symbol,
215+ const exprt &sync_object)
216+ {
217+ PRECONDITION (!symbol.type .get_bool (ID_is_static));
218+
219+ codet &code = to_code (symbol.value );
220+
221+ // Get the calls to the functions that implement the critical section
222+ codet monitorenter = get_monitor_call (symbol_table, true , sync_object);
223+ codet monitorexit = get_monitor_call (symbol_table, false , sync_object);
224+
225+ // Create a unique catch label and empty throw type (i.e. any)
226+ // and catch-push them at the beginning of the code (i.e. begin try)
227+ code_push_catcht catch_push;
228+ irep_idt handler (" pc-synchronized-catch" );
229+ irep_idt exception_id;
230+ code_push_catcht::exception_listt &exception_list =
231+ catch_push.exception_list ();
232+ exception_list.push_back (
233+ code_push_catcht::exception_list_entryt (exception_id, handler));
234+
235+ // Create a catch-pop to indicate the end of the try block
236+ code_pop_catcht catch_pop;
237+
238+ // Create the finally block with the same label targeted in the catch-push
239+ const symbolt &tmp_symbol = get_fresh_aux_symbol (
240+ java_reference_type (symbol_typet (" java::java.lang.Throwable" )),
241+ id2string (symbol.name ),
242+ " caught-synchronized-exception" ,
243+ code.source_location (),
244+ ID_java,
245+ symbol_table);
246+ symbol_exprt catch_var (tmp_symbol.name , tmp_symbol.type );
247+ catch_var.set (ID_C_base_name, tmp_symbol.base_name );
248+ code_landingpadt catch_statement (catch_var);
249+ codet catch_instruction = catch_statement;
250+ code_labelt catch_label (handler, code_blockt ());
251+ code_blockt &catch_block = to_code_block (catch_label.code ());
252+ catch_block.add (catch_instruction);
253+ catch_block.add (monitorexit);
254+
255+ // Re-throw exception
256+ side_effect_expr_throwt throw_expr;
257+ throw_expr.copy_to_operands (catch_var);
258+ catch_block.add (code_expressiont (throw_expr));
259+
260+ // Write a monitorexit before every return
261+ monitor_exits (code, monitorexit);
262+
263+ // Wrap the code into a try finally
264+ code_blockt try_block;
265+ try_block.move_to_operands (monitorenter);
266+ try_block.move_to_operands (catch_push);
267+ try_block.move_to_operands (code);
268+ try_block.move_to_operands (catch_pop);
269+ try_block.move_to_operands (catch_label);
270+ code = try_block;
271+ }
272+
92273// / Transforms the codet stored in in \p f_code, which is a call to function
93274// / CProver.startThread:(I)V into a block of code as described by the
94275// / documentation of function convert_thread_block
@@ -284,20 +465,20 @@ static void instrument_getCurrentThreadID(
284465// /
285466// / Note': the ID of the thread is assigned after the thread is created, this
286467// / 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
468+ // / assign the thread ID before the creation of the thread , due to a bug in
288469// / symex. See https://github.com/diffblue/cbmc/issues/1630/for more details.
289470// /
290471// / \param symbol_table: a symbol table
291472void convert_threadblock (symbol_tablet &symbol_table)
292473{
293474 using instrument_callbackt =
294- std::function<void (const code_function_callt&, codet&, symbol_tablet&)>;
475+ std::function<void (const code_function_callt&, codet&, symbol_tablet&)>;
295476 using expr_replacement_mapt =
296- std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
477+ std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
297478
298479 namespacet ns (symbol_table);
299480
300- for (auto entry : symbol_table)
481+ for (const auto & entry : symbol_table)
301482 {
302483 expr_replacement_mapt expr_replacement_map;
303484 const symbolt &symbol = entry.second ;
@@ -361,3 +542,75 @@ void convert_threadblock(symbol_tablet &symbol_table)
361542 }
362543 }
363544}
545+
546+ // / Iterate through the symbol table to find and instrument synchronized
547+ // / methods.
548+ // /
549+ // / Synchronized methods make it impossible for two invocations of the same
550+ // / method on the same object to interleave. In-order to replicate
551+ // / these semantics a call to
552+ // / "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V" and
553+ // / "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V" is instrumented
554+ // / at the start and end of the method. Note, that the former is instrumented
555+ // / at every statement where the execution can exit the method in question.
556+ // / Specifically, out of order return statements and exceptions. The latter
557+ // / is dealt with by instrumenting a try catch block.
558+ // /
559+ // / Note: Static synchronized methods are not supported yet as the
560+ // / synchronization semantics for static methods is different (locking on the
561+ // / class instead the of the object). Upon encountering a static synchronized
562+ // / method the current implementation will ignore the synchronized flag.
563+ // / (showing a warning in the process). This may result in superfluous
564+ // / interleavings.
565+ // /
566+ // / Note': This method requires the availability of
567+ // / "java::java.lang.Object.monitorenter:(Ljava/lang/Object;)V" and
568+ // / "java::java.lang.Object.monitorexit:(Ljava/lang/Object;)V".
569+ // / (java-library-models). If they are not available code_skipt will inserted
570+ // / instead of calls to the aforementioned methods.
571+ // /
572+ // / \param symbol_table: a symbol table
573+ // / \param message_handler: status output
574+ void convert_synchronized_methods (
575+ symbol_tablet &symbol_table,
576+ message_handlert &message_handler)
577+ {
578+ namespacet ns (symbol_table);
579+ for (const auto &entry : symbol_table)
580+ {
581+ const symbolt &symbol = entry.second ;
582+
583+ if (symbol.type .id () != ID_code)
584+ continue ;
585+ if (symbol.value .is_nil ())
586+ continue ;
587+ if (!symbol.type .get_bool (ID_is_synchronized))
588+ continue ;
589+
590+ if (symbol.type .get_bool (ID_is_static))
591+ {
592+ messaget message (message_handler);
593+ message.warning () << " Java method '" << entry.first
594+ << " ' is static and synchronized."
595+ << " This is unsupported, the synchronized keyword"
596+ << " will be ignored."
597+ << messaget::eom;
598+ continue ;
599+ }
600+
601+ // find the object to synchronize on
602+ const irep_idt this_id (id2string (symbol.name ) + " ::this" );
603+ exprt this_expr (this_id);
604+
605+ auto it = symbol_table.symbols .find (this_id);
606+
607+ if (it == symbol_table.symbols .end ())
608+ // failed to find object to synchronize on
609+ continue ;
610+
611+ // get writeable reference and instrument the method
612+ symbolt &w_symbol = symbol_table.get_writeable_ref (entry.first );
613+ instrument_synchronized_code (
614+ symbol_table, w_symbol, it->second .symbol_expr ());
615+ }
616+ }
0 commit comments