@@ -6,14 +6,15 @@ Author: Kurt Degiogrio, kurt.degiorgio@diffblue.com
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,141 @@ 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 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
+
92
228
// / Transforms the codet stored in in \p f_code, which is a call to function
93
229
// / CProver.startThread:(I)V into a block of code as described by the
94
230
// / documentation of function convert_thread_block
@@ -284,20 +420,20 @@ static void instrument_getCurrentThreadID(
284
420
// /
285
421
// / Note': the ID of the thread is assigned after the thread is created, this
286
422
// / 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
288
424
// / symex. See https://github.com/diffblue/cbmc/issues/1630/for more details.
289
425
// /
290
426
// / \param symbol_table: a symbol table
291
427
void convert_threadblock (symbol_tablet &symbol_table)
292
428
{
293
429
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&)>;
295
431
using expr_replacement_mapt =
296
- std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
432
+ std::unordered_map<const exprt, instrument_callbackt, irep_hash>;
297
433
298
434
namespacet ns (symbol_table);
299
435
300
- for (auto entry : symbol_table)
436
+ for (const auto & entry : symbol_table)
301
437
{
302
438
expr_replacement_mapt expr_replacement_map;
303
439
const symbolt &symbol = entry.second ;
@@ -361,3 +497,58 @@ void convert_threadblock(symbol_tablet &symbol_table)
361
497
}
362
498
}
363
499
}
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
+ }
0 commit comments