-
Notifications
You must be signed in to change notification settings - Fork 76
/
base.ml
2214 lines (2110 loc) · 104 KB
/
base.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
(** Value analysis. *)
open Prelude.Ana
open Analyses
open GobConfig
module A = Analyses
module H = Hashtbl
module Q = Queries
module GU = Goblintutil
module ID = ValueDomain.ID
module IdxDom = ValueDomain.IndexDomain
module IntSet = SetDomain.Make (IntDomain.Integers)
module AD = ValueDomain.AD
module Addr = ValueDomain.Addr
module Offs = ValueDomain.Offs
module LF = LibraryFunctions
module CArrays = ValueDomain.CArrays
module BI = IntOps.BigIntOps
let is_global (a: Q.ask) (v: varinfo): bool =
v.vglob || match a (Q.MayEscape v) with `MayBool tv -> tv | _ -> false
let is_static (v:varinfo): bool = v.vstorage == Static
let precious_globs = ref []
let is_precious_glob v = List.exists (fun x -> v.vname = Json.string x) !precious_globs
let privatization = ref false
let is_private (a: Q.ask) (_,_) (v: varinfo): bool =
!privatization && (* must be true *)
(not (ThreadFlag.is_multi a) && is_precious_glob v (* not multi, but precious (earlyglobs) *)
|| match a (Q.MayBePublic v) with `MayBool tv -> not tv | _ -> false) (* usual case where MayBePublic answers *)
module MainFunctor(RVEval:BaseDomain.ExpEvaluator) =
struct
include Analyses.DefaultSpec
exception Top
module VD = BaseDomain.VD
module CPA = BaseDomain.CPA
module Dep = BaseDomain.PartDeps
module Dom = BaseDomain.DomFunctor(RVEval)
module G = BaseDomain.VD
module D = Dom
module C = Dom
module V = Basetype.Variables
let name () = "base"
let startstate v = CPA.bot (), Dep.bot ()
let exitstate v = CPA.bot (), Dep.bot ()
let morphstate v (cpa,dep) = cpa, dep
type cpa = CPA.t
type extra = (varinfo * Offs.t * bool) list
type store = D.t
type value = VD.t
type address = AD.t
type glob_fun = V.t -> G.t
type glob_diff = (V.t * G.t) list
(**************************************************************************
* Helpers
**************************************************************************)
(* hack for char a[] = {"foo"} or {'f','o','o', '\000'} *)
let char_array : (lval, bytes) Hashtbl.t = Hashtbl.create 500
let hash (x,_) = Hashtbl.hash x
let equal (x1,_) (y1,_) = CPA.equal x1 y1
let leq (x1,_) (y1,_) = CPA.leq x1 y1
let compare (x1,_) (y1,_) = CPA.compare x1 y1
(**************************************************************************
* Initializing my variables
**************************************************************************)
let return_varstore = ref dummyFunDec.svar
let return_varinfo () = !return_varstore
let return_var () = AD.from_var (return_varinfo ())
let return_lval (): lval = (Var (return_varinfo ()), NoOffset)
let heap_var ctx =
let info = match (ctx.ask Q.HeapVar) with
| `Varinfo (`Lifted vinfo) -> vinfo
| _ -> failwith("Ran without a malloc analysis.") in
info
let init () =
privatization := get_bool "exp.privatization";
precious_globs := get_list "exp.precious_globs";
return_varstore := Goblintutil.create_var @@ makeVarinfo false "RETURN" voidType
(**************************************************************************
* Abstract evaluation functions
**************************************************************************)
let iDtoIdx n =
match ID.to_int n with
| None -> IdxDom.top ()
| Some n -> IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) n
let unop_ID = function
| Neg -> ID.neg
| BNot -> ID.bitnot
| LNot -> ID.lognot
(* Evaluating Cil's unary operators. *)
let evalunop op typ = function
| `Int v1 -> `Int (ID.cast_to (Cilfacade.get_ikind typ) (unop_ID op v1))
| `Bot -> `Bot
| _ -> VD.top ()
let binop_ID (result_ik: Cil.ikind) = function
| PlusA -> ID.add
| MinusA -> ID.sub
| Mult -> ID.mul
| Div -> ID.div
| Mod -> ID.rem
| Lt -> ID.lt
| Gt -> ID.gt
| Le -> ID.le
| Ge -> ID.ge
| Eq -> ID.eq
| Ne -> ID.ne
| BAnd -> ID.bitand
| BOr -> ID.bitor
| BXor -> ID.bitxor
| Shiftlt -> ID.shift_left
| Shiftrt -> ID.shift_right
| LAnd -> ID.logand
| LOr -> ID.logor
| b -> (fun x y -> (ID.top_of result_ik))
(* Evaluate binop for two abstract values: *)
let evalbinop (op: binop) (t1:typ) (a1:value) (t2:typ) (a2:value) (t:typ) :value =
if M.tracing then M.tracel "eval" "evalbinop %a %a %a\n" d_binop op VD.pretty a1 VD.pretty a2;
(* We define a conversion function for the easy cases when we can just use
* the integer domain operations. *)
let bool_top ik = ID.(join (of_int ik BI.zero) (of_int ik BI.one)) in
(* An auxiliary function for ptr arithmetic on array values. *)
let addToAddr n (addr:Addr.t) =
let typeOffsetOpt o t =
try
Some (typeOffset t o)
with Errormsg.Error ->
None
in
(* adds n to the last offset *)
let rec addToOffset n (t:typ option) = function
| `Index (i, `NoOffset) ->
(* If we have arrived at the last Offset and it is an Index, we add our integer to it *)
`Index(IdxDom.add i (iDtoIdx n), `NoOffset)
| `Field (f, `NoOffset) ->
(* If we have arrived at the last Offset and it is a Field,
* then check if we're subtracting exactly its offsetof.
* If so, n cancels out f exactly.
* This is to better handle container_of hacks. *)
let n_offset = iDtoIdx n in
begin match t with
| Some t ->
let (f_offset_bits, _) = bitsOffset t (Field (f, NoOffset)) in
let f_offset = IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) (BI.of_int (f_offset_bits / 8)) in
begin match IdxDom.(to_bool (eq f_offset (neg n_offset))) with
| Some true -> `NoOffset
| _ -> `Field (f, `Index (n_offset, `NoOffset))
end
| None -> `Field (f, `Index (n_offset, `NoOffset))
end
| `Index (i, o) ->
let t' = BatOption.bind t (typeOffsetOpt (Index (integer 0, NoOffset))) in (* actual index value doesn't matter for typeOffset *)
`Index(i, addToOffset n t' o)
| `Field (f, o) ->
let t' = BatOption.bind t (typeOffsetOpt (Field (f, NoOffset))) in
`Field(f, addToOffset n t' o)
| `NoOffset -> `Index(iDtoIdx n, `NoOffset)
in
let default = function
| Addr.NullPtr when GU.opt_predicate (BI.equal BI.zero) (ID.to_int n) -> Addr.NullPtr
| Addr.SafePtr | Addr.NullPtr when get_bool "exp.ptr-arith-safe" -> Addr.SafePtr
| _ -> Addr.UnknownPtr
in
match Addr.to_var_offset addr with
| [x, o] -> Addr.from_var_offset (x, addToOffset n (Some x.vtype) o)
| _ -> default addr
in
(* The main function! *)
match a1,a2 with
(* For the integer values, we apply the domain operator *)
| `Int v1, `Int v2 ->
let result_ik = Cilfacade.get_ikind t in
`Int (ID.cast_to result_ik (binop_ID result_ik op v1 v2))
(* For address +/- value, we try to do some elementary ptr arithmetic *)
| `Address p, `Int n
| `Int n, `Address p when op=Eq || op=Ne ->
`Int (match ID.to_bool n, AD.to_bool p with
| Some a, Some b -> ID.of_bool (Cilfacade.get_ikind t) (op=Eq && a=b || op=Ne && a<>b)
| _ -> bool_top (Cilfacade.get_ikind t))
| `Address p, `Int n -> begin
match op with
(* For array indexing e[i] and pointer addition e + i we have: *)
| IndexPI | PlusPI ->
`Address (AD.map (addToAddr n) p)
(* Pointer subtracted by a value (e-i) is very similar *)
(* Cast n to the (signed) ptrdiff_ikind, then add the its negated value. *)
| MinusPI ->
let n = ID.neg (ID.cast_to (Cilfacade.ptrdiff_ikind ()) n) in
`Address (AD.map (addToAddr n) p)
| Mod -> `Int (ID.top_of (Cilfacade.ptrdiff_ikind ())) (* we assume that address is actually casted to int first*)
| _ -> `Address AD.top_ptr
end
(* If both are pointer values, we can subtract them and well, we don't
* bother to find the result in most cases, but it's an integer. *)
| `Address p1, `Address p2 -> begin
let result_ik = Cilfacade.get_ikind t in
let eq x y = if AD.is_definite x && AD.is_definite y then Some (AD.Addr.equal (AD.choose x) (AD.choose y)) else None in
match op with
(* TODO use ID.of_incl_list [0; 1] for all comparisons *)
| MinusPP ->
(* when subtracting pointers to arrays, per 6.5.6 of C-standard if we subtract two pointers to the same array, the difference *)
(* between them is the difference in subscript *)
begin
let rec calculateDiffFromOffset x y =
match x, y with
| `Field ((xf:Cil.fieldinfo), xo), `Field((yf:Cil.fieldinfo), yo)
when xf.floc = yf.floc && xf.fname = yf.fname && Cil.typeSig xf.ftype = Cil.typeSig yf.ftype && xf.fbitfield = yf.fbitfield && xf.fattr = yf.fattr ->
calculateDiffFromOffset xo yo
| `Index (i, `NoOffset), `Index(j, `NoOffset) ->
begin
let diff = ValueDomain.IndexDomain.sub i j in
let ik = Cilfacade.get_ikind t in
match ValueDomain.IndexDomain.to_int diff with
| Some z -> `Int(ID.of_int ik z)
| _ -> `Int (ID.top_of ik)
end
| `Index (xi, xo), `Index(yi, yo) when xi = yi ->
calculateDiffFromOffset xo yo
| _ -> `Int (ID.top_of result_ik)
in
if AD.is_definite p1 && AD.is_definite p2 then
match Addr.to_var_offset (AD.choose p1), Addr.to_var_offset (AD.choose p2) with
| [x, xo], [y, yo] when x.vid = y.vid ->
calculateDiffFromOffset xo yo
| _ ->
`Int (ID.top_of result_ik)
else
`Int (ID.top_of result_ik)
end
| Eq ->
let ik = Cilfacade.get_ikind t in
`Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.zero else match eq p1 p2 with Some x when x -> ID.of_int ik BI.one | _ -> bool_top ik)
| Ne ->
let ik = Cilfacade.get_ikind t in
`Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.one else match eq p1 p2 with Some x when x -> ID.of_int ik BI.zero | _ -> bool_top ik)
| _ -> VD.top ()
end
(* For other values, we just give up! *)
| `Bot, _ -> `Bot
| _, `Bot -> `Bot
| _ -> VD.top ()
(* Auxiliary function to append an additional offset to a given offset. *)
let rec add_offset ofs add =
match ofs with
| `NoOffset -> add
| `Field (fld, `NoOffset) -> `Field (fld, add)
| `Field (fld, ofs) -> `Field (fld, add_offset ofs add)
| `Index (exp, `NoOffset) -> `Index (exp, add)
| `Index (exp, ofs) -> `Index (exp, add_offset ofs add)
(* We need the previous function with the varinfo carried along, so we can
* map it on the address sets. *)
let add_offset_varinfo add ad =
match Addr.to_var_offset ad with
| [x,ofs] -> Addr.from_var_offset (x, add_offset ofs add)
| _ -> ad
(* evaluate value using our "query functions" *)
let eval_rv_pre (ask: Q.ask) exp pr =
let binop op e1 e2 =
let equality () =
match ask (Q.MustBeEqual (e1,e2)) with
| `MustBool true ->
if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b\n" d_exp e1 d_exp e2 true;
Some true
| _ -> None
in
let ptrdiff_ikind = match !ptrdiffType with TInt (ik,_) -> ik | _ -> assert false in
match op with
| MinusA when equality () = Some true ->
let ik = Cilfacade.get_ikind (Cil.typeOf exp) in
Some (`Int (ID.of_int ik BI.zero))
| MinusPI
| MinusPP when equality () = Some true -> Some (`Int (ID.of_int ptrdiff_ikind BI.zero))
| MinusPI
| MinusPP when equality () = Some false -> Some (`Int (ID.of_excl_list ptrdiff_ikind [BI.zero]))
| Le
| Ge when equality () = Some true ->
let ik = Cilfacade.get_ikind (Cil.typeOf exp) in
Some (`Int (ID.of_bool ik true))
| Lt
| Gt when equality () = Some true ->
let ik = Cilfacade.get_ikind (Cil.typeOf exp) in
Some (`Int (ID.of_bool ik false))
| Eq -> (match equality () with Some tv ->
let ik = Cilfacade.get_ikind (Cil.typeOf exp) in
Some (`Int (ID.of_bool ik tv)) | None -> None)
| Ne -> (match equality () with Some tv ->
let ik = Cilfacade.get_ikind (Cil.typeOf exp) in
Some (`Int (ID.of_bool ik (not tv))) | None -> None)
| _ -> None
in
match exp with
| BinOp (op,arg1,arg2,_) -> binop op arg1 arg2
| _ -> None
(**************************************************************************
* State functions
**************************************************************************)
let globalize ?(privates=false) a (cpa,dep): cpa * glob_diff =
(* For each global variable, we create the diff *)
let add_var (v: varinfo) (value) (cpa,acc) =
if M.tracing then M.traceli "globalize" ~var:v.vname "Tracing for %s\n" v.vname;
let res =
if is_global a v && ((privates && not (is_precious_glob v)) || not (is_private a (cpa,dep) v)) then begin
if M.tracing then M.tracec "globalize" "Publishing its value: %a\n" VD.pretty value;
(CPA.remove v cpa, (v,value) :: acc)
end else
(cpa,acc)
in
if M.tracing then M.traceu "globalize" "Done!\n";
res
in
(* We fold over the local state, and collect the globals *)
CPA.fold add_var cpa (cpa, [])
let sync' privates multi ctx: D.t * glob_diff =
let cpa,dep = ctx.local in
let privates = privates || (!GU.earlyglobs && not multi) in
let cpa, diff = if !GU.earlyglobs || multi then globalize ~privates:privates ctx.ask ctx.local else (cpa,[]) in
(cpa, dep), diff
let sync ctx = sync' false (ThreadFlag.is_multi ctx.ask) ctx
let publish_all ctx =
List.iter (fun ((x,d)) -> ctx.sideg x d) (snd (sync' true true ctx))
(** [get st addr] returns the value corresponding to [addr] in [st]
* adding proper dependencies.
* For the exp argument it is always ok to put None. This means not using precise information about
* which part of an array is involved. *)
let rec get ?(full=false) a (gs: glob_fun) (st,dep: store) (addrs:address) (exp:exp option): value =
let at = AD.get_type addrs in
let firstvar = if M.tracing then try (List.hd (AD.to_var_may addrs)).vname with _ -> "" else "" in
let get_global x = gs x in
if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a\n" AD.pretty addrs CPA.pretty st;
(* Finding a single varinfo*offset pair *)
let res =
let f_addr (x, offs) =
(* get hold of the variable value, either from local or global state *)
let var = if (!GU.earlyglobs || ThreadFlag.is_multi a) && is_global a x then
match CPA.find x st with
| `Bot -> (if M.tracing then M.tracec "get" "Using global invariant.\n"; get_global x)
| x -> (if M.tracing then M.tracec "get" "Using privatized version.\n"; x)
else begin
if M.tracing then M.tracec "get" "Singlethreaded mode.\n";
CPA.find x st
end
in
let v = VD.eval_offset a (fun x -> get a gs (st,dep) x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in
if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.from_var_offset (x, offs)) VD.pretty v;
if full then v else match v with
| `Blob (c,s,_) -> c
| x -> x
in
let f x =
match Addr.to_var_offset x with
| [x] -> f_addr x (* normal reference *)
| _ when x = Addr.NullPtr -> VD.bot () (* null pointer *)
| _ -> `Int (ID.top_of IChar) (* string pointer *)
in
(* We form the collecting function by joining *)
let c x = match x with (* If address type is arithmetic, and our value is an int, we cast to the correct ik *)
| `Int _ when Cil.isArithmeticType at -> VD.cast at x
| _ -> x
in
let f x a = VD.join (c @@ f x) a in (* Finally we join over all the addresses in the set. If any of the
* addresses is a topped value, joining will fail. *)
try AD.fold f addrs (VD.bot ()) with SetDomain.Unsupported _ -> VD.top ()
in
if M.tracing then M.traceu "get" "Result: %a\n" VD.pretty res;
res
let is_always_unknown variable = variable.vstorage = Extern || Ciltools.is_volatile_tp variable.vtype
(**************************************************************************
* Auxiliary functions for function calls
**************************************************************************)
(* The normal haskell zip that throws no exception *)
let rec zip x y = match x,y with
| (x::xs), (y::ys) -> (x,y) :: zip xs ys
| _ -> []
(* From a list of values, presumably arguments to a function, simply extract
* the pointer arguments. *)
let get_ptrs (vals: value list): address list =
let f x acc = match x with
| `Address adrs when AD.is_top adrs ->
M.warn_each "Unknown address given as function argument"; acc
| `Address adrs when AD.to_var_may adrs = [] -> acc
| `Address adrs ->
let typ = AD.get_type adrs in
if isFunctionType typ then acc else adrs :: acc
| `Top -> M.warn_each "Unknown value type given as function argument"; acc
| _ -> acc
in
List.fold_right f vals []
(* Get the list of addresses accessable immediately from a given address, thus
* all pointers within a structure should be considered, but we don't follow
* pointers. We return a flattend representation, thus simply an address (set). *)
let reachable_from_address (ask: Q.ask) (gs:glob_fun) st (adr: address): address =
if M.tracing then M.tracei "reachability" "Checking for %a\n" AD.pretty adr;
let empty = AD.empty () in
let rec reachable_from_value (value: value) =
if M.tracing then M.trace "reachability" "Checking value %a\n" VD.pretty value;
match value with
| `Top ->
let typ = AD.get_type adr in
let warning = "Unknown value in " ^ AD.short 800 adr ^ " could be an escaped pointer address!" in
if VD.is_immediate_type typ then () else M.warn_each warning; empty
| `Bot -> (*M.debug "A bottom value when computing reachable addresses!";*) empty
| `Address adrs when AD.is_top adrs ->
let warning = "Unknown address in " ^ AD.short 800 adr ^ " has escaped." in
M.warn_each warning; adrs (* return known addresses still to be a bit more sane (but still unsound) *)
(* The main thing is to track where pointers go: *)
| `Address adrs -> adrs
(* Unions are easy, I just ingore the type info. *)
| `Union (t,e) -> reachable_from_value e
(* For arrays, we ask to read from an unknown index, this will cause it
* join all its values. *)
| `Array a -> reachable_from_value (ValueDomain.CArrays.get ask a (ExpDomain.top (), ValueDomain.ArrIdxDomain.top ()))
| `Blob (e,_,_) -> reachable_from_value e
| `List e -> reachable_from_value (`Address (ValueDomain.Lists.entry_rand e))
| `Struct s -> ValueDomain.Structs.fold (fun k v acc -> AD.join (reachable_from_value v) acc) s empty
| `Int _ -> empty
in
let res = reachable_from_value (get ask gs st adr None) in
if M.tracing then M.traceu "reachability" "Reachable addresses: %a\n" AD.pretty res;
res
(* The code for getting the variables reachable from the list of parameters.
* This section is very confusing, because I use the same construct, a set of
* addresses, as both AD elements abstracting individual (ambiguous) addresses
* and the workset of visited addresses. *)
let reachable_vars (ask: Q.ask) (args: address list) (gs:glob_fun) (st: store): address list =
if M.tracing then M.traceli "reachability" "Checking reachable arguments from [%a]!\n" (d_list ", " AD.pretty) args;
let empty = AD.empty () in
(* We begin looking at the parameters: *)
let argset = List.fold_right (AD.join) args empty in
let workset = ref argset in
(* And we keep a set of already visited variables *)
let visited = ref empty in
while not (AD.is_empty !workset) do
visited := AD.union !visited !workset;
(* ok, let's visit all the variables in the workset and collect the new variables *)
let visit_and_collect var (acc: address): address =
let var = AD.singleton var in (* Very bad hack! Pathetic really! *)
AD.union (reachable_from_address ask gs st var) acc in
let collected = AD.fold visit_and_collect !workset empty in
(* And here we remove the already visited variables *)
workset := AD.diff collected !visited
done;
(* Return the list of elements that have been visited. *)
if M.tracing then M.traceu "reachability" "All reachable vars: %a\n" AD.pretty !visited;
List.map AD.singleton (AD.elements !visited)
let drop_non_ptrs (st:CPA.t) : CPA.t =
if CPA.is_top st then st else
let rec replace_val = function
| `Address _ as v -> v
| `Blob (v,s,o) ->
begin match replace_val v with
| `Blob (`Top,_,_)
| `Top -> `Top
| t -> `Blob (t,s,o)
end
| `Struct s ->
let one_field fl vl st =
match replace_val vl with
| `Top -> st
| v -> ValueDomain.Structs.replace st fl v
in
`Struct (ValueDomain.Structs.fold one_field (ValueDomain.Structs.top ()) s)
| _ -> `Top
in
CPA.map replace_val st
let drop_ints (st:CPA.t) : CPA.t =
if CPA.is_top st then st else
let rec replace_val = function
| `Int _ -> `Top
| `Array n -> `Array (ValueDomain.CArrays.map replace_val n)
| `Struct n -> `Struct (ValueDomain.Structs.map replace_val n)
| `Union (f,v) -> `Union (f,replace_val v)
| `Blob (n,s,o) -> `Blob (replace_val n,s,o)
| `Address x -> `Address (ValueDomain.AD.map ValueDomain.Addr.drop_ints x)
| x -> x
in
CPA.map replace_val st
let drop_interval = CPA.map (function `Int x -> `Int (ID.no_interval x) | x -> x)
let context (cpa,dep) =
let f t f (cpa,dep) = if t then f cpa, dep else cpa, dep in
(cpa,dep) |>
f !GU.earlyglobs (CPA.filter (fun k v -> not (V.is_global k) || is_precious_glob k))
%> f (get_bool "exp.addr-context") drop_non_ptrs
%> f (get_bool "exp.no-int-context") drop_ints
%> f (get_bool "exp.no-interval-context") drop_interval
let context_cpa (cpa,dep) = fst @@ context (cpa,dep)
let convertToQueryLval x =
let rec offsNormal o =
let toInt i =
match IdxDom.to_int i with
| Some x ->
(* TODO: Handle values outside of int64 *)
let x = BI.to_int64 x in
Const (CInt64 (x,IInt, None))
| _ -> mkCast (Const (CStr "unknown")) intType
in
match o with
| `NoOffset -> `NoOffset
| `Field (f,o) -> `Field (f,offsNormal o)
| `Index (i,o) -> `Index (toInt i,offsNormal o)
in
match x with
| ValueDomain.AD.Addr.Addr (v,o) ->[v,offsNormal o]
| _ -> []
let addrToLvalSet a =
let add x y = Q.LS.add y x in
try
AD.fold (fun e c -> List.fold_left add c (convertToQueryLval e)) a (Q.LS.empty ())
with SetDomain.Unsupported _ -> Q.LS.top ()
let reachable_top_pointers_types ctx (ps: AD.t) : Queries.TS.t =
let module TS = Queries.TS in
let empty = AD.empty () in
let reachable_from_address (adr: address) =
let with_type t = function
| (ad,ts,true) ->
begin match unrollType t with
| TPtr (p,_) ->
(ad, TS.add (unrollType p) ts, false)
| _ ->
(ad, ts, false)
end
| x -> x
in
let with_field (a,t,b) = function
| `Top -> (AD.empty (), TS.top (), false)
| `Bot -> (a,t,false)
| `Lifted f -> with_type f.ftype (a,t,b)
in
let rec reachable_from_value (value: value) =
match value with
| `Top -> (empty, TS.top (), true)
| `Bot -> (empty, TS.bot (), false)
| `Address adrs when AD.is_top adrs -> (empty,TS.bot (), true)
| `Address adrs -> (adrs,TS.bot (), AD.has_unknown adrs)
| `Union (t,e) -> with_field (reachable_from_value e) t
| `Array a -> reachable_from_value (ValueDomain.CArrays.get ctx.ask a (ExpDomain.top(), ValueDomain.ArrIdxDomain.top ()))
| `Blob (e,_,_) -> reachable_from_value e
| `List e -> reachable_from_value (`Address (ValueDomain.Lists.entry_rand e))
| `Struct s ->
let join_tr (a1,t1,_) (a2,t2,_) = AD.join a1 a2, TS.join t1 t2, false in
let f k v =
join_tr (with_type k.ftype (reachable_from_value v))
in
ValueDomain.Structs.fold f s (empty, TS.bot (), false)
| `Int _ -> (empty, TS.bot (), false)
in
reachable_from_value (get ctx.ask ctx.global ctx.local adr None)
in
let visited = ref empty in
let work = ref ps in
let collected = ref (TS.empty ()) in
while not (AD.is_empty !work) do
let next = ref empty in
let do_one a =
let (x,y,_) = reachable_from_address (AD.singleton a) in
collected := TS.union !collected y;
next := AD.union !next x
in
if not (AD.is_top !work) then
AD.iter do_one !work;
visited := AD.union !visited !work;
work := AD.diff !next !visited
done;
!collected
(* The evaluation function as mutually recursive eval_lv & eval_rv *)
let rec eval_rv (a: Q.ask) (gs:glob_fun) (st: store) (exp:exp): value =
let rec do_offs def = function (* for types that only have one value *)
| Field (fd, offs) -> begin
match Goblintutil.is_blessed (TComp (fd.fcomp, [])) with
| Some v -> do_offs (`Address (AD.singleton (Addr.from_var_offset (v,convert_offset a gs st (Field (fd, offs)))))) offs
| None -> do_offs def offs
end
| Index (_, offs) -> do_offs def offs
| NoOffset -> def
in
(* we have a special expression that should evaluate to top ... *)
if exp = MyCFG.unknown_exp then VD.top () else
(* First we try with query functions --- these are currently more precise.
* Ideally we would meet both values, but we fear types might not match. (bottom) *)
match eval_rv_pre a exp st with
| Some x -> x
| None ->
(* query functions were no help ... now try with values*)
match (if get_bool "exp.lower-constants" then constFold true exp else exp) with
(* Integer literals *)
(* seems like constFold already converts CChr to CInt64 *)
| Const (CChr x) -> eval_rv a gs st (Const (charConstToInt x)) (* char becomes int, see Cil doc/ISO C 6.4.4.4.10 *)
| Const (CInt64 (num,ikind,str)) ->
(match str with Some x -> M.tracel "casto" "CInt64 (%s, %a, %s)\n" (Int64.to_string num) d_ikind ikind x | None -> ());
`Int (ID.cast_to ikind (IntDomain.of_const (num,ikind,str)))
(* String literals *)
| Const (CStr x) -> `Address (AD.from_string x) (* normal 8-bit strings, type: char* *)
| Const (CWStr xs as c) -> (* wide character strings, type: wchar_t* *)
let x = Pretty.sprint 80 (d_const () c) in (* escapes, see impl. of d_const in cil.ml *)
let x = String.sub x 2 (String.length x - 3) in (* remove surrounding quotes: L"foo" -> foo *)
`Address (AD.from_string x) (* `Address (AD.str_ptr ()) *)
(* Variables and address expressions *)
| Lval (Var v, ofs) -> do_offs (get a gs st (eval_lv a gs st (Var v, ofs)) (Some exp)) ofs
(*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*)
| Lval (Mem e, ofs) ->
(*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*)
let rec contains_vla (t:typ) = match t with
| TPtr (t, _) -> contains_vla t
| TArray(t, None, args) -> true
| TArray(t, Some exp, args) when isConstant exp -> contains_vla t
| TArray(t, Some exp, args) -> true
| _ -> false
in
let b = Mem e, NoOffset in (* base pointer *)
let t = typeOfLval b in (* static type of base *)
let p = eval_lv a gs st b in (* abstract base addresses *)
let v = (* abstract base value *)
let open Addr in
(* pre VLA: *)
(* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *)
let cast_ok = function
| Addr a ->
begin
match Cil.isInteger (sizeOf t), Cil.isInteger (sizeOf (get_type_addr a)) with
| Some i1, Some i2 -> Int64.compare i1 i2 <= 0
| _ ->
if contains_vla t || contains_vla (get_type_addr a) then
begin
(* TODO: Is this ok? *)
M.warn "Casting involving a VLA is assumed to work";
true
end
else
false
end
| _ -> false
in
if AD.for_all cast_ok p then
get a gs st p (Some exp) (* downcasts are safe *)
else
VD.top () (* upcasts not! *)
in
let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *)
M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v';
let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *)
let v' = do_offs v' ofs in (* handle blessed fields? *)
v'
(* Binary operators *)
(* Eq/Ne when both values are equal and casted to the same type *)
| BinOp (op, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 && (op = Eq || op = Ne) ->
let a1 = eval_rv a gs st e1 in
let a2 = eval_rv a gs st e2 in
let both_arith_type = isArithmeticType (typeOf e1) && isArithmeticType (typeOf e2) in
let is_safe = (VD.equal a1 a2 || VD.is_safe_cast t1 (typeOf e1) && VD.is_safe_cast t2 (typeOf e2)) && not both_arith_type in
M.tracel "cast" "remove cast on both sides for %a? -> %b\n" d_exp exp is_safe;
if is_safe then ( (* we can ignore the casts if the values are equal anyway, or if the casts can't change the value *)
let e1 = if isArithmeticType (typeOf e1) then c1 else e1 in
let e2 = if isArithmeticType (typeOf e2) then c2 else e2 in
eval_rv a gs st (BinOp (op, e1, e2, typ))
)
else
let a1 = eval_rv a gs st c1 in
let a2 = eval_rv a gs st c2 in
evalbinop op t1 a1 t2 a2 typ
| BinOp (op,arg1,arg2,typ) ->
let a1 = eval_rv a gs st arg1 in
let a2 = eval_rv a gs st arg2 in
let t1 = typeOf arg1 in
let t2 = typeOf arg2 in
evalbinop op t1 a1 t2 a2 typ
(* Unary operators *)
| UnOp (op,arg1,typ) ->
let a1 = eval_rv a gs st arg1 in
evalunop op typ a1
(* The &-operator: we create the address abstract element *)
| AddrOf lval -> `Address (eval_lv a gs st lval)
(* CIL's very nice implicit conversion of an array name [a] to a pointer
* to its first element [&a[0]]. *)
| StartOf lval ->
let array_ofs = `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset) in
let array_start ad =
match Addr.to_var_offset ad with
| [x, offs] -> Addr.from_var_offset (x, add_offset offs array_ofs)
| _ -> ad
in
`Address (AD.map array_start (eval_lv a gs st lval))
| CastE (t, Const (CStr x)) -> (* VD.top () *) eval_rv a gs st (Const (CStr x)) (* TODO safe? *)
| CastE (t, exp) ->
let v = eval_rv a gs st exp in
VD.cast ~torg:(typeOf exp) t v
| _ -> VD.top ()
(* A hackish evaluation of expressions that should immediately yield an
* address, e.g. when calling functions. *)
and eval_fv a (gs:glob_fun) st (exp:exp): AD.t =
match exp with
| Lval lval -> eval_lv a gs st lval
| _ -> eval_tv a gs st exp
(* Used also for thread creation: *)
and eval_tv a (gs:glob_fun) st (exp:exp): AD.t =
match (eval_rv a gs st exp) with
| `Address x -> x
| _ -> M.bailwith "Problems evaluating expression to function calls!"
and eval_int a gs st exp =
match eval_rv a gs st exp with
| `Int x -> x
| _ -> ID.top_of (Cilfacade.get_ikind (Cil.typeOf exp))
(* A function to convert the offset to our abstract representation of
* offsets, i.e. evaluate the index expression to the integer domain. *)
and convert_offset a (gs:glob_fun) (st: store) (ofs: offset) =
match ofs with
| NoOffset -> `NoOffset
| Field (fld, ofs) -> `Field (fld, convert_offset a gs st ofs)
| Index (exp, ofs) ->
let exp_rv = eval_rv a gs st exp in
match exp_rv with
| `Int i -> `Index (iDtoIdx i, convert_offset a gs st ofs)
| `Top -> `Index (IdxDom.top (), convert_offset a gs st ofs)
| `Bot -> `Index (IdxDom.bot (), convert_offset a gs st ofs)
| _ -> M.bailwith "Index not an integer value"
(* Evaluation of lvalues to our abstract address domain. *)
and eval_lv (a: Q.ask) (gs:glob_fun) st (lval:lval): AD.t =
let rec do_offs def = function
| Field (fd, offs) -> begin
match Goblintutil.is_blessed (TComp (fd.fcomp, [])) with
| Some v -> do_offs (AD.singleton (Addr.from_var_offset (v,convert_offset a gs st (Field (fd, offs))))) offs
| None -> do_offs def offs
end
| Index (_, offs) -> do_offs def offs
| NoOffset -> def
in
match lval with
| Var x, NoOffset when (not x.vglob) && Goblintutil.is_blessed x.vtype<> None ->
begin match Goblintutil.is_blessed x.vtype with
| Some v -> AD.singleton (Addr.from_var v)
| _ -> AD.singleton (Addr.from_var_offset (x, convert_offset a gs st NoOffset))
end
(* The simpler case with an explicit variable, e.g. for [x.field] we just
* create the address { (x,field) } *)
| Var x, ofs ->
if x.vglob
then AD.singleton (Addr.from_var_offset (x, convert_offset a gs st ofs))
else do_offs (AD.singleton (Addr.from_var_offset (x, convert_offset a gs st ofs))) ofs
(* The more complicated case when [exp = & x.field] and we are asked to
* evaluate [(\*exp).subfield]. We first evaluate [exp] to { (x,field) }
* and then add the subfield to it: { (x,field.subfield) }. *)
| Mem n, ofs -> begin
match (eval_rv a gs st n) with
| `Address adr -> do_offs (AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr) ofs
| `Bot -> AD.bot ()
| _ -> let str = Pretty.sprint ~width:80 (Pretty.dprintf "%a " d_lval lval) in
M.debug ("Failed evaluating "^str^" to lvalue"); do_offs AD.unknown_ptr ofs
end
(* run eval_rv from above and keep a result that is bottom *)
(* this is needed for global variables *)
let eval_rv_keep_bot = eval_rv
(* run eval_rv from above, but change bot to top to be sound for programs with undefined behavior. *)
(* Previously we only gave sound results for programs without undefined behavior, so yielding bot for accessing an uninitialized array was considered ok. Now only [invariant] can yield bot/Deadcode if the condition is known to be false but evaluating an expression should not be bot. *)
let eval_rv (a: Q.ask) (gs:glob_fun) (st: store) (exp:exp): value =
try
let r = eval_rv a gs st exp in
if M.tracing then M.tracel "eval" "eval_rv %a = %a\n" d_exp exp VD.pretty r;
if VD.is_bot r then VD.top_value (typeOf exp) else r
with IntDomain.ArithmeticOnIntegerBot _ ->
ValueDomain.Compound.top_value (typeOf exp)
(* Evaluate an expression containing only locals. This is needed for smart joining the partitioned arrays where ctx is not accessible. *)
(* This will yield `Top for expressions containing any access to globals, and does not make use of the query system. *)
(* Wherever possible, don't use this but the query system or normal eval_rv instead. *)
let eval_exp x (exp:exp) =
(* Since ctx is not available here, we need to make some adjustments *)
let knownothing = fun _ -> `Top in (* our version of ask *)
let gs = fun _ -> `Top in (* the expression is guaranteed to not contain globals *)
match (eval_rv knownothing gs x exp) with
| `Int x -> ValueDomain.ID.to_int x
| _ -> None
let eval_funvar ctx fval: varinfo list =
try
let fp = eval_fv ctx.ask ctx.global ctx.local fval in
if AD.mem Addr.UnknownPtr fp then begin
M.warn_each ("Function pointer " ^ sprint d_exp fval ^ " may contain unknown functions.");
dummyFunDec.svar :: AD.to_var_may fp
end else
AD.to_var_may fp
with SetDomain.Unsupported _ ->
M.warn_each ("Unknown call to function " ^ sprint d_exp fval ^ ".");
[dummyFunDec.svar]
(* interpreter end *)
let query ctx (q:Q.t) =
let to_int = BI.to_int64 in
match q with
| Q.EvalFunvar e ->
begin
let fs = eval_funvar ctx e in
(* Messages.report ("Base: I should know it! "^string_of_int (List.length fs));*)
`LvalSet (List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs)
end
| Q.EvalInt e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
| `Int i when ID.is_int i -> `Int (to_int (Option.get (ID.to_int i)))
| `Bot -> `Bot
| v -> M.warn ("Query function answered " ^ (VD.short 20 v)); `Top
end
| Q.EvalLength e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
| `Address a ->
let slen = List.map String.length (AD.to_string a) in
let lenOf = function
| TArray (_, l, _) -> (try Some (lenOfArray l) with _ -> None)
| _ -> None
in
let alen = List.filter_map (fun v -> lenOf v.vtype) (AD.to_var_may a) in
let d = List.fold_left ID.join (ID.bot_of (Cilfacade.ptrdiff_ikind ())) (List.map (ID.of_int (Cilfacade.ptrdiff_ikind ()) %BI.of_int) (slen @ alen)) in
(* ignore @@ printf "EvalLength %a = %a\n" d_exp e ID.pretty d; *)
(match ID.to_int d with Some i -> `Int (to_int i) | None -> `Top)
| `Bot -> `Bot
| _ -> `Top
end
| Q.BlobSize e -> begin
let p = eval_rv ctx.ask ctx.global ctx.local e in
(* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *)
match p with
| `Address a ->
let r = get ~full:true ctx.ask ctx.global ctx.local a None in
(* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *)
(match r with
| `Blob (_,s,_) -> (match ID.to_int s with Some i -> `Int (to_int i) | None -> `Top)
| _ -> `Top)
| _ -> `Top
end
| Q.MayPointTo e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
| `Address a ->
let s = addrToLvalSet a in
if AD.mem Addr.UnknownPtr a
then `LvalSet (Q.LS.add (dummyFunDec.svar, `NoOffset) s)
else `LvalSet s
| `Bot -> `Bot
| _ -> `Top
end
| Q.ReachableFrom e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
| `Top -> `Top
| `Bot -> `Bot
| `Address a when AD.is_top a || AD.mem Addr.UnknownPtr a ->
`LvalSet (Q.LS.top ())
| `Address a ->
let xs = List.map addrToLvalSet (reachable_vars ctx.ask [a] ctx.global ctx.local) in
let addrs = List.fold_left (Q.LS.join) (Q.LS.empty ()) xs in
`LvalSet addrs
| _ -> `LvalSet (Q.LS.empty ())
end
| Q.ReachableUkTypes e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
| `Top -> `Top
| `Bot -> `Bot
| `Address a when AD.is_top a || AD.mem Addr.UnknownPtr a ->
`TypeSet (Q.TS.top ())
| `Address a ->
`TypeSet (reachable_top_pointers_types ctx a)
| _ -> `TypeSet (Q.TS.empty ())
end
| Q.EvalStr e -> begin
match eval_rv ctx.ask ctx.global ctx.local e with
(* exactly one string in the set (works for assignments of string constants) *)
| `Address a when List.length (AD.to_string a) = 1 -> (* exactly one string *)
`Str (List.hd (AD.to_string a))
(* check if we have an array of chars that form a string *)
(* TODO return may-points-to-set of strings *)
| `Address a when List.length (AD.to_string a) > 1 -> (* oh oh *)
M.debug_each @@ "EvalStr (" ^ sprint d_exp e ^ ") returned " ^ AD.short 80 a;
`Top
| `Address a when List.length (AD.to_var_may a) = 1 -> (* some other address *)
(* Cil.varinfo * (AD.Addr.field, AD.Addr.idx) Lval.offs *)
(* ignore @@ printf "EvalStr `Address: %a -> %s (must %i, may %i)\n" d_plainexp e (VD.short 80 (`Address a)) (List.length @@ AD.to_var_must a) (List.length @@ AD.to_var_may a); *)
begin match unrollType (typeOf e) with
| TPtr(TInt(IChar, _), _) ->
let v, offs = Q.LS.choose @@ addrToLvalSet a in
let ciloffs = Lval.CilLval.to_ciloffs offs in
let lval = Var v, ciloffs in
(try `Str (Bytes.to_string (Hashtbl.find char_array lval))
with Not_found -> `Top)
| _ -> (* what about ISChar and IUChar? *)
(* ignore @@ printf "Type %a\n" d_plaintype t; *)
`Top
end
| x ->
(* ignore @@ printf "EvalStr Unknown: %a -> %s\n" d_plainexp e (VD.short 80 x); *)
`Top
end
| Q.MustBeEqual (e1, e2) -> begin
let e1_val = eval_rv ctx.ask ctx.global ctx.local e1 in
let e2_val = eval_rv ctx.ask ctx.global ctx.local e2 in
match e1_val, e2_val with
| `Int i1, `Int i2 -> begin
match ID.to_int i1, ID.to_int i2 with
| Some i1', Some i2' when i1' = i2' -> `MustBool true
| _ -> `MustBool false
end
| _ -> `MustBool false
end
| Q.MayBeEqual (e1, e2) -> begin
(* Printf.printf "----------------------> may equality check for %s and %s \n" (ExpDomain.short 20 (`Lifted e1)) (ExpDomain.short 20 (`Lifted e2)); *)
let e1_val = eval_rv ctx.ask ctx.global ctx.local e1 in
let e2_val = eval_rv ctx.ask ctx.global ctx.local e2 in
match e1_val, e2_val with
| `Int i1, `Int i2 -> begin
(* This should behave like == and also work on different int types, hence the cast (just like with == in C) *)
let e1_ik = Cilfacade.get_ikind (Cil.typeOf e1) in
let e2_ik = Cilfacade.get_ikind (Cil.typeOf e2) in
let ik= Cil.commonIntKind e1_ik e2_ik in
if ID.is_bot (ID.meet (ID.cast_to ik i1) (ID.cast_to ik i2)) then
begin
(* Printf.printf "----------------------> NOPE may equality check for %s and %s \n" (ExpDomain.short 20 (`Lifted e1)) (ExpDomain.short 20 (`Lifted e2)); *)
`MayBool false
end
else `MayBool true
end
| _ -> `MayBool true
end
| Q.MayBeLess (e1, e2) -> begin
(* Printf.printf "----------------------> may check for %s < %s \n" (ExpDomain.short 20 (`Lifted e1)) (ExpDomain.short 20 (`Lifted e2)); *)
let e1_val = eval_rv ctx.ask ctx.global ctx.local e1 in
let e2_val = eval_rv ctx.ask ctx.global ctx.local e2 in
match e1_val, e2_val with
| `Int i1, `Int i2 -> begin
match (ID.minimal i1), (ID.maximal i2) with
| Some i1', Some i2' ->
if i1' >= i2' then
begin
(* Printf.printf "----------------------> NOPE may check for %s < %s \n" (ExpDomain.short 20 (`Lifted e1)) (ExpDomain.short 20 (`Lifted e2)); *)
`MayBool false
end
else `MayBool true
| _ -> `MayBool true
end
| _ -> `MayBool true
end
| _ -> Q.Result.top ()
let update_variable variable value state =
if ((get_bool "exp.volatiles_are_top") && (is_always_unknown variable)) then
CPA.add variable (VD.top ()) state
else
CPA.add variable value state
(** Add dependencies between a value and the expression it (or any of its contents) are partitioned by *)
let add_partitioning_dependencies (x:varinfo) (value:VD.t) (st,dep:store):store =