6
6
7
7
\*******************************************************************/
8
8
9
- #include " java_bytecode_convert_threadblock .h"
9
+ #include " java_bytecode_concurrency_instrumentation .h"
10
10
#include " expr2java.h"
11
11
#include " java_types.h"
12
12
13
13
#include < util/expr_iterator.h>
14
14
#include < util/namespace.h>
15
15
#include < util/cprover_prefix.h>
16
16
#include < util/std_types.h>
17
+ #include < util/fresh_symbol.h>
17
18
#include < util/arith_tools.h>
18
19
19
20
// Disable linter to allow an std::string constant.
@@ -38,7 +39,7 @@ static symbolt add_or_get_symbol(
38
39
const bool is_thread_local,
39
40
const bool is_static_lifetime)
40
41
{
41
- const symbolt* psymbol = nullptr ;
42
+ const symbolt * psymbol = nullptr ;
42
43
namespacet ns (symbol_table);
43
44
ns.lookup (name, psymbol);
44
45
if (psymbol != nullptr )
@@ -89,6 +90,186 @@ static const std::string get_thread_block_identifier(
89
90
return integer2string (lbl_id);
90
91
}
91
92
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
+
92
273
// / Transforms the codet stored in in \p f_code, which is a call to function
93
274
// / CProver.startThread:(I)V into a block of code as described by the
94
275
// / documentation of function convert_thread_block
@@ -284,20 +465,20 @@ static void instrument_getCurrentThreadID(
284
465
// /
285
466
// / Note': the ID of the thread is assigned after the thread is created, this
286
467
// / 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
288
469
// / symex. See https://github.com/diffblue/cbmc/issues/1630/for more details.
289
470
// /
290
471
// / \param symbol_table: a symbol table
291
472
void convert_threadblock (symbol_tablet &symbol_table)
292
473
{
293
474
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&)>;
295
476
using expr_replacement_mapt =
296
- std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
477
+ std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
297
478
298
479
namespacet ns (symbol_table);
299
480
300
- for (auto entry : symbol_table)
481
+ for (const auto & entry : symbol_table)
301
482
{
302
483
expr_replacement_mapt expr_replacement_map;
303
484
const symbolt &symbol = entry.second ;
@@ -361,3 +542,75 @@ void convert_threadblock(symbol_tablet &symbol_table)
361
542
}
362
543
}
363
544
}
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