Source file mt_analysis_hooks.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
open Eva_ast
open Mt_cil
open Mt_memory.Types
open Mt_types
open Mt_shared_vars_types
open Mt_thread
let wrap_res res = Some (Mt_memory.int_to_value res)
let no_res = (None : value option)
type hook_sig = (exp * value) list -> state * value option
let current_position analysis =
match Callstack.top_callsite analysis.curr_stack with
| Kglobal -> assert false
| Kstmt stmt ->
stmt, Option.get (Callstack.pop analysis.curr_stack)
let log_arg analysis =
let stack = analysis.curr_stack in
let stack = Option.value (Callstack.pop stack) ~default:stack in
let source = kinstr_to_source (Callstack.top_callsite stack) in
let append fmt =
if Mt_options.PrintCallstacks.get () || Mt_self.Debug.get () > 1
then Format.fprintf fmt "@.%a" Callstack.pretty stack
in
source, append
let result analysis =
let source, append = log_arg analysis in
Mt_self.result ~once:true ?source ~append
let warning analysis =
let source, append = log_arg analysis in
Mt_self.warning ~once:true ?source ~append
let error analysis =
let source, append = log_arg analysis in
Mt_self.error ?source ~append
exception Hook_failure of int
let default_err_code = -255
let hook_fail ?(code=default_err_code) () =
raise (Hook_failure code)
let catch_conversion analysis ~prefix v msg =
match v with
| Ok v -> v
| Error error ->
warning analysis "@[%s: %s. %s. Ignoring.@]" prefix msg error;
hook_fail ()
let find_id kind find id =
match find id with
| Some elt -> Ok elt
| None ->
let error =
Format.sprintf
"Id %d for %s does not exists (incrementation inside program?)."
id kind
in
Error error
let find_thread = find_id "thread" Thread.find
let find_mutex = find_id "mutex" Mutex.find
let find_queue = find_id "queue" Mqueue.find
let project_singleton_int cvalue possible_values =
let find i =
List.find_opt (fun (x, _) -> Datatype.Int.equal x i) possible_values
in
try
Cvalue.V.project_ival cvalue
|> Ival.project_int
|> Integer.to_int_exn
|> find
with Cvalue.V.Not_based_on_null | Ival.Not_Singleton_Int | Z.Overflow ->
None
type operation = {
name: string;
before: int list;
after: int;
}
let check_value warn possible_values operation value =
match project_singleton_int value possible_values with
| Some (i, _msg) when List.mem i operation.before -> ()
| Some (_i, msg) -> warn msg
| None ->
let reason =
Format.asprintf
"unable to check its precise status (internal value %a should be %a)"
Cvalue.V.pretty value
(Pretty_utils.pp_list ~sep:" or " Datatype.Int.pretty) operation.before
in
warn reason
module ThreadOp = struct
let not_created = 0
let started = 1
let suspended = 2
let cancelled = 3
let possible_values =
[ not_created, "it might not be created yet" ;
started, "it might have already been started by the current thread";
suspended, "it might have already been suspended by the current thread";
cancelled, "it might have been cancelled by the current thread"; ]
let start =
{ name = "start"; before = [suspended]; after = started; }
let suspend =
{ name = "suspend"; before = [started]; after = suspended; }
let cancel =
{ name = "cancel"; before = [started; suspended]; after = cancelled; }
let check_and_write analysis state thread operation =
let id = Mt_ids.of_thread thread in
let value = Mt_ids.read_id_state state id in
let warn failure_reason =
warning analysis "Trying to %s thread %a, but %s."
operation.name Thread.pretty thread failure_reason
in
check_value warn possible_values operation value;
Mt_ids.write_id_state state id operation.after
end
module MutexOp = struct
let not_init = 0
let unlocked = 1
let locked = 2
let possible_values =
[ not_init, "it might not be initialized yet";
unlocked, "it might already be unlocked (and initialized)";
locked, "it might already be locked (and initialized)"; ]
let initialize = { name = "initialize"; before = [not_init]; after = unlocked; }
let lock = { name = "lock"; before = [unlocked]; after = locked; }
let unlock = { name = "unlock"; before = [locked]; after = unlocked; }
let check_and_write analysis state mutex operation =
let id = Mt_ids.of_mutex mutex in
let value = Mt_ids.read_id_state state id in
let warn failure_reason =
warning analysis "Trying to %s mutex %a, but %s."
operation.name Mutex.pretty mutex failure_reason
in
check_value warn possible_values operation value;
Mt_ids.write_id_state state id operation.after
end
module QueueOp = struct
let not_init = 0
let initialized = 1
let possible_values =
[ not_init, "it might not be initialized yet";
initialized, "it might be already initialized"; ]
let initialize =
{ name = "initialize"; before = [not_init]; after = initialized; }
let use name = { name; before = [initialized]; after = initialized; }
let send = use "send message to"
let receive = use "receive message from"
let check_and_write analysis state queue operation =
let id = Mt_ids.of_queue queue in
let value = Mt_ids.read_id_state state id in
let warn failure_reason =
warning analysis "Trying to %s message queue %a, but %s."
operation.name Mqueue.pretty queue failure_reason
in
check_value warn possible_values operation value;
Mt_ids.write_id_state state id operation.after
end
(** When a thread is created, it must not inherit from its creator the status
of mutexes. This function sets all mutexes passed as argument to 1
(unlocked). *)
let reset_mutexes mutexes state =
Mutex.Set.fold
(fun mutex state -> Mt_ids.replace_id_value state (Mt_ids.of_mutex mutex) ~before:2 ~after:1)
mutexes state
let sync_values analysis state =
let join ~written ~state =
Cvalue.Model.fold
(fun b offsm state ->
let offsm' = Cvalue.Model.find_base_or_default b state in
match offsm' with
| `Top -> Mt_self.fatal "Top state"
| `Bottom -> state
| `Value offsm' ->
let offsm'' = Cvalue.V_Offsetmap.join offsm offsm' in
Cvalue.Model.add_base b offsm'' state)
written state
in
let v = Mt_lib.var_thread_created () in
let value = Results.(in_cvalue_state state |> eval_var v |> as_cvalue) in
if Cvalue.V.is_zero value then
state
else
fold_threads analysis state
(fun th state ->
match th.th_values_written with
| Cvalue.Model.Bottom -> state
| Cvalue.Model.Top -> Cvalue.Model.top
| Cvalue.Model.Map written ->
if not (ThreadState.equal analysis.curr_thread th) then
join ~written ~state
else state
)
let hook_sync analysis state : hook_sig = function _ ->
sync_values analysis state, no_res
let basic_thread eva_thread stack func state params parent = {
th_eva_thread = eva_thread;
th_stack = stack;
th_init_state = state;
th_fun = func;
th_params = params;
th_parent = parent;
th_to_recompute = SetRecomputeReason.empty;
th_read_written = AccessesByZone.empty_map;
th_amap = Trace.empty;
th_cfg = Mt_cfg_types.CfgNode.dead;
th_read_written_cfg = Mt_cfg_types.AccessesByZoneNode.empty_map;
th_values_written = Cvalue.Model.empty_map;
th_projects = [];
th_value_results = None;
th_priority= PDefault;
}
let spawn_thread analysis eva_thread stack func state params parent =
try
let th' = Thread.Hashtbl.find analysis.all_threads eva_thread in
if Option.equal ThreadState.equal parent th'.th_parent = false
then (
let pp_parent = Pretty_utils.pp_opt ~none:"<none>" ThreadState.pretty in
error analysis "Thread '%a' is launched by two different \
threads (%a and %a). Ignoring"
Thread.pretty eva_thread
pp_parent parent
pp_parent th'.th_parent;
hook_fail ())
else if Callstack.equal stack th'.th_stack = false then (
error analysis
"Thread '%a' is launched in two different contexts:@.\
Context 1:@.@[<hov 2> %a@]@.Context 2:@.@[<hov 2> %a@]@.Ignoring"
Thread.pretty eva_thread
Callstack.pretty stack
Callstack.pretty th'.th_stack;
hook_fail ())
else if Kernel_function.get_id func <> Kernel_function.get_id th'.th_fun
then (
error analysis
"Thread '%a' can be two different functions \
(%s and %s). Imprecise pointer? Ignoring."
Thread.pretty eva_thread
(Kernel_function.get_name func)
(Kernel_function.get_name th'.th_fun);
hook_fail ())
else (
let init_state', ris = Mt_memory.join_state th'.th_init_state state
and args, ra = Mt_memory.join_params th'.th_params params
in
th'.th_init_state <- init_state';
th'.th_params <- args;
if ris then ThreadState.recompute_because th' InitialEnvChanged;
if ra then ThreadState.recompute_because th' InitialArgsChanged;
let text =
if ris || ra then "New context for" else "Thread" in
result analysis "@[<hov 2>%s@ %a@]" text ThreadState.pretty_detailed th';
th'
)
with Not_found ->
let th = basic_thread eva_thread stack func state params parent in
th.th_to_recompute <- SetRecomputeReason.singleton FirstIteration;
Thread.Hashtbl.add analysis.all_threads eva_thread th;
result analysis "@[<hov>New thread: %a@]" ThreadState.pretty_detailed th;
th
let standalone_thread th kf initial_state =
match Function_calls.analysis_target kf Kglobal with
| `Builtin _ | `Spec _ ->
Mt_self.not_yet_implemented
"Using an ACSL specification or a builtin to interpret entry point %a \
of thread %a is not supported."
Kernel_function.pretty kf Thread.pretty th
| `Body (fundec, _) ->
let formals = fundec.sformals in
let eval_arg vi =
Results.(in_cvalue_state initial_state |> eval_var vi |> as_cvalue)
in
let args = List.map eval_arg formals in
let stack = Callstack.init ~thread:(Thread.id th) ~entry_point:kf in
basic_thread th stack kf initial_state args None
let main_thread k_main initial_state =
standalone_thread Thread.main k_main initial_state
let interrupt_thread kf initial_state =
let th = Thread.interrupt_handler kf in
standalone_thread th kf initial_state
(** Set the global variable that indicates that at least one thread is running
to one *)
let thread_is_running state =
let p_thread_running = Mt_lib.var_thread_created (), 0 in
Mt_memory.write_int_pointer p_thread_running 1 state
(** Hook registered in the value analysis for the creation of thread *)
let hook_thread_creation analysis state : hook_sig = function
| (_, name) :: (_, f) :: params ->
let conv = catch_conversion analysis ~prefix:"During thread creation" in
let kf = conv (Mt_memory.extract_fun f) "invalid thread function" in
let formals = Kernel_function.get_formals kf in
let rec trunc_params = function
| [], [] -> []
| _formal :: qf, param :: qp -> param :: trunc_params (qf, qp)
| [], (_ :: _ as params) ->
if Mt_options.ModerateWarnings.get () then
warning analysis
"During thread creation, mismatch between function \
'%s' signature and actual arguments. Ignoring last \
%d argument(s) and continuing."
(Kernel_function.get_name kf) (List.length params);
[]
| _ :: _, [] ->
error analysis
"When creating thread from function %s: too few \
arguments, %d expected but %d given. Ignoring.]"
(Kernel_function.get_name kf)
(List.length formals) (List.length params);
hook_fail ()
in
let params = List.map snd (trunc_params (formals, params)) in
let eva_thread =
let name = Concurrency.Name.of_cvalue name in
let pos = current_position analysis in
Thread.spawn pos name kf params
in
ignore (spawn_thread analysis eva_thread analysis.curr_stack kf
Cvalue.Model.bottom params (Some analysis.curr_thread));
register_event analysis (CreateThread eva_thread);
Mt_ids.write_id_state state (Mt_ids.of_thread eva_thread) 2,
wrap_res (Thread.id eva_thread)
| _ -> Mt_self.fatal "Incorrect mthread binding for thread creation"
let update_initial_state analysis th state =
let state = thread_is_running state in
let state_started = Mt_memory.clear_non_globals state in
let state_started = reset_mutexes analysis.all_mutexes state_started in
let th = Thread.Hashtbl.find analysis.all_threads th in
let initial, changed = Mt_memory.join_state th.th_init_state state_started in
if changed then (
ThreadState.recompute_because th Mt_thread.InitialEnvChanged;
if Cvalue.Model.is_reachable th.th_init_state then
result analysis "@[<hov 2>New context for@ %a@]"
ThreadState.pretty_detailed th;
);
th.th_init_state <- initial;
sync_values analysis state
let hook_thread_start_suspend operation aux_state evt analysis state : hook_sig = function
| [_, offset] ->
let prefix = "During thread " ^ operation.name in
let conv v = catch_conversion analysis ~prefix v in
let offset = conv (Mt_memory.extract_int offset) "invalid thread id" in
if offset <> 0 then
let th = conv (find_thread offset) "unknown thread" in
let state = ThreadOp.check_and_write analysis state th operation in
let evt = evt th in
result analysis "@[%a@]" Event.pretty evt;
register_event analysis evt;
let state = aux_state analysis th (state:state) in
state, wrap_res 0
else (
warning analysis "Trying to %s unknown thread. Ignoring." operation.name;
hook_fail ~code:(-1) ())
| _ -> Mt_self.fatal "Incorrect mthread binding for thread %s" operation.name
(** Hook registered in the value analysis when a thread is started *)
let hook_thread_start =
hook_thread_start_suspend ThreadOp.start
update_initial_state (fun i -> StartThread i)
let hook_thread_suspend =
hook_thread_start_suspend ThreadOp.suspend
(fun _ _ s -> s) (fun i -> SuspendThread i)
let hook_thread_cancellation analysis state : hook_sig = function
| [_, offset] ->
let prefix = "During thread cancellation" in
let conv v = catch_conversion analysis ~prefix v in
let offset = conv (Mt_memory.extract_int offset) "invalid thread id" in
if offset <> 0 then
let th = conv (find_thread offset) "unknown thread" in
register_event analysis (CancelThread th);
let state = ThreadOp.check_and_write analysis state th ThreadOp.cancel in
state, wrap_res 0
else (
warning analysis "Trying to cancel unknown thread. Ignoring.";
hook_fail ~code:(-1) ())
| _ -> Mt_self.fatal "Incorrect mthread binding for thread cancellation \
(only the thread id is expected)"
let hook_thread_exit analysis (_state: state) : hook_sig = function
| [_, v] ->
if ThreadState.is_main analysis.curr_thread then (
error analysis
"Call to thread exit primitive inside main thread. Ignoring";
hook_fail ())
else (
register_event analysis (ThreadExit v);
result analysis "Thread exiting with value %a" Cvalue.V.pretty v;
Cvalue.Model.bottom, no_res)
| _ -> Mt_self.fatal "Incorrect mthread binding for thread exit \
(only the return value is expected)"
let hook_thread_id analysis state : hook_sig = fun _ ->
state, wrap_res (Thread.id analysis.curr_thread.th_eva_thread)
let hook_thread_priority analysis state : hook_sig = function
|[ _, p] ->
begin
let p = catch_conversion analysis
~prefix:"During thread priority definition"
(Mt_memory.extract_int p)
"invalid thread id"
in
begin
match analysis.curr_thread.th_priority with
| PPriority p' ->
if p <> p' then begin
warning analysis
"Conflicting priorities (previous: %d, new %d) for thread '%a'."
p
p'
ThreadState.pretty analysis.curr_thread;
analysis.curr_thread.th_priority <- PUnknown;
end
| PUnknown -> ()
| PDefault ->
result analysis "Setting priority to %d" p;
analysis.curr_thread.th_priority <- PPriority p;
end;
state, wrap_res 0
end
| _ -> Mt_self.fatal "Incorrect mthread binding for thread priority \
(only a non negative integer is expected)"
(** --- Hook registered in the value analysis related to messages --- *)
let hook_queue_init analysis state : hook_sig = function
| [_, name; _, size] ->
let prefix = "During queue initialization" in
let conv v = catch_conversion analysis ~prefix v in
let pos = current_position analysis
and name = Concurrency.Name.of_cvalue name
and size = conv (Mt_memory.extract_int size) "invalid size" in
let q = Mqueue.create pos name in
analysis.all_queues <- Mqueue.Set.add q analysis.all_queues;
let state = QueueOp.check_and_write analysis state q QueueOp.initialize in
let size = if size < 0 then None else Some size in
register_event analysis (CreateQueue (q, size));
state, wrap_res (Mqueue.id q)
| _ -> Mt_self.fatal "Incorrect mthread binding for queue creation"
let hook_send_msg analysis state : hook_sig = function
| [(_, offset); (_exp_content, content); (_exp_size, size)] ->
let conv v = catch_conversion analysis ~prefix:"During message sending" v in
let offset = conv (Mt_memory.extract_int offset) "invalid queue id" in
if offset <> 0 then
let sbytes = conv (Mt_memory.extract_int size) "invalid message size" in
if sbytes <= 0 then
conv (Error (string_of_int sbytes)) "invalid message length";
let q = conv (find_queue offset) "unknown queue" in
let content = Mt_memory.read_slice ~p:content ~sbytes state in
let state = QueueOp.check_and_write analysis state q QueueOp.send in
let action = SendMsg (q, (content, sbytes)) in
result analysis "@[%a@]" Event.pretty action;
register_event analysis action;
state, wrap_res 0
else (
warning analysis
"Trying to send message on uninitialized queue. Ignoring.";
state, wrap_res (-1))
| _ -> Mt_self.fatal "Incorrect mthread binding for message sending"
let find_msg_content analysis q =
let th acc = function
| SendMsg (q', (v, size)) ->
if Mqueue.equal q q' then (th, v, size) :: acc else acc
| _ -> acc
in
fold_threads analysis []
(fun { th_eva_thread = th; th_amap = m } ->
Trace.fold' m (fun a r -> extract_action th r a))
let hook_receive_msg analysis state : hook_sig = function
| [(_,offset); (_e_size, size); (e_loc, loc)] ->
let prefix = "During message reception" in
let conv v = catch_conversion analysis ~prefix v in
let offset = conv (Mt_memory.extract_int offset) "invalid queue id" in
if offset <> 0 then
let smax = conv (Mt_memory.extract_int size) "invalid size"
and p = conv (Mt_memory.extract_pointer loc) "invalid destination buffer"
and q = conv (find_queue offset) "unknown queue" in
let state = QueueOp.check_and_write analysis state q QueueOp.receive in
let action = ReceiveMsg (q, p, smax) in
register_event analysis action;
let contents = find_msg_content analysis q in
let state, res, pp =
if contents <> [] then
let length, kept_mess, _, state =
List.fold_left
(fun (length, kept_mess, exact, state) (_, slice, smess as mess) ->
let sbytes = min smess smax in
let state' =
Mt_memory.write_slice ~p ~sbytes ~slice ~exact state
in
if Cvalue.Model.is_reachable state' then
let sbytes_val =
Cvalue.V.inject_ival (Ival.of_int sbytes) in
let length' = Cvalue.V.join sbytes_val length in
length', mess :: kept_mess, false, state'
else (
warning analysis
"Found message of length %d, which is too long \
for buffer '%a'. Execution will continue without \
those messages.(Ignore \"This path is assumed to \
be dead message if any\".)"
smess pp_exp e_loc;
length, kept_mess, exact, state)
)
(Cvalue.V.bottom, [], true, state) contents
in
match kept_mess with
| [] ->
Cvalue.Model.bottom,
no_res,
(fun fmt -> Format.fprintf fmt "No valid value to receive.")
| _ :: _ ->
let pp fmt =
Format.fprintf fmt "Possible %s values:@.%a"
(if List.length kept_mess = List.length contents
then "" else "valid ")
(Pretty_utils.pp_list ~pre:"@[<v>" ~sep:"@,"
(fun fmt (th, v, _) ->
Format.fprintf fmt "@[From thread %a:@ %a@]"
Thread.pretty th
Mt_memory.pretty_slice v
)) kept_mess
in
state, Some length, pp
else
Cvalue.Model.bottom,
no_res,
(fun fmt -> Format.fprintf fmt "No value to receive (yet?).")
in
result analysis "@[<hov>%a@ %t@]" Event.pretty action pp;
state, res
else (
warning analysis
"Trying to receive value on non-initialized queue. Ignoring.";
state, wrap_res (-2))
| _ -> Mt_self.fatal "Incorrect mthread binding for message reception"
let aux_mutex ~operation:op ~event analysis state : hook_sig = function
| [_, offset] ->
let prefix = "During mutex " ^ op.name in
let conv v = catch_conversion analysis ~prefix v in
let offset_conversion = Mt_memory.extract_int_possibly_zero offset in
let offset, exact = conv offset_conversion "invalid mutex id" in
if exact = `WithZero then
warning analysis "Trying to %s a possibly uninitialized mutex." op.name;
if offset <> 0 then
let m = conv (find_mutex offset) "unknown mutex" in
let state = MutexOp.check_and_write analysis state m op in
let evt : event = event m in
result analysis "%a" Event.pretty evt;
register_event analysis evt;
let with_external = sync_values analysis state in
with_external, wrap_res 0
else (
warning analysis "Trying to %s uninitialized mutex. Ignoring" op.name;
state, wrap_res (-1))
| _ ->
Mt_self.fatal "Incorrect mthread binding for mutex function"
let hook_init_mutex analysis state : hook_sig = function
| [_, name] ->
let pos = current_position analysis
and name = Concurrency.Name.of_cvalue name in
let mutex = Mutex.create pos name in
analysis.all_mutexes <- Mutex.Set.add mutex analysis.all_mutexes;
let state = MutexOp.check_and_write analysis state mutex MutexOp.initialize in
result analysis "Initializing mutex %a" Mutex.pretty mutex;
state, wrap_res (Mutex.id mutex)
| _ ->
Mt_self.fatal "Incorrect mthread binding for mutex function"
let hook_lock_mutex =
aux_mutex ~operation:MutexOp.lock ~event:(fun id -> MutexLock id)
let hook_release_mutex =
aux_mutex ~operation:MutexOp.unlock ~event:(fun id -> MutexRelease id)
(** --- Misc --- *)
let hook_dummy_message analysis state : hook_sig = function
| (_, name) :: args ->
let conv v = catch_conversion analysis ~prefix:"During unknown event" v in
let name = Mt_memory.extract_constant_string name in
let name = conv name "invalid event name" in
let evt = Dummy (name, List.map snd args) in
register_event analysis evt;
result analysis "Monitored event: %a" Event.pretty evt;
state, no_res
| _ -> Mt_self.fatal "Incorrect mthread binding for unknown event"
(** --- Main declarations --- *)
let mthread_builtins =
[
"Frama_C_thread_create", hook_thread_creation;
"Frama_C_thread_start", hook_thread_start;
"Frama_C_thread_suspend", hook_thread_suspend;
"Frama_C_thread_cancel", hook_thread_cancellation;
"Frama_C_thread_exit", hook_thread_exit;
"Frama_C_thread_id", hook_thread_id;
"Frama_C_thread_priority", hook_thread_priority;
"Frama_C_mutex_init", hook_init_mutex;
"Frama_C_mutex_lock", hook_lock_mutex;
"Frama_C_mutex_unlock", hook_release_mutex;
"Frama_C_queue_init", hook_queue_init;
"Frama_C_queue_send", hook_send_msg;
"Frama_C_queue_receive", hook_receive_msg;
"Frama_C_mthread_show", hook_dummy_message;
"Frama_C_mthread_sync", hook_sync;
]
;;
let is_mthread_builtin s =
List.exists (fun (s', _) -> s = s') mthread_builtins
let catch_functions_calls analysis (stack : Callstack.callstack) kf state kind =
let f = Kernel_function.get_name kf in
if is_mthread_builtin f && Option.is_none (Callstack.pop stack) then
Mt_self.abort "Thread function %s called as starting thread function" f;
if kind = `Spec then
Mt_lib.warn_on_unsupported_library_function kf;
analysis.curr_stack <- stack;
if Callstack.is_empty stack then
analysis.curr_events_stack <- [];
if Callstack.is_empty analysis.curr_stack &&
Thread.is_main analysis.curr_thread.th_eva_thread then begin
let th = main_thread kf state in
let th = spawn_thread analysis th.th_eva_thread
th.th_stack th.th_fun th.th_init_state th.th_params None in
if analysis.main_thread != th then begin
analysis.main_thread <- th;
analysis.curr_thread <- th;
th.th_to_recompute <- SetRecomputeReason.empty;
let interrupt_handlers = Mt_options.InterruptHandlers.get () in
let interrupts, state =
Kernel_function.Set.fold
(fun kf_interrupt (interrupts, state) ->
let th = interrupt_thread kf_interrupt state in
let th =
spawn_thread analysis th.th_eva_thread th.th_stack th.th_fun
th.th_init_state th.th_params None
in
let state =
Mt_ids.write_id_state state (Mt_ids.of_thread th.th_eva_thread) 1
in
(th :: interrupts, state))
interrupt_handlers
([], state)
in
if interrupts != [] then begin
List.iter
(fun th ->
let _ = update_initial_state analysis th.th_eva_thread state in ())
(th :: interrupts)
end
end
end;
push_function_call analysis;
match kind with
| `Spec | `Builtin ->
Mt_shared_vars.register_concurrent_var_accesses analysis (`Leaf state);
pop_function_call analysis;
| `Body | `Reuse -> ()
let catch_functions_record analysis stack _kf _pre_state = function
| `Body (Cvalue_callbacks.{before_stmts; after_stmts}, i) ->
analysis.curr_stack <- stack;
let hbefore = Lazy.force before_stmts in
let hafter = Lazy.force after_stmts in
Mt_shared_vars.register_concurrent_var_accesses analysis (`Final hbefore);
register_memory_states analysis ~before:hbefore ~after:hafter;
let cur_events = curr_events analysis in
Datatype.Int.Hashtbl.add analysis.memexec_cache i cur_events;
pop_function_call analysis;
| `Reuse i ->
let events = Datatype.Int.Hashtbl.find analysis.memexec_cache i in
register_multiple_events analysis events;
pop_function_call analysis;
| `Builtin _ | `Spec _ -> ()