-
Notifications
You must be signed in to change notification settings - Fork 75
/
relationPriv.apron.ml
1439 lines (1281 loc) · 52.5 KB
/
relationPriv.apron.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
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(** Relational thread-modular value analyses for {!RelationAnalysis}, i.e. {!ApronAnalysis} and {!AffineEqualityAnalysis}.
@see <https://doi.org/10.1007/978-3-031-30044-8_2> Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V. Clustered Relational Thread-Modular Abstract Interpretation with Local Traces. *)
open Batteries
open GoblintCil
open Analyses
open RelationDomain
open GobConfig
(* open BaseUtil *)
module Q = Queries
module RelationComponents = RelationDomain.RelComponents
open CommonPriv
module type S =
functor (RD: RelationDomain.RD) ->
sig
module D: Lattice.S
module G: Lattice.S
module V: Printable.S
module P: DisjointDomain.Representative with type elt := D.t (** Path-representative. *)
type relation_components_t := RelationDomain.RelComponents (RD) (D).t
val name: unit -> string
val startstate: unit -> D.t
val read_global: Q.ask -> (V.t -> G.t) -> relation_components_t -> varinfo -> varinfo -> RD.t
(* [invariant]: Check if we should avoid producing a side-effect, such as updates to
the state when following conditional guards. *)
val write_global: ?invariant:bool -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> varinfo -> varinfo -> relation_components_t
val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t
val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t
val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `Return | `Init | `Thread] -> relation_components_t
val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t
val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t
val threadenter: Q.ask -> (V.t -> G.t) -> relation_components_t -> relation_components_t
val thread_join: ?force:bool -> Q.ask -> (V.t -> G.t) -> Cil.exp -> relation_components_t -> relation_components_t
val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> relation_components_t -> relation_components_t
val iter_sys_vars: (V.t -> G.t) -> VarQuery.t -> V.t VarQuery.f -> unit (** [Queries.IterSysVars] for apron. *)
val invariant_vars: Q.ask -> (V.t -> G.t) -> relation_components_t -> varinfo list
(** Returns global variables which are privatized. *)
val init: unit -> unit
val finalize: unit -> unit
end
(** Top privatization, which doesn't track globals at all.
This is unlike base's "none" privatization. which does track globals, but doesn't privatize them. *)
module Top: S = functor (RD: RelationDomain.RD) ->
struct
module D = Lattice.Unit
module G = Lattice.Unit
module V = EmptyV
module AV = RD.V
module P = UnitP
type relation_components_t = RelComponents (RD) (D).t
let name () = "top"
let startstate () = ()
let read_global ask getg (st: relation_components_t) g x =
let rel = st.rel in
assert (not (RD.mem_var rel (AV.global g)));
rel
let write_global ?(invariant=false) ask getg sideg (st: relation_components_t) g x: relation_components_t =
let rel = st.rel in
assert (not (RD.mem_var rel (AV.global g)));
st
let lock ask getg st m = st
let unlock ask getg sideg st m = st
let thread_join ?(force=false) ask getg exp st = st
let thread_return ask getg sideg tid st = st
let escape node ask getg sideg (st:relation_components_t) escaped:relation_components_t =
let rel = st.rel in
let rel_local = RD.remove_filter rel (fun var ->
match AV.find_metadata var with
| Some (Global _) -> false
| Some (Local r) -> EscapeDomain.EscapedVars.mem r escaped
| _ -> false
)
in
{ st with rel = rel_local }
let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason =
match reason with
| `Join when ConfCheck.branched_thread_creation () ->
if ask.f (Q.MustBeSingleThreaded {since_start = true}) then
st
else
(* must be like enter_multithreaded *)
let rel = st.rel in
let rel_local = RD.remove_filter rel (fun var ->
match AV.find_metadata var with
| Some (Global _) -> true
| _ -> false
)
in
{st with rel = rel_local}
| `Join
| `Normal
| `Init
| `Thread
| `Return ->
st
let enter_multithreaded ask getg sideg (st: relation_components_t): relation_components_t =
let rel = st.rel in
let rel_local = RD.remove_filter rel (fun var ->
match AV.find_metadata var with
| Some (Global _) -> true
| _ -> false
)
in
{st with rel = rel_local}
let threadenter ask getg (st: relation_components_t): relation_components_t =
{rel = RD.top (); priv = startstate ()}
let iter_sys_vars getg vq vf = ()
let invariant_vars ask getg st = []
let init () = ()
let finalize () = ()
end
module type ProtectionBasedPrivParam =
sig
(** Whether to be path-sensitive w.r.t. locally written protected globals that have been continuously protected since writing. *)
val path_sensitive: bool
end
(** Protection-Based Reading. Is unsound w.r.t. to locals escaping and becoming public. *)
module ProtectionBasedPriv (Param: ProtectionBasedPrivParam): S = functor (RD: RelationDomain.RD) ->
struct
include ConfCheck.RequireMutexActivatedInit
open Protection
(** Locally must-written protected globals that have been continuously protected since writing. *)
open struct
module P =
struct
include MustVars
let name () = "P"
end
end
(** Locally may-written protected globals that have been continuously protected since writing. *)
(* TODO: is this right? *)
module W =
struct
include MayVars
let name () = "W"
end
module D = Lattice.Prod (P) (W)
module G = RD
module V = Printable.UnitConf (struct let name = "global" end)
module PS =
struct
include Printable.Option (P) (struct let name = "None" end)
let of_elt (p, _) =
if Param.path_sensitive then
Some p
else
None
end
type relation_components_t = RelationComponents (RD) (D).t
module VM =
struct
type t =
| Local of varinfo
| Unprot of varinfo
| Prot of varinfo
let var_name = function
| Local x -> x.vname ^ "#" ^ string_of_int(x.vid)
| Unprot g -> g.vname ^ "#unprot"
| Prot g -> g.vname ^ "#prot"
end
module AV =
struct
include RelationDomain.VarMetadataTbl (VM)
let local g = make_var (Local g)
let unprot g = make_var (Unprot g)
let prot g = make_var (Prot g)
end
let name () = "ProtectionBasedPriv"
(** Restrict environment to global invariant variables. *)
let restrict_global rel =
RD.remove_filter rel (fun var ->
match AV.find_metadata var with
| Some (Unprot _ | Prot _) -> false
| _ -> true
)
(** Restrict environment to local variables and still-protected global variables. *)
let restrict_local is_unprot rel w_remove =
let remove_local_vars = List.map AV.local (W.elements w_remove) in
let rel' = RD.remove_vars rel remove_local_vars in
(* remove global vars *)
RD.remove_filter rel' (fun var ->
match AV.find_metadata var with
| Some (Unprot g | Prot g) -> is_unprot g
| _ -> false
)
let startstate () = (P.empty (), W.empty ())
let read_global ask getg (st: relation_components_t) g x =
let rel = st.rel in
let (p, w) = st.priv in
let g_local_var = AV.local g in
let x_var = AV.local x in
let rel_local =
if W.mem g w then
RD.assign_var rel x_var g_local_var
else
RD.bot ()
in
let rel_local' =
if P.mem g p then
rel_local
else if is_unprotected ask g then (
let g_unprot_var = AV.unprot g in
let rel_unprot = RD.add_vars rel [g_unprot_var] in
let rel_unprot = RD.assign_var rel_unprot x_var g_unprot_var in
(* let oct_unprot' = RD.D2.join oct_local oct_unprot in
(* unlock *)
let oct_unprot' = RD.D2.remove_vars oct_unprot' [g_unprot_var; g_local_var] in
(* add, assign from, remove is not equivalent to forget if g#unprot already existed and had some relations *)
(* TODO: why removing g_unprot_var? *)
oct_unprot' *)
RD.join rel_local rel_unprot
)
else (
let g_prot_var = AV.prot g in
let rel_prot = RD.add_vars rel [g_prot_var] in
let rel_prot = RD.assign_var rel_prot x_var g_prot_var in
RD.join rel_local rel_prot
)
in
let rel_local' = restrict_local (is_unprotected ask) rel_local' (W.empty ()) in
let rel_local' = RD.meet rel_local' (getg ()) in
rel_local'
let write_global ?(invariant=false) ask getg sideg (st: relation_components_t) g x =
let rel = st.rel in
let (p, w) = st.priv in
let g_local_var = AV.local g in
let g_unprot_var = AV.unprot g in
let x_var = AV.local x in
let rel_local = RD.add_vars rel [g_local_var] in
let rel_local = RD.assign_var rel_local g_local_var x_var in
let rel_side = RD.add_vars rel_local [g_unprot_var] in
let rel_side = RD.assign_var rel_side g_unprot_var g_local_var in
let rel' = rel_side in
let rel_side = restrict_global rel_side in
sideg () rel_side;
let st' =
(* if is_unprotected ask g then
st (* add, assign, remove gives original local state *)
else
(* restricting g#unprot-s out from oct' gives oct_local *)
{oct = oct_local; priv = (P.add g p, W.add g w)} *)
if is_unprotected ask g then
{st with rel = restrict_local (is_unprotected ask) rel' (W.singleton g)}
else (
let p' = P.add g p in
let w' = W.add g w in
{rel = restrict_local (is_unprotected ask) rel' (W.empty ()); priv = (p', w')}
)
in
let rel_local' = RD.meet st'.rel (getg ()) in
{st' with rel = rel_local'}
let lock ask getg (st: relation_components_t) m = st
let unlock ask getg sideg (st: relation_components_t) m: relation_components_t =
let rel = st.rel in
let (p, w) = st.priv in
let (p_remove, p') = P.partition (fun g -> is_unprotected_without ask g m) p in
let (w_remove, w') = W.partition (fun g -> is_unprotected_without ask g m) w in
let p_a = P.filter (is_protected_by ask m) p in
let w_a = W.filter (is_protected_by ask m) (W.diff w p) in
let big_omega =
let certain = P.elements p_a in
let choice = W.elements w_a in
choice
|> List.map (fun _ -> [true; false])
|> List.n_cartesian_product (* TODO: exponential! *)
|> List.map (fun omega ->
(* list globals where omega is true *)
List.fold_left2 (fun acc g omega_g ->
if omega_g then
g :: acc
else
acc
) certain choice omega
)
in
let rel_side = List.fold_left (fun acc omega ->
let g_prot_vars = List.map AV.prot omega in
let g_local_vars = List.map AV.local omega in
let rel_side1 = RD.add_vars rel g_prot_vars in
let rel_side1 = RD.assign_var_parallel' rel_side1 g_prot_vars g_local_vars in
RD.join acc rel_side1
) (RD.bot ()) big_omega
in
let rel' = rel_side in
let rel_side = restrict_global rel_side in
sideg () rel_side;
let rel_local = restrict_local (fun g -> is_unprotected_without ask g m) rel' w_remove in
let rel_local' = RD.meet rel_local (getg ()) in
{rel = rel_local'; priv = (p', w')}
let thread_join ?(force=false) ask getg exp st = st
let thread_return ask getg sideg tid st = st
let sync ask getg sideg (st: relation_components_t) reason =
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
if ask.f (Q.MustBeSingleThreaded { since_start= true }) then
st
else
(* must be like enter_multithreaded *)
let rel = st.rel in
let (g_vars, gs) =
RD.vars rel
|> List.enum
|> Enum.filter_map (fun var ->
match RD.V.find_metadata var with
| Some (Global g) -> Some (var, g)
| _ -> None
)
|> Enum.uncombine
|> Tuple2.map List.of_enum List.of_enum
in
let g_unprot_vars = List.map AV.unprot gs in
let g_prot_vars = List.map AV.prot gs in
let rel_side = RD.add_vars rel (g_unprot_vars @ g_prot_vars) in
let rel_side = RD.assign_var_parallel' rel_side g_unprot_vars g_vars in
let rel_side = RD.assign_var_parallel' rel_side g_prot_vars g_vars in
let rel_side = restrict_global rel_side in
sideg () rel_side;
(* TODO: why not remove at all? should only remove unprotected? *)
(* let rel_local = RD.remove_vars rel g_vars in
let rel_local' = RD.meet rel_local (getg ()) in
{st with rel = rel_local'} *)
st
| `Join
| `Normal
| `Init
| `Thread ->
st
let escape node ask getg sideg st escaped = (* TODO: Implement *) st
let enter_multithreaded ask getg sideg (st: relation_components_t): relation_components_t =
let rel = st.rel in
let (g_vars, gs) =
RD.vars rel
|> List.enum
|> Enum.filter_map (fun var ->
match RD.V.find_metadata var with
| Some (Global g) -> Some (var, g)
| _ -> None
)
|> Enum.uncombine
|> Tuple2.map List.of_enum List.of_enum
in
let g_unprot_vars = List.map AV.unprot gs in
let g_prot_vars = List.map AV.prot gs in
let rel_side = RD.add_vars rel (g_unprot_vars @ g_prot_vars) in
let rel_side = RD.assign_var_parallel' rel_side g_unprot_vars g_vars in
let rel_side = RD.assign_var_parallel' rel_side g_prot_vars g_vars in
let rel_side = restrict_global rel_side in
sideg () rel_side;
let rel_local = RD.remove_vars rel g_vars in
let rel_local' = RD.meet rel_local (getg ()) in
{rel = rel_local'; priv = startstate ()}
let threadenter ask getg (st: relation_components_t): relation_components_t =
{rel = getg (); priv = startstate ()}
let iter_sys_vars getg vq vf = () (* TODO: or report singleton global for any Global query? *)
let invariant_vars ask getg st = protected_vars ask (* TODO: is this right? *)
let finalize () = ()
module P = PS
end
module CommonPerMutex = functor(RD: RelationDomain.RD) ->
struct
include Protection
module V = RD.V
let remove_globals_unprotected_after_unlock ask m oct =
let newly_unprot var = match V.find_metadata var with
| Some (Global g) -> is_protected_by ask m g && is_unprotected_without ask g m
| _ -> false
in
RD.remove_filter oct newly_unprot
let keep_only_protected_globals ask m oct =
let protected var = match V.find_metadata var with
| Some (Global g) -> is_protected_by ask m g
| _ -> false
in
RD.keep_filter oct protected
end
module PerMutexMeetPrivBase (RD: RelationDomain.RD) =
struct
let invariant_vars ask getg (st: (RD.t, _) RelationDomain.relcomponents_t) =
(* Mutex-meet local states contain precisely the protected global variables,
so we can do fewer queries than {!protected_vars}. *)
RD.vars st.rel
|> List.filter_map (fun var ->
match RD.V.find_metadata var with
| Some (Global g) -> Some g
| _ -> None
)
end
(** Per-mutex meet. *)
module PerMutexMeetPriv (Param: AtomicParam) : S = functor (RD: RelationDomain.RD) ->
struct
open CommonPerMutex(RD)
include MutexGlobals
include PerMutexMeetPrivBase (RD)
module D = Lattice.Unit
module G = RD
module P = UnitP
type relation_components_t = RelationDomain.RelComponents (RD) (D).t
module AV = RD.V
let name () = "PerMutexMeetPriv"
let startstate () = ()
let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var
let get_m_with_mutex_inits ask getg m =
let get_m = getg (V.mutex m) in
let get_mutex_inits = getg V.mutex_inits in
let get_mutex_inits' = keep_only_protected_globals ask m get_mutex_inits in
RD.join get_m get_mutex_inits'
let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g =
let g_var = AV.global g in
let get_mutex_global_g =
let r =
if Param.handle_atomic then
(* Unprotected invariant is one big relation. *)
RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var]
else
getg (V.global g)
in
if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then
(* malloc'ed blobs may not have a value here yet *)
RD.top ()
else
r
in
let get_mutex_inits = getg V.mutex_inits in
let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in
if not (RD.mem_var get_mutex_inits' g_var) then (* TODO: is this just a workaround for an escape bug? https://github.com/goblint/analyzer/pull/1354/files#r1498882657 *)
(* This is an escaped variable whose value was never side-effected to get_mutex_inits' *)
get_mutex_global_g
else
RD.join get_mutex_global_g get_mutex_inits'
let get_mutex_global_g_with_mutex_inits_atomic ask getg =
(* Unprotected invariant is one big relation. *)
let get_mutex_global_g = getg (V.mutex atomic_mutex) in
let get_mutex_inits = getg V.mutex_inits in
RD.join get_mutex_global_g get_mutex_inits
let read_global (ask: Q.ask) getg (st: relation_components_t) g x: RD.t =
let atomic = Param.handle_atomic && ask.f MustBeAtomic in
let rel = st.rel in
(* lock *)
let rel =
if atomic && RD.mem_var rel (AV.global g) then
rel (* Read previous unpublished unprotected write in current atomic section. *)
else if atomic then
RD.meet rel (get_mutex_global_g_with_mutex_inits_atomic ask getg) (* Read unprotected invariant as full relation. *)
else
RD.meet rel (get_mutex_global_g_with_mutex_inits ask getg g)
in
(* read *)
let g_var = AV.global g in
let x_var = AV.local x in
let rel_local = RD.add_vars rel [g_var] in
let rel_local = RD.assign_var rel_local x_var g_var in
(* unlock *)
if not atomic then (
let rel_local' =
if is_unprotected ask g then
RD.remove_vars rel_local [g_var]
else
rel_local
in
rel_local'
)
else
rel_local (* Keep write local as if it were protected by the atomic section. *)
(** Like write global but has option to skip meet with current value, as the value will not have been side-effected to a useful location thus far *)
let write_global_internal ?(skip_meet=false) ?(invariant=false) (ask: Q.ask) getg sideg (st: relation_components_t) g x: relation_components_t =
let atomic = Param.handle_atomic && ask.f MustBeAtomic in
let rel = st.rel in
(* lock *)
let rel =
if skip_meet then
rel
else if atomic && RD.mem_var rel (AV.global g) then
rel (* Read previous unpublished unprotected write in current atomic section. *)
else if atomic then
RD.meet rel (get_mutex_global_g_with_mutex_inits_atomic ask getg) (* Read unprotected invariant as full relation. *)
else
RD.meet rel (get_mutex_global_g_with_mutex_inits ask getg g)
in
(* write *)
let g_var = AV.global g in
let x_var = AV.local x in
let rel_local = RD.add_vars rel [g_var] in
let rel_local = RD.assign_var rel_local g_var x_var in
(* unlock *)
if not atomic then (
let rel_side = RD.keep_vars rel_local [g_var] in
if Param.handle_atomic then
sideg (V.mutex atomic_mutex) rel_side (* Unprotected invariant is one big relation. *)
else
sideg (V.global g) rel_side;
let rel_local' =
if is_unprotected ask g then
RD.remove_vars rel_local [g_var]
else
rel_local
in
{st with rel = rel_local'}
)
else
(* Delay publishing unprotected write in the atomic section. *)
{st with rel = rel_local} (* Keep write local as if it were protected by the atomic section. *)
let write_global = write_global_internal ~skip_meet:false
let write_escape = write_global_internal ~skip_meet:true
let lock ask getg (st: relation_components_t) m =
let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in
(* TODO: somehow actually unneeded here? *)
if not atomic && Locksets.(not (Lockset.mem m (current_lockset ask))) then (
let rel = st.rel in
let get_m = get_m_with_mutex_inits ask getg m in
(* Additionally filter get_m in case it contains variables it no longer protects. E.g. in 36/22. *)
let get_m = keep_only_protected_globals ask m get_m in
let rel' = RD.meet rel get_m in
{st with rel = rel'}
)
else
(* Atomic section locking is recursive. *)
st (* sound w.r.t. recursive lock *)
let unlock ask getg sideg (st: relation_components_t) m: relation_components_t =
let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in
let rel = st.rel in
if not atomic then (
let rel_side = keep_only_protected_globals ask m rel in
sideg (V.mutex m) rel_side;
let rel_local = remove_globals_unprotected_after_unlock ask m rel in
{st with rel = rel_local}
)
else (
(* Publish delayed unprotected write as if it were protected by the atomic section. *)
let rel_side = RD.keep_filter rel (fun var ->
match AV.find_metadata var with
| Some (Global g) -> true
| _ -> false
)
in
(* Unprotected invariant is one big relation. *)
(* If no globals are contained here, none need to be published *)
(* https://github.com/goblint/analyzer/pull/1354 *)
if RD.vars rel_side <> [] then
sideg (V.mutex atomic_mutex) rel_side;
let rel_local =
let newly_unprot var = match AV.find_metadata var with
| Some (Global g) -> is_unprotected_without ask g atomic_mutex
| _ -> false
in
RD.remove_filter rel newly_unprot
in
{st with rel = rel_local}
)
let thread_join ?(force=false) ask getg exp st = st
let thread_return ask getg sideg tid st = st
let sync ask getg sideg (st: relation_components_t) reason =
match reason with
| `Return -> (* required for thread return *)
(* TODO: implement? *)
begin match ThreadId.get_current ask with
| `Lifted x (* when CPA.mem x st.cpa *) ->
st
| _ ->
st
end
| `Join when ConfCheck.branched_thread_creation () ->
if ask.f (Q.MustBeSingleThreaded {since_start = true}) then
st
else
let rel = st.rel in
(* Replace with remove_filter once issues are fixed *)
let g_vars = List.filter (fun var ->
match AV.find_metadata var with
| Some (Global _) -> true
| _ -> false
) (RD.vars rel)
in
let rel_side = RD.keep_vars rel g_vars in
(* If no globals are contained here, none need to be published *)
(* https://github.com/goblint/analyzer/pull/1354 *)
if g_vars <> [] then sideg V.mutex_inits rel_side;
let rel_local = RD.remove_filter rel (fun var ->
match AV.find_metadata var with
| Some (Global g) -> is_unprotected ask g
| _ -> false
)
in
{st with rel = rel_local}
| `Join
| `Normal
| `Init
| `Thread ->
st
let enter_multithreaded ask getg sideg (st: relation_components_t): relation_components_t =
let rel = st.rel in
(* Don't use keep_filter & remove_filter because it would duplicate find_metadata-s. *)
let g_vars = List.filter (fun var ->
match AV.find_metadata var with
| Some (Global _) -> true
| _ -> false
) (RD.vars rel)
in
let rel_side = RD.keep_vars rel g_vars in
sideg V.mutex_inits rel_side;
let rel_local = RD.remove_vars rel g_vars in (* TODO: side effect initial values to mutex_globals? *)
{st with rel = rel_local}
let escape node ask getg sideg (st:relation_components_t) escaped : relation_components_t =
let esc_vars = EscapeDomain.EscapedVars.elements escaped in
let esc_vars = List.filter (fun v -> not v.vglob && RD.Tracked.varinfo_tracked v && RD.mem_var st.rel (AV.local v)) esc_vars in
let escape_one (x:varinfo) st = write_escape ask getg sideg st x x in
List.fold_left (fun st v -> escape_one v st) st esc_vars
let threadenter ask getg (st: relation_components_t): relation_components_t =
{rel = RD.top (); priv = startstate ()}
let init () = ()
let finalize () = ()
end
(** May written variables. *)
module W =
struct
include MayVars
let name () = "W"
end
module type ClusterArg = functor (RD: RelationDomain.RD) ->
sig
module LRD: Lattice.S
val keep_only_protected_globals: Q.ask -> LockDomain.Addr.t -> LRD.t -> LRD.t
val keep_global: varinfo -> LRD.t -> LRD.t
val lock: RD.t -> LRD.t -> LRD.t -> RD.t
val unlock: W.t -> RD.t -> LRD.t
val name: unit -> string
end
(** No clustering. *)
module NoCluster:ClusterArg = functor (RD: RelationDomain.RD) ->
struct
open CommonPerMutex(RD)
module LRD = RD
let keep_only_protected_globals = keep_only_protected_globals
let keep_global g oct =
let g_var = V.global g in
RD.keep_vars oct [g_var]
let lock oct local_m get_m =
RD.meet oct (RD.join local_m get_m)
let unlock w oct_side =
oct_side
let name () = "no-clusters"
end
module type ClusteringArg =
sig
val generate: varinfo list -> varinfo list list
val name: unit -> string
end
(** All clusters of size 1 and 2. *)
module Clustering12: ClusteringArg =
struct
let generate gs =
List.cartesian_product gs gs
|> List.filter (fun (g1, g2) -> CilType.Varinfo.compare g1 g2 <= 0) (* filter flipped ordering, keep equals for next step *)
|> List.map (fun (g1, g2) -> [g1; g2]) (* if g1 = g2, then we get a singleton cluster *)
let name () = "cluster12"
end
(** All clusters of size 2. *)
module Clustering2: ClusteringArg =
struct
let generate gs =
match gs with
| [_] -> [gs] (* use clusters of size 1 if only 1 variable *)
| _ ->
List.cartesian_product gs gs
|> List.filter (fun (g1, g2) -> CilType.Varinfo.compare g1 g2 < 0) (* filter flipped ordering, forbid equals for just clusters of size 2 *)
|> List.map (fun (g1, g2) -> [g1; g2])
let name () = "cluster2"
end
(** All subset clusters. *)
module ClusteringPower: ClusteringArg =
struct
let generate gs =
gs
|> List.map (fun _ -> [true; false])
|> List.n_cartesian_product (* TODO: exponential! *)
|> List.map (fun bs ->
(* list globals where omega is true *)
List.fold_left2 (fun acc g b ->
if b then
g :: acc
else
acc
) [] gs bs
)
let name () = "clusterPow"
end
(** One maximum cluster. *)
module ClusteringMax: ClusteringArg =
struct
let generate gs =
[gs]
let name () = "clusterMax"
end
(** Clusters when clustering is downward-closed. *)
module DownwardClosedCluster (ClusteringArg: ClusteringArg) = functor (RD: RelationDomain.RD) ->
struct
open CommonPerMutex(RD)
module VS = SetDomain.Make (CilType.Varinfo)
module LRD = MapDomain.MapBot (VS) (RD)
let keep_only_protected_globals ask m octs =
(* normal (strong) mapping: contains only still fully protected *)
(* must filter by protection to avoid later meeting with non-protecting *)
LRD.filter (fun gs _ ->
VS.for_all (is_protected_by ask m) gs
) octs
let keep_global g octs =
let g_var = V.global g in
(* normal (strong) mapping: contains only still fully protected *)
let g' = VS.singleton g in
(* If there is no map entry yet which contains the global, default to top rather than bot *)
(* Happens e.g. in 46/86 because of escape *)
let oct = Option.default (RD.top ()) (LRD.find_opt g' octs) in
LRD.singleton g' (RD.keep_vars oct [g_var])
let lock_get_m oct local_m get_m =
let joined = LRD.join local_m get_m in
if M.tracing then M.traceli "relationpriv" "lock_get_m:\n get=%a\n joined=%a" LRD.pretty get_m LRD.pretty joined;
let r = LRD.fold (fun _ -> RD.meet) joined (RD.top ()) in
if M.tracing then M.trace "relationpriv" "meet=%a" RD.pretty r;
let r = RD.meet oct r in
if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r;
r
let lock oct local_m get_m =
if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a" LRD.pretty local_m;
let r = lock_get_m oct local_m get_m in
(* is_bot check commented out because it's unnecessarily expensive *)
(* if RD.is_bot_env r then
failwith "DownwardClosedCluster.lock: not downward closed?"; *)
if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r;
r
let unlock w oct_side =
let vars = RD.vars oct_side in
let gs = List.map (fun var -> match V.find_metadata var with
| Some (Global g) -> g
| _ -> assert false (* oct_side should only contain (protected) globals *)
) vars
in
let clusters =
ClusteringArg.generate gs
|> List.map VS.of_list
|> List.filter (VS.exists (fun g -> W.mem g w)) (* cluster intersection w is non-empty *)
in
let oct_side_cluster gs =
RD.keep_vars oct_side (gs |> VS.elements |> List.map V.global)
in
LRD.add_list_fun clusters oct_side_cluster (LRD.empty ())
let name = ClusteringArg.name
end
(** Clusters when clustering is arbitrary (not necessarily downward-closed). *)
module ArbitraryCluster (ClusteringArg: ClusteringArg): ClusterArg = functor (RD: RelationDomain.RD) ->
struct
module DCCluster = (DownwardClosedCluster(ClusteringArg))(RD)
open CommonPerMutex(RD)
module VS = DCCluster.VS
module LRD1 = DCCluster.LRD
module LRD = Lattice.Prod (LRD1) (LRD1) (* second component is only used between keep_* and lock for additional weak mapping *)
let name = ClusteringArg.name
let filter_map' f m =
LRD1.fold (fun k v acc ->
match f k v with
| Some (k', v') ->
LRD1.add k' (RD.join (LRD1.find k' acc) v') acc
| None ->
acc
) m (LRD1.empty ())
let keep_only_protected_globals ask m (octs, _) =
let lad = DCCluster.keep_only_protected_globals ask m octs in
let lad_weak =
(* backup (weak) mapping: contains any still intersecting with protected, needed for decreasing protecting locksets *)
filter_map' (fun gs oct ->
(* must filter by protection to avoid later meeting with non-protecting *)
let gs' = VS.filter (is_protected_by ask m) gs in
if VS.is_empty gs' then
None
else
(* must restrict cluster down to protected (join) *)
Some (gs', keep_only_protected_globals ask m oct)
) octs
in
(lad, lad_weak)
let keep_global g (octs, _) =
let g_var = V.global g in
let lad = DCCluster.keep_global g octs in
let lad_weak =
(* backup (weak) mapping: contains any still intersecting with protected, needed for decreasing protecting locksets *)
filter_map' (fun gs oct ->
(* must filter by protection to avoid later meeting with non-protecting *)
if VS.mem g gs then
(* must restrict cluster down to m_g (join) *)
Some (VS.singleton g, RD.keep_vars oct [g_var])
else
None
) octs
in
(lad, lad_weak)
let lock oct (local_m, _) (get_m, get_m') =
if M.tracing then M.traceli "relationpriv" "cluster lock: local=%a" LRD1.pretty local_m;
let r =
let locked = DCCluster.lock_get_m oct local_m get_m in
if RD.is_bot_env locked then (
let locked' = DCCluster.lock_get_m oct local_m get_m' in
if RD.is_bot_env locked' then
raise Deadcode
else
locked'
)
else
locked
in
if M.tracing then M.traceu "relationpriv" "-> %a" RD.pretty r;
r
let unlock w oct_side =
(DCCluster.unlock w oct_side, LRD1.bot ())
end
(** Per-mutex meet with TIDs. *)
module PerMutexMeetPrivTID (Param: AtomicParam) (Digest: Digest) (Cluster: ClusterArg): S = functor (RD: RelationDomain.RD) ->
struct
open CommonPerMutex(RD)
include MutexGlobals
include PerMutexMeetPrivBase (RD)
module NC = Cluster(RD)
module Cluster = NC
module LRD = NC.LRD
include PerMutexTidCommon (Digest) (LRD)
module AV = RD.V
module P = UnitP
let name () = "PerMutexMeetPrivTID(" ^ (Cluster.name ()) ^ (if GobConfig.get_bool "ana.relation.priv.must-joined" then ",join" else "") ^ ")"
let get_relevant_writes (ask:Q.ask) m v =
let current = Digest.current ask in
GMutex.fold (fun k v acc ->
if not (Digest.accounted_for ask ~current ~other:k) then
LRD.join acc (Cluster.keep_only_protected_globals ask m v)
else
acc
) v (LRD.bot ())
type relation_components_t = RelationDomain.RelComponents (RD) (D).t
let get_m_with_mutex_inits inits ask getg m =
let get_m = get_relevant_writes ask m (G.mutex @@ getg (V.mutex m)) in
if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.Addr.pretty m LRD.pretty get_m;
let r =
if not inits then
get_m
else
let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in
let get_mutex_inits' = Cluster.keep_only_protected_globals ask m get_mutex_inits in
if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits';
LRD.join get_m get_mutex_inits'
in
if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r;
r
let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var
let get_mutex_global_g_with_mutex_inits inits ask getg g =
let get_mutex_global_g =
if Param.handle_atomic then (
(* Unprotected invariant is one big relation. *)
get_relevant_writes_nofilter ask @@ G.mutex @@ getg (V.mutex atomic_mutex)
|> Cluster.keep_global g
)
else
get_relevant_writes_nofilter ask @@ G.mutex @@ getg (V.global g)
in
if M.tracing then M.traceli "relationpriv" "get_mutex_global_g_with_mutex_inits %a\n get=%a" CilType.Varinfo.pretty g LRD.pretty get_mutex_global_g;
let r =
if not inits then
get_mutex_global_g
else