diff --git a/docs/user-guide/annotating.md b/docs/user-guide/annotating.md index b732e3aaa9..cdfb892ffe 100644 --- a/docs/user-guide/annotating.md +++ b/docs/user-guide/annotating.md @@ -21,10 +21,11 @@ The attribute `goblint_context` can be used to fine-tune function contexts. The following string arguments are supported: 1. `base.interval`/`base.no-interval` to override the `ana.base.context.interval` option. -2. `base.int`/`base.no-int` to override the `ana.base.context.interval` option. -3. `base.non-ptr`/`base.no-non-ptr` to override the `ana.base.context.non-ptr` option. -4. `relation.context`/`relation.no-context` to override the `ana.relation.context` option. -5. `widen`/`no-widen` to override the `ana.context.widen` option. +2. `base.interval_set`/`base.no-interval_set` to override the `ana.base.context.interval_set` option. +3. `base.int`/`base.no-int` to override the `ana.base.context.interval` option. +4. `base.non-ptr`/`base.no-non-ptr` to override the `ana.base.context.non-ptr` option. +5. `relation.context`/`relation.no-context` to override the `ana.relation.context` option. +6. `widen`/`no-widen` to override the `ana.context.widen` option. ### Apron attributes The Apron library can be set to only track variables with the attribute `goblint_apron_track` @@ -40,7 +41,7 @@ struct array { int arr[5] __attribute__((goblint_array_domain("partitioned"))); }; ``` -It is also possible to annotate a type, so that all arrays of this type without an own attribute will use this one: +It is also possible to annotate a type, so that all arrays of this type without an own attribute will use this one: ```c typedef int unrollInt __attribute__((goblint_array_domain("trivial"))); diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a328e8fac8..68096fdfdf 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -595,6 +595,8 @@ struct let drop_interval = CPA.map (function `Int x -> `Int (ID.no_interval x) | x -> x) + let drop_intervalSet = CPA.map (function `Int x -> `Int (ID.no_intervalSet x) | x -> x ) + let context (fd: fundec) (st: store): store = let f keep drop_fn (st: store) = if keep then st else { st with cpa = drop_fn st.cpa} in st |> @@ -604,6 +606,7 @@ struct %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.non-ptr" ~removeAttr:"base.no-non-ptr" ~keepAttr:"base.non-ptr" fd) drop_non_ptrs %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.int" ~removeAttr:"base.no-int" ~keepAttr:"base.int" fd) drop_ints %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.interval" ~removeAttr:"base.no-interval" ~keepAttr:"base.interval" fd) drop_interval + %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.interval_set" ~removeAttr:"base.no-interval_set" ~keepAttr:"base.interval_set" fd) drop_intervalSet let context_cpa fd (st: store) = (context fd st).cpa diff --git a/src/autoTune.ml b/src/autoTune.ml index c412af061a..32f6e33922 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -129,8 +129,8 @@ let disableIntervalContextsInRecursiveFunctions () = ResettableLazy.force functionCallMaps |> fun (x,_,_) -> x |> FunctionCallMap.iter (fun f set -> (*detect direct recursion and recursion with one indirection*) if FunctionSet.mem f set || (not @@ FunctionSet.disjoint (calledFunctions f) (callingFunctions f)) then ( - print_endline ("function " ^ (f.vname) ^" is recursive, disable interval context"); - f.vattr <- addAttributes (f.vattr) [Attr ("goblint_context",[AStr "base.no-interval"; AStr "relation.no-context"])]; + print_endline ("function " ^ (f.vname) ^" is recursive, disable interval and interval_set contexts"); + f.vattr <- addAttributes (f.vattr) [Attr ("goblint_context",[AStr "base.no-interval"; AStr "base.no-interval_set"; AStr "relation.no-context"])]; ) ) @@ -172,7 +172,7 @@ let focusOnSpecification () = match Svcomp.Specification.of_option () with | UnreachCall s -> () | NoDataRace -> (*enable all thread analyses*) - print_endline @@ "Specification: NoDataRace -> enabeling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; + print_endline @@ "Specification: NoDataRace -> enabling thread analyses \"" ^ (String.concat ", " notNeccessaryThreadAnalyses) ^ "\""; let enableAnalysis = GobConfig.set_auto "ana.activated[+]" in List.iter enableAnalysis notNeccessaryThreadAnalyses; | NoOverflow -> (*We focus on integer analysis*) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 2d393dc937..4ce89cca1f 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -26,6 +26,22 @@ let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_ov let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) +type overflow_info = { overflow: bool; underflow: bool;} + +let set_overflow_flag ~cast ~underflow ~overflow ik = + let signed = Cil.isSigned ik in + if !GU.postsolving && signed && not cast then + Goblintutil.svcomp_may_overflow := true; + let sign = if signed then "Signed" else "Unsigned" in + match underflow, overflow with + | true, true -> + M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign + | true, false -> + M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign + | false, true -> + M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign + | false, false -> assert false + let reset_lazy () = ResettableLazy.reset widening_thresholds; ResettableLazy.reset widening_thresholds_desc @@ -115,7 +131,7 @@ sig val cast_to: ?torg:Cil.typ -> Cil.ikind -> t -> t end - +(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) module type IkindUnawareS = sig include B @@ -129,9 +145,8 @@ sig val arbitrary: unit -> t QCheck.arbitrary val invariant: Cil.exp -> t -> Invariant.t end -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. - TODO: Should be ported to S in the future. *) +(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) module type S = sig include B @@ -165,7 +180,35 @@ sig val project: Cil.ikind -> int_precision -> t -> t val arbitrary: Cil.ikind -> t QCheck.arbitrary end -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) + +module type SOverflow = +sig + + include S + + val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info + + val cast_to : ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info + + val of_int : Cil.ikind -> int_t -> t * overflow_info + + val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info + + val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info + val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info + + val shift_left : Cil.ikind -> t -> t -> t * overflow_info + + val shift_right : Cil.ikind -> t -> t -> t * overflow_info +end module type Y = sig @@ -500,11 +543,48 @@ module Std (B: sig include StdTop (B) end -module IntervalFunctor(Ints_t : IntOps.IntOps): S with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = +(* Textbook interval arithmetic, without any overflow handling etc. *) +module IntervalArith(Ints_t : IntOps.IntOps) = struct + let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) + let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) + + let mul (x1, x2) (y1, y2) = + let x1y1 = (Ints_t.mul x1 y1) in + let x1y2 = (Ints_t.mul x1 y2) in + let x2y1 = (Ints_t.mul x2 y1) in + let x2y2 = (Ints_t.mul x2 y2) in + (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) + + let div (x1, x2) (y1, y2) = + let x1y1n = (Ints_t.div x1 y1) in + let x1y2n = (Ints_t.div x1 y2) in + let x2y1n = (Ints_t.div x2 y1) in + let x2y2n = (Ints_t.div x2 y2) in + let x1y1p = (Ints_t.div x1 y1) in + let x1y2p = (Ints_t.div x1 y2) in + let x2y1p = (Ints_t.div x2 y1) in + let x2y2p = (Ints_t.div x2 y2) in + (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) + + let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) + let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) + + let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) + + let one = (Ints_t.one, Ints_t.one) + let zero = (Ints_t.zero, Ints_t.zero) + let top_bool = (Ints_t.zero, Ints_t.one) + + let to_int (x1, x2) = + if Ints_t.equal x1 x2 then Some x1 else None +end + +module IntervalFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = struct let name () = "intervals" type int_t = Ints_t.t type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] + module IArith = IntervalArith(Ints_t) let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) @@ -522,51 +602,40 @@ struct | Some (a, b) -> if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - let set_overflow_flag ~cast ~underflow ~overflow ik = - let signed = Cil.isSigned ik in - if !GU.postsolving && signed && not cast then ( - Goblintutil.svcomp_may_overflow := true); - - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik = function None -> None | Some (x,y) -> - if Ints_t.compare x y > 0 then None + let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> + if Ints_t.compare x y > 0 then + (None,{underflow=false; overflow=false}) else ( let (min_ik, max_ik) = range ik in let underflow = Ints_t.compare min_ik x > 0 in let overflow = Ints_t.compare max_ik y < 0 in - if underflow || overflow then ( - if not suppress_ovwarn then set_overflow_flag ~cast ~underflow ~overflow ik; - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) + let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in + let v = + if underflow || overflow then + if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) + (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) + (* on Z will not safely contain the minimal and maximal elements after the cast *) + let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in + let resdiff = Ints_t.abs (Ints_t.sub y x) in + if Ints_t.compare resdiff diff > 0 then top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) + else + let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in + let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in + if Ints_t.compare l u <= 0 then + Some (l, u) + else + (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) + top_of ik + else if not cast && should_ignore_overflow ik then + let tl, tu = BatOption.get @@ top_of ik in + Some (Ints_t.max tl x, Ints_t.min tu y) + else + top_of ik else - top_of ik - ) - else Some (x,y) + Some (x,y) + in + (v, ov_info) ) let leq (x:t) (y:t) = @@ -578,20 +647,20 @@ struct let join ik (x:t) y = match x, y with | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) + | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst let meet ik (x:t) y = match x, y with | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) + | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst (* TODO: change to_int signature so it returns a big_int *) - let to_int = function Some (x,y) when Ints_t.compare x y = 0 -> Some x | _ -> None + let to_int x = Option.bind x (IArith.to_int) let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some (Ints_t.zero, Ints_t.zero) - let one = Some (Ints_t.one, Ints_t.one) - let top_bool = Some (Ints_t.zero, Ints_t.one) + let zero = Some IArith.zero + let one = Some IArith.one + let top_bool = Some IArith.top_bool let of_bool _ik = function true -> one | false -> zero let to_bool (a: t) = match a with @@ -633,7 +702,7 @@ struct let l2 = if Ints_t.compare l0 l1 = 0 then l0 else Ints_t.min l1 (Ints_t.max lt min_ik) in let ut = if threshold then upper_threshold u1 else max_ik in let u2 = if Ints_t.compare u0 u1 = 0 then u0 else Ints_t.max u1 (Ints_t.min ut max_ik) in - norm ik @@ Some (l2,u2) + norm ik @@ Some (l2,u2) |> fst let widen ik x y = let r = widen ik x y in if M.tracing then M.tracel "int" "interval widen %a %a -> %a\n" pretty x pretty y pretty r; @@ -647,7 +716,7 @@ struct let (min_ik, max_ik) = range ik in let lr = if Ints_t.compare min_ik x1 = 0 then y1 else x1 in let ur = if Ints_t.compare max_ik x2 = 0 then y2 else x2 in - norm ik @@ Some (lr,ur) + norm ik @@ Some (lr,ur) |> fst let narrow ik x y = if get_bool "ana.int.interval_narrow_by_meet" then @@ -687,18 +756,18 @@ struct | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) | _ -> match to_int i1, to_int i2 with - | Some x, Some y -> (try norm ik (of_int ik (f ik x y)) with Division_by_zero -> top_of ik) + | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) | _ -> top_of ik let bitcomp f ik i1 i2 = match is_bot i1, is_bot i2 with - | true, true -> bot_of ik + | true, true -> (bot_of ik,{underflow=false; overflow=false}) | true, _ | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) | _ -> match to_int i1, to_int i2 with - | Some x, Some y -> (try norm ik (of_int ik (f ik x y)) with Division_by_zero | Invalid_argument _ -> top_of ik) - | _ -> (set_overflow_flag ~cast:false ~underflow:true ~overflow:true ik; top_of ik) + | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) + | _ -> (top_of ik,{underflow=true; overflow=true}) let bitxor = bit (fun _ik -> Ints_t.bitxor) let bitand = bit (fun _ik -> Ints_t.bitand) @@ -709,24 +778,23 @@ struct bot_of ik else match to_int i1 with - | Some x -> of_int ik (f ik x) + | Some x -> of_int ik (f ik x) |> fst | _ -> top_of ik let bitnot = bit1 (fun _ik -> Ints_t.bitnot) let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) let shift_left = bitcomp (fun _ik x y -> Ints_t.shift_left x (Ints_t.to_int y)) - let neg ?no_ov ik = function None -> None | Some (x,y) -> norm ik @@ Some (Ints_t.neg y, Ints_t.neg x) + let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - let add ?no_ov ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.add x1 y1, Ints_t.add x2 y2) + let binary_op_with_norm ?no_ov op ik x y = match x, y with + | None, None -> (None, {overflow=false; underflow= false}) + | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + | Some x, Some y -> norm ik @@ Some (op x y) - let sub ?no_ov ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.sub x1 y2, Ints_t.sub x2 y1) (* y1, y2 are in different order here than in add *) + let add ?no_ov = binary_op_with_norm IArith.add + let mul ?no_ov = binary_op_with_norm IArith.mul + let sub ?no_ov = binary_op_with_norm IArith.sub let rem ik x y = match x, y with | None, None -> None @@ -742,43 +810,27 @@ struct top_of ik else (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) + * Otherwise we meet with a [range] the result can be in. + * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. + * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - let mul ?no_ov ik x y = - match x, y with - | None, None -> bot () - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - let x1y1 = (Ints_t.mul x1 y1) in let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in let x2y2 = (Ints_t.mul x2 y2) in - norm ik @@ Some ((Ints_t.min (Ints_t.min x1y1 x1y2) (Ints_t.min x2y1 x2y2)), - (Ints_t.max (Ints_t.max x1y1 x1y2) (Ints_t.max x2y1 x2y2))) - let rec div ?no_ov ik x y = match x, y with - | None, None -> bot () + | None, None -> (bot (),{underflow=false; overflow=false}) | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> + | (Some (x1,x2) as x), (Some (y1,y2) as y) -> begin let is_zero v = Ints_t.compare v Ints_t.zero = 0 in match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) + | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero)) (Some (y1,y2)) -> top_of ik - | _ -> - let x1y1n = (Ints_t.div x1 y1) in let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in let x2y2p = (Ints_t.div x2 y2) in - norm ik @@ Some ((Ints_t.min (Ints_t.min x1y1n x1y2n) (Ints_t.min x2y1n x2y2n)), - (Ints_t.max (Ints_t.max x1y1p x1y2p) (Ints_t.max x2y1p x2y2p))) + | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) + | _ -> binary_op_with_norm IArith.div ik x y end let ne ik x y = @@ -863,10 +915,10 @@ struct let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in let pair_arb = QCheck.pair int_arb int_arb in let shrink = function - | Some (l, u) -> (return None) <+> (MyCheck.shrink pair_arb (l, u) >|= of_interval ik) + | Some (l, u) -> (return None) <+> (MyCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) | None -> empty in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (of_interval ik) pair_arb) + QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) let relift x = x let modulo n k = @@ -889,8 +941,8 @@ struct if Ints_t.equal y max_ik then y else Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) - else norm ik @@ Some (rcx, lcy) + else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst + else norm ik @@ Some (rcx, lcy) |> fst | _ -> None let refine_with_congruence ik x y = @@ -911,30 +963,608 @@ struct let (min_ik, max_ik) = range ik in let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) in + let intv' = norm ik @@ Some (l', u') |> fst in + let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in meet ik intv' range let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = match intv, incl with | None, _ | _, None -> intv | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv + let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with + | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in + let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with + | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in + match min None ls, max None ls with + | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) + | _, _-> intv let project ik p t = t end +(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) +module IntervalSetFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = +struct + + module Interval = IntervalFunctor(Ints_t) + module IArith = IntervalArith(Ints_t) + + + let name () = "interval_sets" + + type int_t = Ints_t.t + + let (>.) a b = Ints_t.compare a b > 0 + let (=.) a b = Ints_t.compare a b = 0 + let (<.) a b = Ints_t.compare a b < 0 + let (>=.) a b = Ints_t.compare a b >= 0 + let (<=.) a b = Ints_t.compare a b <= 0 + let (+.) a b = Ints_t.add a b + let (-.) a b = Ints_t.sub a b + let ( *.) a b = Ints_t.mul a b + let (/.) a b = Ints_t.div a b + + let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) + let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) + + (* + Each domain's element is guaranteed to be in canonical form. That is, each interval contained + inside the set does not overlap with each other and they are not adjacent. + *) + type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] + + let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) + + let top () = failwith @@ "top () not implemented for " ^ (name ()) + + let top_of ik = [range ik] + + let bot () = [] + + let bot_of ik = bot () + + let show (x: t) = + let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in + List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" + + (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) + type event = Enter of Ints_t.t | Exit of Ints_t.t + + let unbox_event = function Enter x -> x | Exit x -> x + + let cmp_events x y = + (* Deliberately comparing ints first => Cannot be derived *) + let res = Ints_t.compare (unbox_event x) (unbox_event y) in + if res <> 0 then res + else + begin + match (x, y) with + | (Enter _, Exit _) -> -1 + | (Exit _, Enter _) -> 1 + | (_, _) -> 0 + end + + let interval_set_to_events (xs: t) = + List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs + + let two_interval_sets_to_events (xs: t) (ys: t) = + let xs = interval_set_to_events xs in + let ys = interval_set_to_events ys in + List.merge cmp_events xs ys + + (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap + This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) + let combined_event_list lattice_op (xs:event list) = + let l = match lattice_op with `Join -> 1 | `Meet -> 2 in + let aux (interval_count, acc) = function + | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) + | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) + in + List.fold_left aux (0, []) xs |> snd |> List.rev + + let rec events_to_intervals = function + | [] -> [] + | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) + | _ -> failwith "Invalid events list" + + let remove_empty_gaps (xs: t) = + let aux acc (l, r) = match acc with + | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' + | _ -> (l, r)::acc + in + List.fold_left aux [] xs |> List.rev + + let canonize (xs: t) = + interval_set_to_events xs |> + List.sort cmp_events |> + combined_event_list `Join |> + events_to_intervals |> + remove_empty_gaps + + let unop (x: t) op = match x with + | [] -> [] + | _ -> canonize @@ List.concat_map op x + + let binop (x: t) (y: t) op : t = match x, y with + | [], _ -> [] + | _, [] -> [] + | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) + + + include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) + + let minimal = function + | [] -> None + | (x, _)::_ -> Some x + + let maximal = function + | [] -> None + | xs -> Some (BatList.last xs |> snd) + + let equal_to_interval i (a, b) = + if a =. b && b =. i then + `Eq + else if a <=. i && i <=. b then + `Top + else + `Neq + + let equal_to i xs = match List.map (equal_to_interval i) xs with + | [] -> failwith "unsupported: equal_to with bottom" + | [`Eq] -> `Eq + | ys when List.for_all ((=) `Neq) ys -> `Neq + | _ -> `Top + + let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = + if x >. y then + ([],{underflow=false; overflow=false}) + else + let (min_ik, max_ik) = range ik in + let underflow = min_ik >. x in + let overflow = max_ik <. y in + let v = if underflow || overflow then + begin + if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) + (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) + (* on Z will not safely contain the minimal and maximal elements after the cast *) + let diff = Ints_t.abs (max_ik -. min_ik) in + let resdiff = Ints_t.abs (y -. x) in + if resdiff >. diff then + [range ik] + else + let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in + let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in + if l <=. u then + [(l, u)] + else + (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) + [(min_ik, u); (l, max_ik)] + else if not cast && should_ignore_overflow ik then + [Ints_t.max min_ik x, Ints_t.min max_ik y] + else + [range ik] + end + else + [(x,y)] + in + if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) + + let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = + let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in + let intvs = List.concat_map fst res in + let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in + let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in + (canonize intvs,{underflow; overflow}) + + let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with + | [], _ -> ([],{overflow=false; underflow=false}) + | _, [] -> ([],{overflow=false; underflow=false}) + | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) + + let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with + | [], _ -> ([],{overflow=false; underflow=false}) + | _, [] -> ([],{overflow=false; underflow=false}) + | _, _ -> + let res = List.map op (BatList.cartesian_product x y) in + let intvs = List.concat_map fst res in + let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in + let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in + (canonize intvs,{underflow; overflow}) + + let unary_op_with_norm op (ik:ikind) (x: t) = match x with + | [] -> ([],{overflow=false; underflow=false}) + | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x + + let rec leq (xs: t) (ys: t) = + let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in + match xs, ys with + | [], _ -> true + | _, [] -> false + | (xl,xr)::xs', (yl,yr)::ys' -> + if leq_interval (xl,xr) (yl,yr) then + leq xs' ys + else if xr <. yl then + false + else + leq xs ys' + + let join ik (x: t) (y: t): t = + two_interval_sets_to_events x y |> + combined_event_list `Join |> + events_to_intervals |> + remove_empty_gaps + + let meet ik (x: t) (y: t): t = + two_interval_sets_to_events x y |> + combined_event_list `Meet |> + events_to_intervals + + let to_int = function + | [x] -> IArith.to_int x + | _ -> None + + let zero = [IArith.zero] + let one = [IArith.one] + let top_bool = [IArith.top_bool] + + let not_bool (x:t) = + let is_false x = equal x zero in + let is_true x = equal x one in + if is_true x then zero else if is_false x then one else top_bool + + let to_bool = function + | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false + | x -> if leq zero x then None else Some true + + let of_bool _ = function true -> one | false -> zero + + let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) + + let of_int ik (x: int_t) = of_interval ik (x, x) + + let lt ik x y = + match x, y with + | [], [] -> bot_of ik + | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + | _, _ -> + let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in + let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in + if max_x <. min_y then + of_bool ik true + else if min_x >=. max_y then + of_bool ik false + else + top_bool + + let le ik x y = + match x, y with + | [], [] -> bot_of ik + | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + | _, _ -> + let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in + let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in + if max_x <=. min_y then + of_bool ik true + else if min_x >. max_y then + of_bool ik false + else + top_bool + + let gt ik x y = not_bool @@ le ik x y + + let ge ik x y = not_bool @@ lt ik x y + + let eq ik x y = match x, y with + | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> + one + | _ -> + if is_bot (meet ik x y) then + zero + else + top_bool + + let ne ik x y = not_bool @@ eq ik x y + let interval_to_int i = Interval.to_int (Some i) + let interval_to_bool i = Interval.to_bool (Some i) + + let log f ik (i1, i2) = + match (interval_to_bool i1, interval_to_bool i2) with + | Some x, Some y -> of_bool ik (f x y) + | _ -> top_of ik + + + let bit f ik (i1, i2) = + match (interval_to_int i1), (interval_to_int i2) with + | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) + | _ -> top_of ik + + + let bitcomp f ik (i1, i2) = + match (interval_to_int i1, interval_to_int i2) with + | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) + | _, _ -> (top_of ik,{overflow=false; underflow=false}) + + let bitand ik x y = + let interval_bitand = bit Ints_t.bitand ik in + binop x y interval_bitand + + let bitor ik x y = + let interval_bitor = bit Ints_t.bitor ik in + binop x y interval_bitor + + let bitxor ik x y = + let interval_bitxor = bit Ints_t.bitxor ik in + binop x y interval_bitxor + + let bitnot ik x = + let interval_bitnot i = + match interval_to_int i with + | Some x -> of_int ik (Ints_t.bitnot x) |> fst + | _ -> top_of ik + in + unop x interval_bitnot + + let shift_left ik x y = + let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in + binary_op_with_ovc x y interval_shiftleft + + let shift_right ik x y = + let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in + binary_op_with_ovc x y interval_shiftright + + let lognot ik x = + let log1 f ik i1 = + match interval_to_bool i1 with + | Some x -> of_bool ik (f x) + | _ -> top_of ik + in + let interval_lognot = log1 not ik in + unop x interval_lognot + + let logand ik x y = + let interval_logand = log (&&) ik in + binop x y interval_logand + + let logor ik x y = + let interval_logor = log (||) ik in + binop x y interval_logor + + let add ?no_ov = binary_op_with_norm IArith.add + let sub ?no_ov = binary_op_with_norm IArith.sub + let mul ?no_ov = binary_op_with_norm IArith.mul + let neg ?no_ov = unary_op_with_norm IArith.neg + + let div ?no_ov ik x y = + let rec interval_div x (y1, y2) = begin + let top_of ik = top_of ik |> List.hd in + let is_zero v = v =. Ints_t.zero in + match y1, y2 with + | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) + | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) + | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) + | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik + | _ -> IArith.div x (y1, y2) + end + in binary_op_with_norm interval_div ik x y + + let rem ik x y = + let interval_rem (x, y) = + if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then + top_of ik + else + let (xl, xu) = x in let (yl, yu) = y in + let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in + let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in + let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in + meet ik (bit Ints_t.rem ik (x, y)) [range] + in + binop x y interval_rem + + let cast_to ?torg ?no_ov ik x = norm_intvs ~cast:true ik x + + (* + narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys + *) + let narrow ik xs ys = match xs ,ys with + | [], _ -> [] | _ ,[] -> xs + | _, _ -> + let min_xs = minimal xs |> Option.get in + let max_xs = maximal xs |> Option.get in + let min_ys = minimal ys |> Option.get in + let max_ys = maximal ys |> Option.get in + let min_range,max_range = range ik in + let min = if min_xs =. min_range then min_ys else min_xs in + let max = if max_xs =. max_range then max_ys else max_xs in + xs + |> (function (_, y)::z -> (min, y)::z | _ -> []) + |> List.rev + |> (function (x, _)::z -> (x, max)::z | _ -> []) + |> List.rev + + (* + 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. + and joins all intervals in xs assigned to the same interval in ys as one interval. + 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. + 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) + + The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. + *) + let widen ik xs ys = + let (min_ik,max_ik) = range ik in + let threshold = get_bool "ana.int.interval_threshold_widening" in + let upper_threshold (_,u) = + let ts = if GobConfig.get_string "ana.int.interval_threshold_widening_constants" = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in + let u = Ints_t.to_bigint u in + let t = List.find_opt (fun x -> Z.compare u x <= 0) ts in + BatOption.map_default Ints_t.of_bigint max_ik t + in + let lower_threshold (l,_) = + let ts = if GobConfig.get_string "ana.int.interval_threshold_widening_constants" = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in + let l = Ints_t.to_bigint l in + let t = List.find_opt (fun x -> Z.compare l x >= 0) ts in + BatOption.map_default Ints_t.of_bigint min_ik t + in + (*obtain partitioning of xs intervals according to the ys interval that includes them*) + let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= + match xs,ys with + | _, [] -> [] + | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys + | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) + | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys + in + let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in + (*merge a pair of adjacent partitions*) + let merge_pair ik (a,b) (c,d) = + let new_a = function + | None -> Some (upper_threshold b, upper_threshold b) + | Some (ax,ay) -> Some (ax, upper_threshold b) + in + let new_c = function + | None -> Some (lower_threshold d, lower_threshold d) + | Some (cx,cy) -> Some (lower_threshold d, cy) + in + if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then + [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] + else + [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] + in + let partitions_are_approaching part_left part_right = match part_left, part_right with + | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) + | _,_ -> false + in + (*merge all approaching pairs of adjacent partitions*) + let rec merge_list ik = function + | [] -> [] + | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) + | x::xs -> x :: merge_list ik xs + in + (*expands left extremity*) + let widen_left = function + | [] -> [] + | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts + | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts + | x -> x + in + (*expands right extremity*) + let widen_right x = + let map_rightmost = function + | [] -> [] + | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts + | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts + | x -> x + in + List.rev x |> map_rightmost |> List.rev + in + interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd + + let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) + + let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) + + let invariant_ikind e ik xs = + List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> + let open Invariant in List.fold_left (||) (bot ()) + + let modulo n k = + let result = Ints_t.rem n k in + if result >=. Ints_t.zero then result + else result +. k + + let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = + let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = + match intv, cong with + | Some (x, y), Some (c, m) -> + if m =. Ints_t.zero && (c <. x || c >. y) then [] + else if m =. Ints_t.zero then + [(c, c)] + else + let (min_ik, max_ik) = range ik in + let rcx = + if x =. min_ik then x else + x +. (modulo (c -. x) (Ints_t.abs m)) in + let lcy = + if y =. max_ik then y else + y -. (modulo (y -. c) (Ints_t.abs m)) in + if rcx >. lcy then [] + else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst + else norm_interval ik (rcx, lcy) |> fst + | _ -> [] + in + List.map (fun x -> Some x) intvs |> List.map (refine_with_congruence_interval ik cong) |> List.flatten + + let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] + + let refine_with_incl_list ik intvs = function + | None -> intvs + | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) + + let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = + let intv1 = (min, excl -. Ints_t.one) in + let intv2 = (excl +. Ints_t.one, max) in + norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst + + let of_excl_list ik (excls: int_t list) = + let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in + let res = List.fold_left (meet ik) (top_of ik) excl_list in + res + + let refine_with_excl_list ik (intv : t) = function + | None -> intv + | Some (xs, range) -> + let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = + excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl + in + let excl_list = List.map (excl_to_intervalset ik range) xs in + List.fold_left (meet ik) intv excl_list + + let project ik p t = t + + let arbitrary ik = + let open QCheck.Iter in + (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint MyCheck.Arbitrary.big_int in *) + (* TODO: apparently bigints are really slow compared to int64 for domaintest *) + let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 MyCheck.Arbitrary.int64 in + let pair_arb = QCheck.pair int_arb int_arb in + let list_pair_arb = QCheck.small_list pair_arb in + let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in + let shrink xs = MyCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list + in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) + +end + +module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct + include D + + let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y + + let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y + + let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y + + let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y + + let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x + + let cast_to ?torg ?no_ov ik x = fst @@ D.cast_to ?torg ?no_ov ik x + + let of_int ik x = fst @@ D.of_int ik x + + let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x + + let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x + + let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x + + let shift_left ik x y = fst @@ D.shift_left ik x y + + let shift_right ik x y = fst @@ D.shift_right ik x y + +end module IntIkind = struct let ikind () = Cil.IInt end module Interval = IntervalFunctor (BI) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (IntervalFunctor (IntOps.Int64Ops))) (IntIkind) - +module Interval32 = IntDomWithDefaultIkind (IntDomLifter ( SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)) ) ) (IntIkind) +module IntervalSet = IntervalSetFunctor(BI) module Integers(Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) struct include Printable.Std @@ -1293,9 +1923,9 @@ struct let is_top x = x = top () let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Definite x -> if i = x then `Eq else `Neq - | `Excluded (s,r) -> if S.mem i s then `Neq else `Top + | `Bot -> failwith "unsupported: equal_to with bottom" + | `Definite x -> if i = x then `Eq else `Neq + | `Excluded (s,r) -> if S.mem i s then `Neq else `Top let top_of ik = `Excluded (S.empty (), size ik) let cast_to ?torg ?no_ov ik = function @@ -1315,61 +1945,61 @@ struct | `Definite x -> `Definite (BigInt.cast_to ik x) | `Bot -> `Bot - (* Wraps definite values and excluded values according to the ikind. - * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. - * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. - *) - let norm ik v = - match v with - | `Excluded (s, r) -> - let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in - (* If no overflow occurred, just return x *) - if not possibly_overflowed then ( - v - ) - (* Else, if an overflow might have occurred but we should just ignore it *) - else if should_ignore_overflow ik then ( - let r = size ik in - (* filter out excluded elements that are not in the range *) - let mapped_excl = S.filter (in_range r) s in - `Excluded (mapped_excl, r) - ) - (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) - else if not (should_wrap ik) then ( - top_of ik - ) else ( - (* Else an overflow occurred that we should treat with wrap-around *) - let r = size ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - let mapped_excl = S.map (fun excl -> BigInt.cast_to ik excl) s in - match ik with - | IBool -> - begin match S.mem BigInt.zero mapped_excl, S.mem BigInt.one mapped_excl with - | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) - | true, false -> `Definite BigInt.one (* Not {0} -> 1 *) - | false, true -> `Definite BigInt.zero (* Not {1} -> 0 *) - | true, true -> `Bot (* Not {0, 1} -> bot *) - end - | ik -> - `Excluded (mapped_excl, r) - ) - | `Definite x -> - let min, max = Size.range ik in + (* Wraps definite values and excluded values according to the ikind. + * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. + * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. + *) + let norm ik v = + match v with + | `Excluded (s, r) -> + let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in + (* If no overflow occurred, just return x *) + if not possibly_overflowed then ( + v + ) + (* Else, if an overflow might have occurred but we should just ignore it *) + else if should_ignore_overflow ik then ( + let r = size ik in + (* filter out excluded elements that are not in the range *) + let mapped_excl = S.filter (in_range r) s in + `Excluded (mapped_excl, r) + ) + (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) + else if not (should_wrap ik) then ( + top_of ik + ) else ( + (* Else an overflow occurred that we should treat with wrap-around *) + let r = size ik in (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - if should_wrap ik then ( - cast_to ik v - ) - else if BigInt.compare min x <= 0 && BigInt.compare x max <= 0 then ( - v - ) - else if should_ignore_overflow ik then ( - M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; - `Bot - ) - else ( - top_of ik - ) - | `Bot -> `Bot + let mapped_excl = S.map (fun excl -> BigInt.cast_to ik excl) s in + match ik with + | IBool -> + begin match S.mem BigInt.zero mapped_excl, S.mem BigInt.one mapped_excl with + | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) + | true, false -> `Definite BigInt.one (* Not {0} -> 1 *) + | false, true -> `Definite BigInt.zero (* Not {1} -> 0 *) + | true, true -> `Bot (* Not {0, 1} -> bot *) + end + | ik -> + `Excluded (mapped_excl, r) + ) + | `Definite x -> + let min, max = Size.range ik in + (* Perform a wrap-around for unsigned values and for signed values (if configured). *) + if should_wrap ik then ( + cast_to ik v + ) + else if BigInt.compare min x <= 0 && BigInt.compare x max <= 0 then ( + v + ) + else if should_ignore_overflow ik then ( + M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; + `Bot + ) + else ( + top_of ik + ) + | `Bot -> `Bot let leq x y = match (x,y) with (* `Bot <= x is always true *) @@ -1497,36 +2127,36 @@ struct | `Bot -> `Bot let lift2 f ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite _ - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (f x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) + (* We don't bother with exclusion sets: *) + | `Excluded _, `Definite _ + | `Definite _, `Excluded _ + | `Excluded _, `Excluded _ -> top () + (* The good case: *) + | `Definite x, `Definite y -> + (try `Definite (f x y) with | Division_by_zero -> top ()) + | `Bot, `Bot -> `Bot + | _ -> + (* If only one of them is bottom, we raise an exception that eval_rv will catch *) + raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) (* Default behaviour for binary operators that are injective in either * argument, so that Exclusion Sets can be used: *) let lift2_inj f ik x y = let def_exc f x s r = `Excluded (S.map (f x) s, apply_range (f x) r) in norm ik @@ - match x,y with - (* If both are exclusion sets, there isn't anything we can do: *) - | `Excluded _, `Excluded _ -> top () - (* A definite value should be applied to all members of the exclusion set *) - | `Definite x, `Excluded (s,r) -> def_exc f x s r - (* Same thing here, but we should flip the operator to map it properly *) - | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r - (* The good case: *) - | `Definite x, `Definite y -> `Definite (f x y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + match x,y with + (* If both are exclusion sets, there isn't anything we can do: *) + | `Excluded _, `Excluded _ -> top () + (* A definite value should be applied to all members of the exclusion set *) + | `Definite x, `Excluded (s,r) -> def_exc f x s r + (* Same thing here, but we should flip the operator to map it properly *) + | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r + (* The good case: *) + | `Definite x, `Definite y -> `Definite (f x y) + | `Bot, `Bot -> `Bot + | _ -> + (* If only one of them is bottom, we raise an exception that eval_rv will catch *) + raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) (* The equality check: *) let eq ik x y = match x,y with @@ -1847,8 +2477,8 @@ module Enums : S with type int_t = BigInt.t = struct | Inc xs -> let casted_xs = BISet.map (BigInt.cast_to ik) xs in if Cil.isSigned ik && not (BISet.equal xs casted_xs) - then top_of ik (* When casting into a signed type and the result does not fit, the behavior is implementation-defined *) - else Inc casted_xs + then top_of ik (* When casting into a signed type and the result does not fit, the behavior is implementation-defined *) + else Inc casted_xs let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) @@ -1914,9 +2544,9 @@ module Enums : S with type int_t = BigInt.t = struct let lift2 f (ikind: Cil.ikind) u v = handle_bot u v (fun () -> - norm ikind @@ match u, v with - | Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (f (BISet.choose x) (BISet.choose y))) - | _,_ -> top_of ikind) + norm ikind @@ match u, v with + | Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (f (BISet.choose x) (BISet.choose y))) + | _,_ -> top_of ikind) let lift2 f ikind a b = try lift2 f ikind a b with Division_by_zero -> top_of ikind @@ -1950,18 +2580,18 @@ module Enums : S with type int_t = BigInt.t = struct let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = handle_bot x y (fun () -> - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = BI.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let x_min = minimal x in - let y_min = minimal y in - if x_min = None || y_min = None || BI.compare (Option.get x_min) BI.zero < 0 || BI.compare (Option.get y_min) BI.zero < 0 then - top_of ik - else - lift2 shift_op_big_int ik x y) + (* BigInt only accepts int as second argument for shifts; perform conversion here *) + let shift_op_big_int a (b: int_t) = + let (b : int) = BI.to_int b in + shift_op a b + in + (* If one of the parameters of the shift is negative, the result is undefined *) + let x_min = minimal x in + let y_min = minimal y in + if x_min = None || y_min = None || BI.compare (Option.get x_min) BI.zero < 0 || BI.compare (Option.get y_min) BI.zero < 0 then + top_of ik + else + lift2 shift_op_big_int ik x y) let shift_left = shift BigInt.shift_left @@ -1995,8 +2625,8 @@ module Enums : S with type int_t = BigInt.t = struct then x else match to_bool x with - | Some b -> of_bool ik (not b) - | None -> top_bool + | Some b -> of_bool ik (not b) + | None -> top_bool let logand = lift2 I.logand let logor = lift2 I.logor @@ -2026,32 +2656,32 @@ module Enums : S with type int_t = BigInt.t = struct let lt ik x y = handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when I.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when I.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) + match minimal x, maximal x, minimal y, maximal y with + | _, Some x2, Some y1, _ when I.compare x2 y1 < 0 -> of_bool ik true + | Some x1, _, _, Some y2 when I.compare x1 y2 >= 0 -> of_bool ik false + | _, _, _, _ -> top_bool) let gt ik x y = lt ik y x let le ik x y = handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when I.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when I.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) + match minimal x, maximal x, minimal y, maximal y with + | _, Some x2, Some y1, _ when I.compare x2 y1 <= 0 -> of_bool ik true + | Some x1, _, _, Some y2 when I.compare x1 y2 > 0 -> of_bool ik false + | _, _, _, _ -> top_bool) let ge ik x y = le ik y x let eq ik x y = handle_bot x y (fun () -> - match x, y with + match x, y with | Inc xs, Inc ys when BISet.is_singleton xs && BISet.is_singleton ys -> of_bool ik (I.equal (BISet.choose xs) (BISet.choose ys)) - | _, _ -> - if is_bot (meet ik x y) then - (* If the meet is empty, there is no chance that concrete values are equal *) - of_bool ik false - else - top_bool) + | _, _ -> + if is_bot (meet ik x y) then + (* If the meet is empty, there is no chance that concrete values are equal *) + of_bool ik false + else + top_bool) let ne ik x y = lognot ik (eq ik x y) @@ -2170,7 +2800,7 @@ struct let equal_to i = function | None -> failwith "unsupported: equal_to with bottom" | Some (a, b) when b =: Ints_t.zero -> if a =: i then `Eq else `Neq - | Some (a, b) -> if i %: b =: a then `Top else `Neq + | Some (a, b) -> if i %: b =: a then `Top else `Neq let leq (x:t) (y:t) = match x, y with @@ -2179,7 +2809,7 @@ struct | Some (c1,m1), Some (c2,m2) when m2 =: Ints_t.zero && m1 =: Ints_t.zero -> c1 =: c2 | Some (c1,m1), Some (c2,m2) when m2 =: Ints_t.zero -> c1 =: c2 && m1 =: Ints_t.zero | Some (c1,m1), Some (c2,m2) -> m2 |: (Ints_t.gcd (c1 -: c2) m1) - (* Typo in original equation of P. Granger (m2 instead of m1): gcd (c1 -: c2) m2 + (* Typo in original equation of P. Granger (m2 instead of m1): gcd (c1 -: c2) m2 Reference: https://doi.org/10.1080/00207168908803778 Page 171 corollary 3.3*) let leq x y = @@ -2320,8 +2950,8 @@ struct let shift_right ik x y = let res = shift_right ik x y in - if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a \n" pretty x pretty y pretty res; - res + if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a \n" pretty x pretty y pretty res; + res let shift_left ik x y = (* Naive primality test *) @@ -2578,8 +3208,8 @@ struct let refine_with_interval ik (cong : t) (intv : (int_t * int_t) option) : t = let pretty_intv _ i = (match i with - | Some(l, u) -> let s = "["^Ints_t.to_string l^","^Ints_t.to_string u^"]" in Pretty.text s - | _ -> Pretty.text ("Display Error")) in + | Some(l, u) -> let s = "["^Ints_t.to_string l^","^Ints_t.to_string u^"]" in Pretty.text s + | _ -> Pretty.text ("Display Error")) in let refn = refine_with_interval ik cong intv in if M.tracing then M.trace "refine" "cong_refine_with_interval %a %a -> %a\n" pretty cong pretty_intv intv pretty refn; refn @@ -2591,6 +3221,40 @@ struct let project ik p t = t end +module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct + + include D + + let lift v = (v, {overflow=false; underflow=false}) + + let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y + + let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y + + let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y + + let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y + + let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x + + let cast_to ?torg ?no_ov ik x = lift @@ D.cast_to ?torg ?no_ov ik x + + let of_int ik x = lift @@ D.of_int ik x + + let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x + + let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x + + let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x + + let shift_left ik x y = lift @@ D.shift_left ik x y + + let shift_right ik x y = lift @@ D.shift_right ik x y + +end + + + (* The old IntDomList had too much boilerplate since we had to edit every function in S when adding a new domain. With the following, we only have to edit the places where fn are applied, i.e., create, mapp, map, map2. You can search for I3 below to see where you need to extend. *) (* discussion: https://github.com/goblint/analyzer/pull/188#issuecomment-818928540 *) module IntDomTupleImpl = struct @@ -2598,150 +3262,192 @@ module IntDomTupleImpl = struct open Batteries type int_t = BI.t - module I1 = DefExc + module I1 = SOverflowLifter(DefExc) module I2 = Interval - module I3 = Enums - module I4 = Congruence + module I3 = SOverflowLifter(Enums) + module I4 = SOverflowLifter(Congruence) + module I5 = IntervalSetFunctor (BI) - type t = I1.t option * I2.t option * I3.t option * I4.t option + type t = I1.t option * I2.t option * I3.t option * I4.t option * I5.t option [@@deriving to_yojson, eq, ord] let name () = "intdomtuple" (* The Interval domain can lead to too many contexts for recursive functions (top is [min,max]), but we don't want to drop all ints as with `ana.base.context.int`. TODO better solution? *) - let no_interval = Tuple4.map2 (const None) + let no_interval = Tuple5.map2 (const None) + let no_intervalSet = Tuple5.map5 (const None) - type 'a m = (module S with type t = 'a) - type 'a m2 = (module S with type t = 'a and type int_t = int_t ) + type 'a m = (module SOverflow with type t = 'a) + type 'a m2 = (module SOverflow with type t = 'a and type int_t = int_t ) (* only first-order polymorphism on functions -> use records to get around monomorphism restriction on arguments *) type 'b poly_in = { fi : 'a. 'a m -> 'b -> 'a } (* inject *) type 'b poly2_in = { fi2 : 'a. 'a m2 -> 'b -> 'a } (* inject for functions that depend on int_t *) + type 'b poly2_in_ovc = { fi2_ovc : 'a. 'a m2 -> 'b -> 'a * overflow_info} (* inject for functions that depend on int_t *) + type 'b poly_pr = { fp : 'a. 'a m -> 'a -> 'b } (* project *) type 'b poly_pr2 = { fp2 : 'a. 'a m2 -> 'a -> 'b } (* project for functions that depend on int_t *) type 'b poly2_pr = {f2p: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'b} type poly1 = {f1: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a} (* needed b/c above 'b must be different from 'a *) + type poly1_ovc = {f1_ovc: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a * overflow_info } (* needed b/c above 'b must be different from 'a *) type poly2 = {f2: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'a} + type poly2_ovc = {f2_ovc: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'a * overflow_info } type 'b poly3 = { f3: 'a. 'a m -> 'a option } (* used for projection to given precision *) - let create r x ((p1, p2, p3, p4): int_precision) = + let create r x ((p1, p2, p3, p4, p5): int_precision) = let f b g = if b then Some (g x) else None in - f p1 @@ r.fi (module I1), f p2 @@ r.fi (module I2), f p3 @@ r.fi (module I3), f p4 @@ r.fi (module I4) + f p1 @@ r.fi (module I1), f p2 @@ r.fi (module I2), f p3 @@ r.fi (module I3), f p4 @@ r.fi (module I4), f p5 @@ r.fi (module I5) let create r x = (* use where values are introduced *) create r x (int_precision_from_node_or_config ()) - let create2 r x ((p1, p2, p3, p4): int_precision) = + let create2 r x ((p1, p2, p3, p4, p5): int_precision) = let f b g = if b then Some (g x) else None in - f p1 @@ r.fi2 (module I1), f p2 @@ r.fi2 (module I2), f p3 @@ r.fi2 (module I3), f p4 @@ r.fi2 (module I4) + f p1 @@ r.fi2 (module I1), f p2 @@ r.fi2 (module I2), f p3 @@ r.fi2 (module I3), f p4 @@ r.fi2 (module I4), f p5 @@ r.fi2 (module I5) let create2 r x = (* use where values are introduced *) create2 r x (int_precision_from_node_or_config ()) + let no_overflow ik = function + | Some(_, {underflow; overflow}) -> not (underflow || overflow) + | _ -> false + + let check_ov ik intv intv_set = + let no_ov = (no_overflow ik intv) || (no_overflow ik intv_set) in + if not no_ov && ( BatOption.is_some intv || BatOption.is_some intv_set) then ( + let (_,{underflow=underflow_intv; overflow=overflow_intv}) = match intv with None -> (I2.bot (), {underflow= true; overflow = true}) | Some x -> x in + let (_,{underflow=underflow_intv_set; overflow=overflow_intv_set}) = match intv_set with None -> (I5.bot (), {underflow= true; overflow = true}) | Some x -> x in + let underflow = underflow_intv && underflow_intv_set in + let overflow = overflow_intv && overflow_intv_set in + set_overflow_flag ~cast:false ~underflow ~overflow ik; + ); + no_ov + + let create2_ovc ik r x ((p1, p2, p3, p4, p5): int_precision) = + let f b g = if b then Some (g x) else None in + let map x = Option.map fst x in + let intv = f p2 @@ r.fi2_ovc (module I2) in + let intv_set = f p5 @@ r.fi2_ovc (module I5) in + ignore (check_ov ik intv intv_set); + map @@ f p1 @@ r.fi2_ovc (module I1), map @@ f p2 @@ r.fi2_ovc (module I2), map @@ f p3 @@ r.fi2_ovc (module I3), map @@ f p4 @@ r.fi2_ovc (module I4), map @@ f p5 @@ r.fi2_ovc (module I5) + + let create2_ovc ik r x = (* use where values are introduced *) + create2_ovc ik r x (int_precision_from_node_or_config ()) + + let opt_map2 f ?no_ov = curry @@ function Some x, Some y -> Some (f ?no_ov x y) | _ -> None - let to_list x = Tuple4.enum x |> List.of_enum |> List.filter_map identity (* contains only the values of activated domains *) + let to_list x = Tuple5.enum x |> List.of_enum |> List.filter_map identity (* contains only the values of activated domains *) let to_list_some x = List.filter_map identity @@ to_list x (* contains only the Some-values of activated domains *) let exists = function - | (Some true, _, _, _) - | (_, Some true, _, _) - | (_, _, Some true, _) - | (_, _, _, Some true) -> + | (Some true, _, _, _, _) + | (_, Some true, _, _, _) + | (_, _, Some true, _, _) + | (_, _, _, Some true, _) + | (_, _, _, _, Some true) -> true | _ -> false let for_all = function - | (Some false, _, _, _) - | (_, Some false, _, _) - | (_, _, Some false, _) - | (_, _, _, Some false) -> + | (Some false, _, _, _, _) + | (_, Some false, _, _, _) + | (_, _, Some false, _, _) + | (_, _, _, Some false, _) + | (_, _, _, _, Some false) -> false | _ -> true (* f0: constructors *) - let top () = create { fi = fun (type a) (module I:S with type t = a) -> I.top } () - let bot () = create { fi = fun (type a) (module I:S with type t = a) -> I.bot } () - let top_of = create { fi = fun (type a) (module I:S with type t = a) -> I.top_of } - let bot_of = create { fi = fun (type a) (module I:S with type t = a) -> I.bot_of } - let of_bool ik = create { fi = fun (type a) (module I:S with type t = a) -> I.of_bool ik } - let of_excl_list ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.of_excl_list ik} - let of_int ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.of_int ik } - let starting ?(suppress_ovwarn=false) ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.starting ~suppress_ovwarn ik } - let ending ?(suppress_ovwarn=false) ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.ending ~suppress_ovwarn ik } - let of_interval ?(suppress_ovwarn=false) ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.of_interval ~suppress_ovwarn ik } - let of_congruence ik = create2 { fi2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.of_congruence ik } - - let refine_with_congruence ik ((a, b, c, d) : t) (cong : (int_t * int_t) option) : t= + let top () = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.top } () + let bot () = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.bot } () + let top_of = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.top_of } + let bot_of = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.bot_of } + let of_bool ik = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.of_bool ik } + let of_excl_list ik = create2 { fi2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_excl_list ik} + let of_int ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_int ik } + let starting ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.starting ~suppress_ovwarn ik } + let ending ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.ending ~suppress_ovwarn ik } + let of_interval ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_interval ~suppress_ovwarn ik } + let of_congruence ik = create2 { fi2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_congruence ik } + + let refine_with_congruence ik ((a, b, c, d, e) : t) (cong : (int_t * int_t) option) : t= let opt f a = curry @@ function Some x, y -> Some (f a x y) | _ -> None in ( opt I1.refine_with_congruence ik a cong , opt I2.refine_with_congruence ik b cong , opt I3.refine_with_congruence ik c cong - , opt I4.refine_with_congruence ik d cong ) + , opt I4.refine_with_congruence ik d cong + , opt I5.refine_with_congruence ik e cong) - let refine_with_interval ik (a, b, c, d) intv = + let refine_with_interval ik (a, b, c, d, e) intv = let opt f a = curry @@ function Some x, y -> Some (f a x y) | _ -> None in ( opt I1.refine_with_interval ik a intv , opt I2.refine_with_interval ik b intv , opt I3.refine_with_interval ik c intv - , opt I4.refine_with_interval ik d intv ) + , opt I4.refine_with_interval ik d intv + , opt I5.refine_with_interval ik e intv ) - let refine_with_excl_list ik (a, b, c, d) excl = + let refine_with_excl_list ik (a, b, c, d, e) excl = let opt f a = curry @@ function Some x, y -> Some (f a x y) | _ -> None in ( opt I1.refine_with_excl_list ik a excl , opt I2.refine_with_excl_list ik b excl , opt I3.refine_with_excl_list ik c excl - , opt I4.refine_with_excl_list ik d excl ) + , opt I4.refine_with_excl_list ik d excl + , opt I5.refine_with_excl_list ik e excl ) - let refine_with_incl_list ik (a, b, c, d) incl = + let refine_with_incl_list ik (a, b, c, d, e) incl = let opt f a = curry @@ function Some x, y -> Some (f a x y) | _ -> None in ( opt I1.refine_with_incl_list ik a incl , opt I2.refine_with_incl_list ik b incl , opt I3.refine_with_incl_list ik c incl - , opt I4.refine_with_incl_list ik d incl ) + , opt I4.refine_with_incl_list ik d incl + , opt I5.refine_with_incl_list ik e incl ) - let mapp r (a, b, c, d) = + let mapp r (a, b, c, d, e) = let map = BatOption.map in ( map (r.fp (module I1)) a , map (r.fp (module I2)) b , map (r.fp (module I3)) c - , map (r.fp (module I4)) d) + , map (r.fp (module I4)) d + , map (r.fp (module I5)) e) - let mapp2 r (a, b, c, d) = + let mapp2 r (a, b, c, d, e) = BatOption. - ( map (r.fp2 (module I1)) a - , map (r.fp2 (module I2)) b - , map (r.fp2 (module I3)) c - , map (r.fp2 (module I4)) d ) + ( map (r.fp2 (module I1)) a + , map (r.fp2 (module I2)) b + , map (r.fp2 (module I3)) c + , map (r.fp2 (module I4)) d + , map (r.fp2 (module I5)) e) (* exists/for_all *) - let is_bot = exists % mapp { fp = fun (type a) (module I:S with type t = a) -> I.is_bot } - let is_top = for_all % mapp { fp = fun (type a) (module I:S with type t = a) -> I.is_top } - let is_top_of ik = for_all % mapp { fp = fun (type a) (module I:S with type t = a) -> I.is_top_of ik } - let is_excl_list = exists % mapp { fp = fun (type a) (module I:S with type t = a) -> I.is_excl_list } - - let map2p r (xa, xb, xc, xd) (ya, yb, yc, yd) = - ( opt_map2 (r.f2p (module I1)) xa ya - , opt_map2 (r.f2p (module I2)) xb yb - , opt_map2 (r.f2p (module I3)) xc yc - , opt_map2 (r.f2p (module I4)) xd yd ) + let is_bot = exists % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_bot } + let is_top = for_all % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_top } + let is_top_of ik = for_all % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_top_of ik } + let is_excl_list = exists % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_excl_list } + + let map2p r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = + ( opt_map2 (r.f2p (module I1)) xa ya + , opt_map2 (r.f2p (module I2)) xb yb + , opt_map2 (r.f2p (module I3)) xc yc + , opt_map2 (r.f2p (module I4)) xd yd + , opt_map2 (r.f2p (module I5)) xe ye) (* f2p: binary projections *) let (%%) f g x = f % (g x) (* composition for binary function g *) let leq = for_all - %% map2p {f2p= (fun (type a) (module I : S with type t = a) ?no_ov -> I.leq)} + %% map2p {f2p= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.leq)} let flat f x = match to_list_some x with [] -> None | xs -> Some (f xs) @@ -2751,7 +3457,7 @@ module IntDomTupleImpl = struct let (mins, maxs) = List.split rs in (List.concat vs, (List.min mins, List.max maxs)) in - mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge + mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge let to_incl_list x = let hd l = match l with h::t -> h | _ -> [] in @@ -2760,104 +3466,103 @@ module IntDomTupleImpl = struct let b y = BatList.map BatSet.of_list (tl y) in let merge y = BatSet.elements @@ BatList.fold BatSet.intersect (a y) (b y) in - mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.to_incl_list } x |> flat merge + mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_incl_list } x |> flat merge - let pretty () = (fun xs -> text "(" ++ (try List.reduce (fun a b -> a ++ text "," ++ b) xs with Invalid_argument _ -> nil) ++ text ")") % to_list % mapp { fp = fun (type a) (module I:S with type t = a) -> (* assert sf==I.short; *) I.pretty () } (* NOTE: the version above does something else. also, we ignore the sf-argument here. *) + let pretty () = (fun xs -> text "(" ++ (try List.reduce (fun a b -> a ++ text "," ++ b) xs with Invalid_argument _ -> nil) ++ text ")") % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> (* assert sf==I.short; *) I.pretty () } (* NOTE: the version above does something else. also, we ignore the sf-argument here. *) let refine_functions ik : (t -> t) list = let maybe reffun ik domtup dom = match dom with Some y -> reffun ik domtup y | _ -> domtup in - [(fun (a, b, c, d) -> refine_with_excl_list ik (a, b, c, d) (to_excl_list (a, b, c, d))); - (fun (a, b, c, d) -> refine_with_incl_list ik (a, b, c, d) (to_incl_list (a, b, c, d))); - (fun (a, b, c, d) -> maybe refine_with_interval ik (a, b, c, d) b); - (fun (a, b, c, d) -> maybe refine_with_congruence ik (a, b, c, d) d)] + [(fun (a, b, c, d, e) -> refine_with_excl_list ik (a, b, c, d, e) (to_excl_list (a, b, c, d, e))); + (fun (a, b, c, d, e) -> refine_with_incl_list ik (a, b, c, d, e) (to_incl_list (a, b, c, d, e))); + (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); + (fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)] - let refine ik ((a, b, c, d ) : t ) : t = - let dt = ref (a, b, c, d) in + let refine ik ((a, b, c, d, e) : t ) : t = + let dt = ref (a, b, c, d, e) in (match GobConfig.get_string "ana.int.refinement" with - | "never" -> () - | "once" -> + | "never" -> () + | "once" -> + List.iter (fun f -> dt := f !dt) (refine_functions ik); + | "fixpoint" -> + let quit_loop = ref false in + while not !quit_loop do + let old_dt = !dt in List.iter (fun f -> dt := f !dt) (refine_functions ik); - | "fixpoint" -> - let quit_loop = ref false in - while not !quit_loop do - let old_dt = !dt in - List.iter (fun f -> dt := f !dt) (refine_functions ik); - quit_loop := equal old_dt !dt; - if is_bot !dt then dt := bot_of ik; quit_loop := true; - if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a\n" pretty old_dt pretty !dt; - done; - | _ -> () + quit_loop := equal old_dt !dt; + if is_bot !dt then dt := bot_of ik; quit_loop := true; + if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a\n" pretty old_dt pretty !dt; + done; + | _ -> () ); !dt - let no_overflow ik r = - if should_ignore_overflow ik then true - else let ika, ikb = Size.range ik in - match I2.minimal r, I2.maximal r with - | Some ra, Some rb -> BI.compare ika ra < 0 || BI.compare rb ikb < 0 - | _ -> false (* map with overflow check *) - let mapovc ik r (a, b, c, d) = + let mapovc ?(cast=false) ik r (a, b, c, d, e) = let map f ?no_ov = function Some x -> Some (f ?no_ov x) | _ -> None in - let intv = map (r.f1 (module I2)) b in - let no_ov = - match intv with Some i -> no_overflow ik i | _ -> should_ignore_overflow ik - in refine ik - ( map (r.f1 (module I1)) a - , intv - , map (r.f1 (module I3)) c - , map (r.f1 (module I4)) ~no_ov d ) + let intv = map (r.f1_ovc (module I2)) b in + let intv_set = map (r.f1_ovc (module I5)) e in + let no_ov = check_ov ik intv intv_set in + let no_ov = no_ov || should_ignore_overflow ik in + refine ik + ( map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I1) x |> fst) a + , BatOption.map fst intv + , map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I3) x |> fst) c + , map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I4) x |> fst) ~no_ov d + , BatOption.map fst intv_set ) (* map2 with overflow check *) - let map2ovc ik r (xa, xb, xc, xd) (ya, yb, yc, yd) = - let intv = opt_map2 (r.f2 (module I2)) xb yb in - let no_ov = - match intv with Some i -> no_overflow ik i | _ -> should_ignore_overflow ik - in + let map2ovc ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = + let intv = opt_map2 (r.f2_ovc (module I2)) xb yb in + let intv_set = opt_map2 (r.f2_ovc (module I5)) xe ye in + let no_ov = check_ov ik intv intv_set in + let no_ov = no_ov || should_ignore_overflow ik in refine ik - ( opt_map2 (r.f2 (module I1)) xa ya - , intv - , opt_map2 (r.f2 (module I3)) xc yc - , opt_map2 (r.f2 (module I4)) ~no_ov xd yd ) + ( opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I1) x y |> fst) xa ya + , BatOption.map fst intv + , opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I3) x y |> fst) xc yc + , opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I4) x y |> fst) ~no_ov:no_ov xd yd + , BatOption.map fst intv_set ) - let map ik r (a, b, c, d) = + let map ik r (a, b, c, d, e) = refine ik BatOption. ( map (r.f1 (module I1)) a , map (r.f1 (module I2)) b , map (r.f1 (module I3)) c - , map (r.f1 (module I4)) d ) + , map (r.f1 (module I4)) d + , map (r.f1 (module I5)) e) - let map2 ?(norefine=false) ik r (xa, xb, xc, xd) (ya, yb, yc, yd) = + let map2 ?(norefine=false) ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = let r = ( opt_map2 (r.f2 (module I1)) xa ya , opt_map2 (r.f2 (module I2)) xb yb , opt_map2 (r.f2 (module I3)) xc yc - , opt_map2 (r.f2 (module I4)) xd yd ) + , opt_map2 (r.f2 (module I4)) xd yd + , opt_map2 (r.f2 (module I5)) xe ye) in if norefine then r else refine ik r (* f1: unary ops *) let neg ?no_ov ik = - mapovc ik {f1= (fun (type a) (module I : S with type t = a) ?no_ov -> I.neg ?no_ov ik)} + mapovc ik {f1_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.neg ?no_ov ik)} let bitnot ik = - map ik {f1= (fun (type a) (module I : S with type t = a) ?no_ov -> I.bitnot ik)} + map ik {f1 = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.bitnot ik)} let lognot ik = - map ik {f1= (fun (type a) (module I : S with type t = a) ?no_ov -> I.lognot ik)} + map ik {f1 = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.lognot ik)} let cast_to ?torg ?no_ov t = - mapovc t {f1= (fun (type a) (module I : S with type t = a) ?no_ov -> I.cast_to ?torg ?no_ov t)} + mapovc ~cast:true t {f1_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.cast_to ?torg ?no_ov t)} (* fp: projections *) let equal_to i x = - let xs = mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.equal_to i } x |> Tuple4.enum |> List.of_enum |> List.filter_map identity in + let xs = mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.equal_to i } x |> Tuple5.enum |> List.of_enum |> List.filter_map identity in if List.mem `Eq xs then `Eq else if List.mem `Neq xs then `Neq else `Top @@ -2867,23 +3572,24 @@ module IntDomTupleImpl = struct if n>1 then Messages.info ~category:Unsound "Inconsistent state! %a" (Pretty.docList ~sep:(Pretty.text ",") (Pretty.text % show)) us; (* do not want to abort *) None ) - let to_int = same BI.to_string % mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.to_int } - let to_bool = same string_of_bool % mapp { fp = fun (type a) (module I:S with type t = a) -> I.to_bool } - let minimal = flat (List.max ~cmp:BI.compare) % mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.minimal } - let maximal = flat (List.min ~cmp:BI.compare) % mapp2 { fp2 = fun (type a) (module I:S with type t = a and type int_t = int_t) -> I.maximal } + let to_int = same BI.to_string % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_int } + let to_bool = same string_of_bool % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.to_bool } + let minimal = flat (List.max ~cmp:BI.compare) % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.minimal } + let maximal = flat (List.min ~cmp:BI.compare) % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.maximal } (* others *) - let show = String.concat "; " % to_list % mapp { fp = fun (type a) (module I:S with type t = a) x -> I.name () ^ ":" ^ (I.show x) } - let to_yojson = [%to_yojson: Yojson.Safe.t list] % to_list % mapp { fp = fun (type a) (module I:S with type t = a) x -> I.to_yojson x } - let hash = List.fold_left (lxor) 0 % to_list % mapp { fp = fun (type a) (module I:S with type t = a) -> I.hash } + let show = String.concat "; " % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.name () ^ ":" ^ (I.show x) } + let to_yojson = [%to_yojson: Yojson.Safe.t list] % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.to_yojson x } + let hash = List.fold_left (lxor) 0 % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.hash } (* `map/opt_map` are used by `project` *) let opt_map b f = curry @@ function None, true -> f | x, y when y || b -> x | _ -> None - let map ~keep r (i1, i2, i3, i4) (b1, b2, b3, b4) = + let map ~keep r (i1, i2, i3, i4, i5) (b1, b2, b3, b4, b5) = ( opt_map keep (r.f3 (module I1)) i1 b1 , opt_map keep (r.f3 (module I2)) i2 b2 , opt_map keep (r.f3 (module I3)) i3 b3 - , opt_map keep (r.f3 (module I4)) i4 b4 ) + , opt_map keep (r.f3 (module I4)) i4 b4 + , opt_map keep (r.f3 (module I5)) i5 b5 ) (** Project tuple t to precision p * We have to deactivate IntDomains after the refinement, since we might @@ -2902,81 +3608,81 @@ module IntDomTupleImpl = struct * This way we won't loose any information for the refinement. * ~keep:false will set the elements to `None` as defined by p *) let project ik (p: int_precision) t = - let t_padded = map ~keep:true { f3 = fun (type a) (module I:S with type t = a) -> Some (I.top_of ik) } t p in + let t_padded = map ~keep:true { f3 = fun (type a) (module I:SOverflow with type t = a) -> Some (I.top_of ik) } t p in let t_refined = refine ik t_padded in - map ~keep:false { f3 = fun (type a) (module I:S with type t = a) -> None } t_refined p + map ~keep:false { f3 = fun (type a) (module I:SOverflow with type t = a) -> None } t_refined p (* f2: binary ops *) let join ik = - map2 ~norefine:true ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.join ik)} + map2 ~norefine:true ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.join ik)} let meet ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.meet ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.meet ik)} let widen ik = - map2 ~norefine:true ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.widen ik)} + map2 ~norefine:true ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.widen ik)} let narrow ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.narrow ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.narrow ik)} let add ?no_ov ik = map2ovc ik - {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.add ?no_ov ik)} + {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.add ?no_ov ik)} let sub ?no_ov ik = map2ovc ik - {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.sub ?no_ov ik)} + {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.sub ?no_ov ik)} let mul ?no_ov ik = map2ovc ik - {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.mul ?no_ov ik)} + {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.mul ?no_ov ik)} let div ?no_ov ik = map2ovc ik - {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.div ?no_ov ik)} + {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.div ?no_ov ik)} let rem ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.rem ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.rem ik)} let lt ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.lt ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.lt ik)} let gt ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.gt ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.gt ik)} let le ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.le ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.le ik)} let ge ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.ge ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.ge ik)} let eq ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.eq ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.eq ik)} let ne ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.ne ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.ne ik)} let bitand ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.bitand ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.bitand ik)} let bitor ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.bitor ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.bitor ik)} let bitxor ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.bitxor ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.bitxor ik)} let shift_left ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.shift_left ik)} + map2ovc ik {f2_ovc= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.shift_left ik)} let shift_right ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.shift_right ik)} + map2ovc ik {f2_ovc= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.shift_right ik)} let logand ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.logand ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.logand ik)} let logor ik = - map2 ik {f2= (fun (type a) (module I : S with type t = a) ?no_ov -> I.logor ik)} + map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.logor ik)} (* printing boilerplate *) @@ -2992,22 +3698,23 @@ module IntDomTupleImpl = struct else Invariant.top () | None -> - let is = to_list (mapp { fp = fun (type a) (module I:S with type t = a) -> I.invariant_ikind e ik } x) + let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) in List.fold_left (fun a i -> Invariant.(a && i) ) (Invariant.top ()) is - let arbitrary ik = QCheck.(set_print show @@ quad (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik))) + let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) end module IntDomTuple = struct - module I = IntDomLifter (IntDomTupleImpl) - include I + module I = IntDomLifter (IntDomTupleImpl) + include I - let top () = failwith "top in IntDomTuple not supported. Use top_of instead." - let no_interval (x: I.t) = {x with v = IntDomTupleImpl.no_interval x.v} + let top () = failwith "top in IntDomTuple not supported. Use top_of instead." + let no_interval (x: I.t) = {x with v = IntDomTupleImpl.no_interval x.v} + let no_intervalSet (x: I.t) = {x with v = IntDomTupleImpl.no_intervalSet x.v} end let of_const (i, ik, str) = IntDomTuple.of_int ik i diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index 04e3d183eb..4671fb3013 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -7,6 +7,8 @@ val should_ignore_overflow: Cil.ikind -> bool val reset_lazy: unit -> unit +type overflow_info = { overflow: bool; underflow: bool;} + module type Arith = sig type t @@ -273,6 +275,39 @@ sig end (** Interface of IntDomain implementations taking an ikind for arithmetic operations *) +module type SOverflow = +sig + + include S + + val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info + + val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info + + val cast_to : ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info + + val of_int : Cil.ikind -> int_t -> t * overflow_info + + val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info + + val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info + val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info + + val shift_left : Cil.ikind -> t -> t -> t * overflow_info + + val shift_right: Cil.ikind -> t -> t -> t * overflow_info + + +end + +module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t + module OldDomainFacade (Old : IkindUnawareS with type int_t = int64) : S with type int_t = IntOps.BigIntOps.t and type t = Old.t (** Facade for IntDomain implementations that do not implement the interface where arithmetic functions take an ikind parameter. *) @@ -320,6 +355,7 @@ module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type module IntDomTuple : sig include Z val no_interval: t -> t + val no_intervalSet: t -> t val ikind: t -> ikind end @@ -369,7 +405,9 @@ module FlattenedBI : IkindUnawareS with type t = [`Top | `Lifted of IntOps.BigIn module Lifted : IkindUnawareS with type t = [`Top | `Lifted of int64 | `Bot] and type int_t = int64 (** Artificially bounded integers in their natural ordering. *) -module IntervalFunctor(Ints_t : IntOps.IntOps): S with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option +module IntervalFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option + +module IntervalSetFunctor(Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list module Interval32 :Y with (* type t = (IntOps.Int64Ops.t * IntOps.Int64Ops.t) option and *) type int_t = IntOps.Int64Ops.t @@ -379,7 +417,9 @@ module BigInt: val cast_to: Cil.ikind -> Z.t -> Z.t end -module Interval : S with type int_t = IntOps.BigIntOps.t +module Interval : SOverflow with type int_t = IntOps.BigIntOps.t + +module IntervalSet : SOverflow with type int_t = IntOps.BigIntOps.t module Congruence : S with type int_t = IntOps.BigIntOps.t diff --git a/src/util/options.schema.json b/src/util/options.schema.json index c3346677d6..57359329e0 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -357,7 +357,13 @@ "interval": { "title": "ana.int.interval", "description": - "Use IntDomain.Interval32: (int64 * int64) option.", + "Use IntDomain.Interval32: (Z.t * Z.t) option.", + "type": "boolean", + "default": false + }, + "interval_set": { + "title": "ana.int.interval_set", + "description": "Use IntDomain.IntervalSet: (Z.t * Z.t) list.", "type": "boolean", "default": false }, @@ -589,6 +595,13 @@ "Integer values of the Interval domain in function contexts.", "type": "boolean", "default": true + }, + "interval_set": { + "title": "ana.base.context.interval_set", + "description": + "Integer values of the IntervalSet domain in function contexts.", + "type": "boolean", + "default": true } }, "additionalProperties": false @@ -1430,7 +1443,7 @@ "type": "array", "items": { "type": "string", - "enum": ["base.no-non-ptr", "base.non-ptr", "base.no-int", "base.int", "base.no-interval", "base.interval", "relation.no-context", "relation.context", "no-widen", "widen"] + "enum": ["base.no-non-ptr", "base.non-ptr", "base.no-int", "base.int", "base.no-interval", "base.no-interval_set","base.interval", "base.interval_set","relation.no-context", "relation.context", "no-widen", "widen"] }, "default": [] } diff --git a/src/util/precisionUtil.ml b/src/util/precisionUtil.ml index 0b0480251d..15c4894918 100644 --- a/src/util/precisionUtil.ml +++ b/src/util/precisionUtil.ml @@ -1,19 +1,20 @@ (* We define precision by the number of IntDomains activated. - * We currently have 4 types: DefExc, Interval, Enums, Congruence *) -type int_precision = (bool * bool * bool * bool) + * We currently have 5 types: DefExc, Interval, Enums, Congruence, IntervalSet *) +type int_precision = (bool * bool * bool * bool * bool) (* Same applies for FloatDomain * We currently have only an interval type analysis *) type float_precision = (bool) (* Thus for maximum precision we activate all Domains *) -let max_int_precision : int_precision = (true, true, true, true) +let max_int_precision : int_precision = (true, true, true, true, true) let max_float_precision : float_precision = (true) let int_precision_from_fundec (fd: GoblintCil.fundec): int_precision = ((ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.def_exc" ~removeAttr:"no-def_exc" ~keepAttr:"def_exc" fd), (ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.interval" ~removeAttr:"no-interval" ~keepAttr:"interval" fd), (ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.enums" ~removeAttr:"no-enums" ~keepAttr:"enums" fd), - (ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.congruence" ~removeAttr:"no-congruence" ~keepAttr:"congruence" fd)) + (ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.congruence" ~removeAttr:"no-congruence" ~keepAttr:"congruence" fd), + (ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.int.interval_set" ~removeAttr:"no-interval_set" ~keepAttr:"interval_set" fd)) let float_precision_from_fundec (fd: GoblintCil.fundec): float_precision = ((ContextUtil.should_keep ~isAttr:GobPrecision ~keepOption:"ana.float.interval" ~removeAttr:"no-float-interval" ~keepAttr:"float-interval" fd)) @@ -22,7 +23,7 @@ let int_precision_from_node (): int_precision = | Some n -> int_precision_from_fundec (Node.find_fundec n) | _ -> max_int_precision (* In case a Node is None we have to handle Globals, i.e. we activate all IntDomains (TODO: verify this assumption) *) -let is_congruence_active (_, _, _, c: int_precision): bool = c +let is_congruence_active (_, _, _, c,_: int_precision): bool = c let float_precision_from_node (): float_precision = match !MyCFG.current_node with @@ -34,7 +35,7 @@ let int_precision_from_node_or_config (): int_precision = int_precision_from_node () else let f n = GobConfig.get_bool ("ana.int."^n) in - (f "def_exc", f "interval", f "enums", f "congruence") + (f "def_exc", f "interval", f "enums", f "congruence", f "interval_set") let float_precision_from_node_or_config (): float_precision = if GobConfig.get_bool "annotation.float.enabled" then diff --git a/tests/regression/34-localwn_restart/04-hh.c b/tests/regression/34-localwn_restart/04-hh.c index 4566ffc1aa..bfbfb1b2ab 100644 --- a/tests/regression/34-localwn_restart/04-hh.c +++ b/tests/regression/34-localwn_restart/04-hh.c @@ -5,7 +5,7 @@ // Example from Halbwachs-Henry, SAS 2012 // Localized widening or restart policy should be able to prove that i <= j+3 // if the abstract domain is powerful enough. -#include > +#include void main() { diff --git a/tests/regression/46-apron2/32-iset_array_octagon.c b/tests/regression/46-apron2/32-iset_array_octagon.c new file mode 100644 index 0000000000..390ce814f2 --- /dev/null +++ b/tests/regression/46-apron2/32-iset_array_octagon.c @@ -0,0 +1,379 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron --set sem.int.signed_overflow assume_none +#include + +void main(void) { + example0(); + example1(); + example2(); + example3(); + example4(); + example4a(); + example4b(); + example4c(); + example5(); + example6(); + example7(); + example8(); + mineEx1(); +} + +void example0(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 1; + j++; + } + + a[j] = 2; // a -> (j,([1,1],[2,2],[0,0])) + + + z = j; + + // Values that may be read are 1 or 2 + __goblint_check(a[z] == 1); // FAIL + __goblint_check(a[z] == 2); + __goblint_check(z >= 0); + __goblint_check(z <= j); + __goblint_check(a[z] == 0); //FAIL +} + +void example1(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 1; + j++; + } + + a[j] = 2; // a -> (j,([1,1],[2,2],[0,0])) + + if(top) { + z = j; + } else { + z = j-1; + } + + // Values that may be read are 1 or 2 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 2); //UNKNOWN + __goblint_check(z >= 0); + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(z <= j); + __goblint_check(a[z] != 0); +} + +void example2(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 2; + j++; + } + + a[j] = 1; // a -> (j,([2,2],[1,1],[0,0])) + + if(top) { + z = j; + } else { + z = j+1; + } + + // Values that may be read are 1 or 0 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 0); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 2); +} + +// Simple example (employing MustBeEqual) +void example3(void) { + int a[42]; + int i = 0; + int x; + + while(i < 42) { + a[i] = 0; + int v = i; + x = a[v]; + __goblint_check(x == 0); + i++; + } +} + +// Simple example (employing MayBeEqual / MayBeSmaller) +void example4(void) { + int a[42]; + int i = 0; + + while(i<=9) { + a[i] = 9; + int j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + int k = a[i-1]; + __goblint_check(k == 9); + + int l = a[0]; + __goblint_check(l == 9); + } + + i++; + } +} +// Just like the example before except that it tests correct behavior when variable order is reversed +void example4a(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); + } + + i++; + } +} + +// Just like the example before except that it tests correct behavior when operands for + are reversed +void example4b(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = 5+i; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); + } + + i++; + } +} + +// Like example before but iterating backwards +void example4c(void) { + int a[42]; + int j; + int i = 41; + + while(i > 8) { + a[i] = 7; + a[i-2] = 31; + + if(i < 41) { + __goblint_check(a[i+1] == 7); + } + + i--; + } +} + +void example5(void) { + int a[40]; + int i = 0; + + // This is a dirty cheat to get the array to be partitioned before entering the loop + // This is needed because the may be less of the octagons is not sophisticated enough yet. + // Once that is fixed this will also work without this line + a[i] = 0; + + while(i < 42) { + int j = i; + a[j] = 0; + i++; + + __goblint_check(a[i] == 0); //UNKNOWN + + __goblint_check(a[i-1] == 0); + __goblint_check(a[j] == 0); + + if (i>1) { + __goblint_check(a[i-2] == 0); + __goblint_check(a[j-1] == 0); + } + } +} + +void example6(void) { + int a[42]; + int i = 0; + int top; + + while(i<30) { + a[i] = 0; + i++; + + __goblint_check(a[top] == 0); //UNKNOWN + + int j=0; + while(j 10) { + if(top) { + j = i-5; + } else { + j = i-7; + } + + __goblint_check(a[j] == 0); + } + } +} + +void example8(void) { + int a[42]; + int i = 0; + int j = i; + + int N; + + if(N < 5) { + N = 5; + } + if(N > 40) { + N = 40; + } + + + while(i < N) { + a[i] = 0; + i++; + j = i; + a[j-1] = 0; + a[j] = 0; + j++; // Octagon knows -1 <= i-j <= -1 + i = j; // Without octagons, we lose partitioning here because we don't know how far the move has been + + __goblint_check(a[i-1] == 0); + __goblint_check(a[i-2] == 0); + } + + j = 0; + while(j < N) { + __goblint_check(a[j] == 0); + j++; + } +} + +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +void mineEx1(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); + // __goblint_check(X == N); // Currently not able to assert this because octagon doesn't handle it + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currenlty we can't detect this + N = 42; + } +} diff --git a/tests/regression/46-apron2/33-iset_no-context.c b/tests/regression/46-apron2/33-iset_no-context.c new file mode 100644 index 0000000000..f1ef90b9c5 --- /dev/null +++ b/tests/regression/46-apron2/33-iset_no-context.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval_set --disable ana.relation.context +extern int __VERIFIER_nondet_int(); + +#include + +int oct(int x, int y) { + int s; + if (x <= y) + s = 1; + else + s = 0; + return s; +} + +void main() { + int x = __VERIFIER_nondet_int(); //rand + int y = __VERIFIER_nondet_int(); //rand + int res; + if (x <= y) { + res = oct(x, y); + __goblint_check(res == 1); // UNKNOWN (indended by disabled context) + } + + res = oct(x, y); +} diff --git a/tests/regression/46-apron2/34-iset_previously_problematic_c.c b/tests/regression/46-apron2/34-iset_previously_problematic_c.c new file mode 100644 index 0000000000..3c1847bbcf --- /dev/null +++ b/tests/regression/46-apron2/34-iset_previously_problematic_c.c @@ -0,0 +1,30 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int i = 0; + int j; + int nGroups; + int bliblablue; + int inUse16[16] ; + + + nGroups = 2; + bliblablue = 4; + + i = 0; + + while (i < 16) { + inUse16[i] = 0; + + j = 0; + while (j < 16) { + j++; + } + + i++; + } + + return 0; +} diff --git a/tests/regression/46-apron2/35-iset_hh.c b/tests/regression/46-apron2/35-iset_hh.c new file mode 100644 index 0000000000..d29c265d5a --- /dev/null +++ b/tests/regression/46-apron2/35-iset_hh.c @@ -0,0 +1,23 @@ +// SKIP PARAM: --enable ana.int.interval_set --set solver td3 --set ana.activated[+] apron --set sem.int.signed_overflow assume_none +// This is part of 34-localization, but also symlinked to 36-apron. + +// ALSO: --enable ana.int.interval_set --set solver slr3 --set ana.activated[+] apron --set sem.int.signed_overflow assume_none +// Example from Halbwachs-Henry, SAS 2012 +// Localized widening or restart policy should be able to prove that i <= j+3 +// if the abstract domain is powerful enough. +#include > + +void main() +{ + int i = 0; + while (i<4) { + int j=0; + while (j<4) { + i=i+1; + j=j+1; + } + i = i-j+1; + __goblint_check(i <= j+3); + } + return ; +} diff --git a/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c new file mode 100644 index 0000000000..4cbcaef064 --- /dev/null +++ b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int i = 0; + int j; + int nGroups = 6; + int inUse16[16]; + + while (i < 16) + { // TO-DO: here + inUse16[i] = 0; + j = 0; + i++; + } + + return 0; +} diff --git a/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c new file mode 100644 index 0000000000..918e67b0e8 --- /dev/null +++ b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int a[256]; + int i = 0; + int zPend = 0; + int ll_i; + int nblock; + + while (i < nblock) + { // TO-DO: Here + if (a[0] == ll_i) // this needs to be var == var for the problem to occur + { + zPend++; + } + else + { + a[0] = 8; + } + + i++; + } + + return 0; +} diff --git a/tests/regression/46-apron2/38-iset_address.c b/tests/regression/46-apron2/38-iset_address.c new file mode 100644 index 0000000000..c75f047b2b --- /dev/null +++ b/tests/regression/46-apron2/38-iset_address.c @@ -0,0 +1,28 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +#include + +void main(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + int* ptr = &N; + *ptr = N; + + + __goblint_check(X-N == 0); + __goblint_check(X == N); + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currenlty we can't detect this + N = 42; + } +} diff --git a/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c new file mode 100644 index 0000000000..18a53d31aa --- /dev/null +++ b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int l; + int r = 42; + + while(1) { + if (l-r <= 0) { + r--; + } else { + break; + } + } + + return 0; +} diff --git a/tests/regression/46-apron2/40-iset_context-attribute.c b/tests/regression/46-apron2/40-iset_context-attribute.c new file mode 100644 index 0000000000..974e00c820 --- /dev/null +++ b/tests/regression/46-apron2/40-iset_context-attribute.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval_set --disable ana.relation.context +extern int __VERIFIER_nondet_int(); + +#include + +int oct(int x, int y) __attribute__((goblint_context("relation.context"))); // attributes are not permitted in a function definition +int oct(int x, int y) { + int s; + if (x <= y) + s = 1; + else + s = 0; + return s; +} + +void main() { + int x = __VERIFIER_nondet_int(); //rand + int y = __VERIFIER_nondet_int(); //rand + int res; + if (x <= y) { + res = oct(x, y); + __goblint_check(res == 1); + } + + res = oct(x, y); +} diff --git a/tests/regression/46-apron2/41-iset_no-context-attribute.c b/tests/regression/46-apron2/41-iset_no-context-attribute.c new file mode 100644 index 0000000000..dd1da4795d --- /dev/null +++ b/tests/regression/46-apron2/41-iset_no-context-attribute.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval_set --enable ana.relation.context +extern int __VERIFIER_nondet_int(); + +#include + +int oct(int x, int y) __attribute__((goblint_context("relation.no-context"))); // attributes are not permitted in a function definition +int oct(int x, int y) { + int s; + if (x <= y) + s = 1; + else + s = 0; + return s; +} + +void main() { + int x = __VERIFIER_nondet_int(); //rand + int y = __VERIFIER_nondet_int(); //rand + int res; + if (x <= y) { + res = oct(x, y); + __goblint_check(res == 1); // UNKNOWN (indended by no-context attribute) + } + + res = oct(x, y); +} diff --git a/tests/regression/46-apron2/42-iset_octagon_interprocedural.c b/tests/regression/46-apron2/42-iset_octagon_interprocedural.c new file mode 100644 index 0000000000..a58b04bcf8 --- /dev/null +++ b/tests/regression/46-apron2/42-iset_octagon_interprocedural.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +#include + +int main(void) { + f1(); +} + +int f1() { + int one; + int two; + + int x; + + one = two; + + __goblint_check(one - two == 0); + x = f2(one,two); + __goblint_check(one - two == 0); + __goblint_check(x == 48); +} + +int f2(int a, int b) { + __goblint_check(a-b == 0); + + return 48; +} diff --git a/tests/regression/46-apron2/43-iset_array_octagon_prec.c b/tests/regression/46-apron2/43-iset_array_octagon_prec.c new file mode 100644 index 0000000000..dfa8c6d84d --- /dev/null +++ b/tests/regression/46-apron2/43-iset_array_octagon_prec.c @@ -0,0 +1,350 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron --enable annotation.int.enabled --set ana.int.refinement fixpoint --set sem.int.signed_overflow assume_none +#include + +void main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2(void) __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example4a(void) __attribute__((goblint_precision("no-def_exc"))); +void example4b(void) __attribute__((goblint_precision("no-def_exc"))); +void example4c(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example7(void) __attribute__((goblint_precision("no-def_exc"))); +void example8(void) __attribute__((goblint_precision("no-def_exc"))); +void mineEx1(void) __attribute__((goblint_precision("no-def_exc"))); + + +void main(void) { + example1(); + example2(); + example3(); + example4(); + example4a(); + example4b(); + example4c(); + example5(); + example6(); + example7(); + example8(); + mineEx1(); +} + +void example1(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 1; + j++; + } + + a[j] = 2; // a -> (j,([1,1],[2,2],[0,0])) + + if(top) { + z = j; + } else { + z = j-1; + } + + // Values that may be read are 1 or 2 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 2); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 0); +} + +void example2(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 2; + j++; + } + + a[j] = 1; // a -> (j,([2,2],[1,1],[0,0])) + + if(top) { + z = j; + } else { + z = j+1; + } + + // Values that may be read are 1 or 0 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 0); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 2); +} + +// Simple example (employing MustBeEqual) +void example3(void) { + int a[42]; + int i = 0; + int x; + + while(i < 42) { + a[i] = 0; + int v = i; + x = a[v]; + __goblint_check(x == 0); + i++; + } +} + +// Simple example (employing MayBeEqual / MayBeSmaller) +void example4(void) { + int a[42]; + int i = 0; + + while(i<=9) { + a[i] = 9; + int j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + int k = a[i-1]; + __goblint_check(k == 9); + + int l = a[0]; + __goblint_check(l == 9); + } + + i++; + } +} +// Just like the example before except that it tests correct behavior when variable order is reversed +void example4a(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); + } + + i++; + } +} + +// Just like the example before except that it tests correct behavior when operands for + are reversed +void example4b(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = 5+i; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); + } + + i++; + } +} + +// Like example before but iterating backwards +void example4c(void) { + int a[42]; + int j; + int i = 41; + + while(i > 8) { + a[i] = 7; + a[i-2] = 31; + + if(i < 41) { + __goblint_check(a[i+1] == 7); + } + + i--; + } +} + +void example5(void) { + int a[40]; + int i = 0; + + // This is a dirty cheat to get the array to be partitioned before entering the loop + // This is needed because the may be less of the octagons is not sophisticated enough yet. + // Once that is fixed this will also work without this line + a[i] = 0; + + while(i < 42) { + int j = i; + a[j] = 0; + i++; + + __goblint_check(a[i] == 0); //UNKNOWN + + __goblint_check(a[i-1] == 0); + __goblint_check(a[j] == 0); + + if (i>1) { + __goblint_check(a[i-2] == 0); + __goblint_check(a[j-1] == 0); + } + } +} + +void example6(void) { + int a[42]; + int i = 0; + int top; + + while(i<30) { + a[i] = 0; + i++; + + __goblint_check(a[top] == 0); //UNKNOWN + + int j=0; + while(j 10) { + if(top) { + j = i-5; + } else { + j = i-7; + } + + __goblint_check(a[j] == 0); + } + } +} + +void example8(void) { + int a[42]; + int i = 0; + int j = i; + + int N; + + if(N < 5) { + N = 5; + } + if(N > 40) { + N = 40; + } + + + while(i < N) { + a[i] = 0; + i++; + j = i; + a[j-1] = 0; + a[j] = 0; + j++; // Octagon knows -1 <= i-j <= -1 + i = j; // Without octagons, we lose partitioning here because we don't know how far the move has been + + __goblint_check(a[i-1] == 0); + __goblint_check(a[i-2] == 0); + } + + j = 0; + while(j < N) { + __goblint_check(a[j] == 0); + j++; + } +} + +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +void mineEx1(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); + // __goblint_check(X == N); // Currently not able to assert this because octagon doesn't handle it + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currenlty we can't detect this + N = 42; + } +} diff --git a/tests/regression/46-apron2/44-iset_array_octagon_keep_last.c b/tests/regression/46-apron2/44-iset_array_octagon_keep_last.c new file mode 100644 index 0000000000..813ad88aa4 --- /dev/null +++ b/tests/regression/46-apron2/44-iset_array_octagon_keep_last.c @@ -0,0 +1,335 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --set ana.activated[+] apron --set sem.int.signed_overflow assume_none +#include + +void main(void) { + example1(); + example2(); + example3(); + example4(); + example4a(); + example4b(); + example4c(); + example5(); + example6(); + example7(); + example8(); + mineEx1(); +} + +void example1(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 1; + j++; + } + + a[j] = 2; // a -> (j,([1,1],[2,2],[0,0])) + + if(top) { + z = j; + } else { + z = j-1; + } + + // Values that may be read are 1 or 2 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 2); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 0); +} + +void example2(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 2; + j++; + } + + a[j] = 1; // a -> (j,([2,2],[1,1],[0,0])) + + if(top) { + z = j; + } else { + z = j+1; + } + + // Values that may be read are 1 or 0 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 0); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 2); +} + +// Simple example (employing MustBeEqual) +void example3(void) { + int a[42]; + int i = 0; + int x; + + while(i < 42) { + a[i] = 0; + int v = i; + x = a[v]; + __goblint_check(x == 0); + i++; + } +} + +// Simple example (employing MayBeEqual / MayBeSmaller) +void example4(void) { + int a[42]; + int i = 0; + + while(i<=9) { + a[i] = 9; + int j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); // UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + int k = a[i-1]; + __goblint_check(k == 9); // UNKNOWN + + int l = a[0]; + __goblint_check(l == 9); // UNKNOWN + } + + i++; + } +} +// Just like the example before except that it tests correct behavior when variable order is reversed +void example4a(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); //UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); //UNKNOWN + } + + i++; + } +} + +// Just like the example before except that it tests correct behavior when operands for + are reversed +void example4b(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = 5+i; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); //UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); //UNKNOWN + } + + i++; + } +} + +// Like example before but iterating backwards +void example4c(void) { + int a[42]; + int j; + int i = 41; + + while(i > 8) { + a[i] = 7; + a[i-2] = 31; + + if(i < 41) { + __goblint_check(a[i+1] == 7); //UNKNOWN + } + + i--; + } +} + +void example5(void) { + int a[40]; + int i = 0; + + // This is a dirty cheat to get the array to be partitioned before entering the loop + // This is needed because the may be less of the octagons is not sophisticated enough yet. + // Once that is fixed this will also work without this line + a[i] = 0; + + while(i < 42) { + int j = i; + a[j] = 0; + i++; + + __goblint_check(a[i] == 0); //UNKNOWN + + __goblint_check(a[i-1] == 0); + __goblint_check(a[j] == 0); + + if (i>1) { + __goblint_check(a[i-2] == 0); + __goblint_check(a[j-1] == 0); + } + } +} + +void example6(void) { + int a[42]; + int i = 0; + int top; + + while(i<30) { + a[i] = 0; + i++; + + __goblint_check(a[top] == 0); //UNKNOWN + + int j=0; + while(j 10) { + if(top) { + j = i-5; + } else { + j = i-7; + } + + __goblint_check(a[j] == 0); + } + } +} + +void example8(void) { + int a[42]; + int i = 0; + int j = i; + + int N; + + if(N < 5) { + N = 5; + } + if(N > 40) { + N = 40; + } + + + while(i < N) { + a[i] = 0; + i++; + j = i; + a[j-1] = 0; + a[j] = 0; + j++; // Octagon knows -1 <= i-j <= -1 + i = j; // Without octagons, we lose partitioning here because we don't know how far the move has been + + __goblint_check(a[i-1] == 0); + __goblint_check(a[i-2] == 0); + } + + j = 0; + while(j < N) { + __goblint_check(a[j] == 0); //UNKNOWN + j++; + } +} + +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +void mineEx1(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); + // __goblint_check(X == N); // Currently not able to assert this because octagon doesn't handle it + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currenlty we can't detect this + N = 42; + } +} diff --git a/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c new file mode 100644 index 0000000000..dab6078b98 --- /dev/null +++ b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c @@ -0,0 +1,41 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +char buf2[67]; + +int main(int argc, char **argv) +{ + int human_output_opts; + int to_block_size; + char buf1[67]; + char local; + + int from_block_size = 1; + + int exponent; + int exponent_max; + int buflen; + int power; + + memmove((void *)buf2, (void const *)buf1, 67); + + int bla = 18; + + if (human_output_opts & 128) + { + if (exponent < 0) + { + exponent = 0; + power = 1; + while (power < to_block_size) + { + exponent++; + if (exponent == exponent_max) + { + break; + } + } + } + } + return 0; +} diff --git a/tests/regression/46-apron2/46-iset_simple-apron-interval.c b/tests/regression/46-apron2/46-iset_simple-apron-interval.c new file mode 100644 index 0000000000..e6304f9edb --- /dev/null +++ b/tests/regression/46-apron2/46-iset_simple-apron-interval.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --set ana.activated[+] apron --enable ana.int.interval_set --set ana.apron.domain interval +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf, adapted +#include + +void main(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); //UNKNOWN + __goblint_check(X == N); //UNKNOWN + + if(X == N) { + N = 8; + } else { + N = 42; + } + __goblint_check(N == 8); // UNKNOWN + __goblint_check(N >= 8); + __goblint_check(N <= 42); +} diff --git a/tests/regression/46-apron2/47-iset_previously_problematic_a.c b/tests/regression/46-apron2/47-iset_previously_problematic_a.c new file mode 100644 index 0000000000..2bf6044560 --- /dev/null +++ b/tests/regression/46-apron2/47-iset_previously_problematic_a.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int top; + + int pad = 0; + int to_uppcase; + int change_case = 0; + + while (change_case != 1 && to_uppcase != 0) { + if(top == 1) { + to_uppcase = 1; + continue; + } + + if(top == 2) { + change_case = 1; + continue; + } + + break; + } + + return 0; +} diff --git a/tests/regression/46-apron2/48-iset_simple-polyhedra.c b/tests/regression/46-apron2/48-iset_simple-polyhedra.c new file mode 100644 index 0000000000..384b7b1654 --- /dev/null +++ b/tests/regression/46-apron2/48-iset_simple-polyhedra.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --enable ana.int.interval_set --set ana.apron.domain polyhedra +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf, adapted +#include + +void main(void) { + int X = 0; + int N = rand(); + if(N < 0 || N > 10000) { N = 0; } + + X = 2 * N; + + __goblint_check(X - 2 * N == 0); + __goblint_check(X == 2 * N); + + if(X == 2 * N) { + N = 8; + } else { + N = 42; //DEAD + } + +} diff --git a/tests/regression/46-apron2/49-iset_previously_problematic_b.c b/tests/regression/46-apron2/49-iset_previously_problematic_b.c new file mode 100644 index 0000000000..19d925013c --- /dev/null +++ b/tests/regression/46-apron2/49-iset_previously_problematic_b.c @@ -0,0 +1,51 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +typedef int wchar_t; +typedef unsigned long size_t; + +char *trim2(char const *s, int how) +{ + char *d; + char *tmp___4; + unsigned int state; + int tmp___9; + + if (tmp___9 == 0) { + tmp___9 = 1; + } + + size_t tmp___18; + + d = tmp___4; + + if (tmp___18 > 1UL) + { + if (how != 1) + { + state = 0U; + + while (1) + { + if (!tmp___9){ + } + else { + break; + } + + + state = 1U; + } + } + } + return (d); + +} + +int main(int argc, char const *argv[]) +{ + char *s; + trim2(s, 4); + + return 0; +} diff --git a/tests/regression/46-apron2/50-iset_branched-not-too-brutal-c.c b/tests/regression/46-apron2/50-iset_branched-not-too-brutal-c.c new file mode 100644 index 0000000000..fe2309a568 --- /dev/null +++ b/tests/regression/46-apron2/50-iset_branched-not-too-brutal-c.c @@ -0,0 +1,33 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval_set --enable ana.sv-comp.functions +extern int __VERIFIER_nondet_int(); + +#include +#include +int global = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) +{ + int top = __VERIFIER_nondet_int(); //rand + pthread_mutex_lock(&mutex); + if(top) { + global = 5; + } else { + global = 12; + } + global = 0; + pthread_mutex_unlock(&mutex); +} + +int main(void) +{ + pthread_t t; + pthread_create(&t, ((void *)0), t_fun, ((void *)0)); + + __goblint_check(global == 0); //UNKNOWN! + + pthread_mutex_lock(&mutex); + __goblint_check(global == 0); + pthread_mutex_unlock(&mutex); + return 0; +} diff --git a/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c new file mode 100644 index 0000000000..551bb731e9 --- /dev/null +++ b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int j = -1; + int digits = 0; + + int hour12; + int number_value; + + if (hour12 > 12) { + hour12 -= 12; + } + + digits = 0; + + while (j < 9) + { + number_value /= 10; + j++; + } + + return 0; +} diff --git a/tests/regression/46-apron2/52-iset_context.c b/tests/regression/46-apron2/52-iset_context.c new file mode 100644 index 0000000000..aa4b134649 --- /dev/null +++ b/tests/regression/46-apron2/52-iset_context.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval_set --enable ana.relation.context +extern int __VERIFIER_nondet_int(); + +#include + +int oct(int x, int y) { + int s; + if (x <= y) + s = 1; + else + s = 0; + return s; +} + +void main() { + int x = __VERIFIER_nondet_int(); //rand + int y = __VERIFIER_nondet_int(); //rand + int res; + if (x <= y) { + res = oct(x, y); + __goblint_check(res == 1); + } + + res = oct(x, y); +} diff --git a/tests/regression/46-apron2/53-iset_previously_problematic_h.c b/tests/regression/46-apron2/53-iset_previously_problematic_h.c new file mode 100644 index 0000000000..05489e47dc --- /dev/null +++ b/tests/regression/46-apron2/53-iset_previously_problematic_h.c @@ -0,0 +1,47 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron +// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// resurface, these tests without asserts are included +int main(int argc, char const *argv[]) +{ + int iter = 0; + int i = 0; + int j; + int t; + int nGroups; + int inUse16[16]; + int top; + + if (top) { + nGroups = 2; + } else { + nGroups = 6; + } + + while (iter < 4) + { + t = 0; + while (t < nGroups) + { + t++; + } + + iter++; + } + + i = 0; + while (i < 16) + { //TO-DO: here + inUse16[i] = 0; + + j = 0; + while (j < 16) + { + j++; + } + + i++; + } + + /* code */ + return 0; +} diff --git a/tests/regression/46-apron2/54-iset_octagon_simple.c b/tests/regression/46-apron2/54-iset_octagon_simple.c new file mode 100644 index 0000000000..adf07fdf53 --- /dev/null +++ b/tests/regression/46-apron2/54-iset_octagon_simple.c @@ -0,0 +1,24 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +#include + +void main(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); + __goblint_check(X == N); + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currently we can't detect this + N = 42; + } +} diff --git a/tests/regression/46-apron2/55-iset_array_octagon_keep_last_prec.c b/tests/regression/46-apron2/55-iset_array_octagon_keep_last_prec.c new file mode 100644 index 0000000000..5a318b6e6a --- /dev/null +++ b/tests/regression/46-apron2/55-iset_array_octagon_keep_last_prec.c @@ -0,0 +1,349 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --set ana.activated[+] apron --enable annotation.int.enabled --set ana.int.refinement fixpoint --set sem.int.signed_overflow assume_none +#include + +void main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2(void) __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example4a(void) __attribute__((goblint_precision("no-def_exc"))); +void example4b(void) __attribute__((goblint_precision("no-def_exc"))); +void example4c(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example8(void) __attribute__((goblint_precision("no-def_exc"))); +void mineEx1(void) __attribute__((goblint_precision("no-def_exc"))); + + +void main(void) { + example1(); + example2(); + example3(); + example4(); + example4a(); + example4b(); + example4c(); + example5(); + example6(); + example7(); + example8(); + mineEx1(); +} + +void example1(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 1; + j++; + } + + a[j] = 2; // a -> (j,([1,1],[2,2],[0,0])) + + if(top) { + z = j; + } else { + z = j-1; + } + + // Values that may be read are 1 or 2 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 2); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 0); +} + +void example2(void) { + int a[20]; + int i = 0; + int j = 0; + int top; + int z; + + // Necessary so we can not answer the queries below from the base domain + // and actually test the behavior of the octagons + int between1and8; + if(between1and8 < 1) { + between1and8 = 1; + } + + if(between1and8 > 8) { + between1and8 = 8; + } + + while(i < 20) { + a[i] = 0; + i++; + } + + while(j < between1and8) { + a[j] = 2; + j++; + } + + a[j] = 1; // a -> (j,([2,2],[1,1],[0,0])) + + if(top) { + z = j; + } else { + z = j+1; + } + + // Values that may be read are 1 or 0 + __goblint_check(a[z] == 1); //UNKNOWN + __goblint_check(a[z] == 0); //UNKNOWN + + // Relies on option sem.int.signed_overflow assume_none + __goblint_check(a[z] != 2); +} + +// Simple example (employing MustBeEqual) +void example3(void) { + int a[42]; + int i = 0; + int x; + + while(i < 42) { + a[i] = 0; + int v = i; + x = a[v]; + __goblint_check(x == 0); + i++; + } +} + +// Simple example (employing MayBeEqual / MayBeSmaller) +void example4(void) { + int a[42]; + int i = 0; + + while(i<=9) { + a[i] = 9; + int j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); // UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + int k = a[i-1]; + __goblint_check(k == 9); // UNKNOWN + + int l = a[0]; + __goblint_check(l == 9); // UNKNOWN + } + + i++; + } +} +// Just like the example before except that it tests correct behavior when variable order is reversed +void example4a(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = i+5; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); //UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); //UNKNOWN + } + + i++; + } +} + +// Just like the example before except that it tests correct behavior when operands for + are reversed +void example4b(void) { + int a[42]; + int j; + int i = 0; + + while(i<=9) { + a[i] = 9; + j = 5+i; + a[j] = 42; + + // Here we know a[i] is 9 when we have MayBeEqual + __goblint_check(a[i] == 9); //UNKNOWN + + // but only about the part to the left of i if we also have MayBeSmaller + if(i>0) { + __goblint_check(a[i-1] == 9); //UNKNOWN + } + + i++; + } +} + +// Like example before but iterating backwards +void example4c(void) { + int a[42]; + int j; + int i = 41; + + while(i > 8) { + a[i] = 7; + a[i-2] = 31; + + if(i < 41) { + __goblint_check(a[i+1] == 7); //UNKNOWN + } + + i--; + } +} + +void example5(void) { + int a[40]; + int i = 0; + + // This is a dirty cheat to get the array to be partitioned before entering the loop + // This is needed because the may be less of the octagons is not sophisticated enough yet. + // Once that is fixed this will also work without this line + a[i] = 0; + + while(i < 42) { + int j = i; + a[j] = 0; + i++; + + __goblint_check(a[i] == 0); //UNKNOWN + + __goblint_check(a[i-1] == 0); + __goblint_check(a[j] == 0); + + if (i>1) { + __goblint_check(a[i-2] == 0); + __goblint_check(a[j-1] == 0); + } + } +} + +void example6(void) { + int a[42]; + int i = 0; + int top; + + while(i<30) { + a[i] = 0; + i++; + + __goblint_check(a[top] == 0); //UNKNOWN + + int j=0; + while(j 10) { + if(top) { + j = i-5; + } else { + j = i-7; + } + + __goblint_check(a[j] == 0); + } + } +} + +void example8(void) { + int a[42]; + int i = 0; + int j = i; + + int N; + + if(N < 5) { + N = 5; + } + if(N > 40) { + N = 40; + } + + + while(i < N) { + a[i] = 0; + i++; + j = i; + a[j-1] = 0; + a[j] = 0; + j++; // Octagon knows -1 <= i-j <= -1 + i = j; // Without octagons, we lose partitioning here because we don't know how far the move has been + + __goblint_check(a[i-1] == 0); + __goblint_check(a[i-2] == 0); + } + + j = 0; + while(j < N) { + __goblint_check(a[j] == 0); //UNKNOWN + j++; + } +} + +// Example from https://www-apr.lip6.fr/~mine/publi/article-mine-HOSC06.pdf +void mineEx1(void) { + int X = 0; + int N = rand(); + if(N < 0) { N = 0; } + + while(X < N) { + X++; + } + + __goblint_check(X-N == 0); + // __goblint_check(X == N); // Currently not able to assert this because octagon doesn't handle it + + if(X == N) { + N = 8; + } else { + // is dead code but if that is detected or not depends on what we do in branch + // currenlty we can't detect this + N = 42; + } +} diff --git a/tests/regression/66-interval-set-one/00-was_problematic_2.c b/tests/regression/66-interval-set-one/00-was_problematic_2.c new file mode 100644 index 0000000000..e5b3938a76 --- /dev/null +++ b/tests/regression/66-interval-set-one/00-was_problematic_2.c @@ -0,0 +1,24 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +int main(void) +{ + int arr[260]; + int n; + + n = 5; + arr[0] = 0; + + while (n > 1) { //here + arr[1] = 7; + n--; + } + + int arr2[260]; + + n = 5; + arr2[259] = 0; + + while (n > 1) { + arr2[258] = 7; + n--; + } +} diff --git a/tests/regression/66-interval-set-one/01-dynamically-sized-array-oob-access.c b/tests/regression/66-interval-set-one/01-dynamically-sized-array-oob-access.c new file mode 100644 index 0000000000..90cd7727fc --- /dev/null +++ b/tests/regression/66-interval-set-one/01-dynamically-sized-array-oob-access.c @@ -0,0 +1,33 @@ +// PARAM: --enable ana.arrayoob --enable ana.int.interval_set --enable ana.int.interval_set --enable ana.int.enums + +// Variable sized array: oob access + +#include +#include +int main() { + int top; + int N; +// The if statement is needed, so the size is actually dynamic + if (top) { + N = 5; + } else { + N = 10; + } + int arr[N]; + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + arr[3] = 4; + arr[4] = 5; // NOWARN + arr[-1] = 10; // WARN + for (int i = 0; i < 5; ++i) { + arr[i] = 5; // NOWARN + } + for (int i = 0; i <= 5; ++i) { + arr[i] = 5; // WARN + } + for (int i = -2; i < 5; ++i) { + arr[i] = 5; // WARN + } + return 0; +} diff --git a/tests/regression/66-interval-set-one/02-continue.c b/tests/regression/66-interval-set-one/02-continue.c new file mode 100644 index 0000000000..1439c0a755 --- /dev/null +++ b/tests/regression/66-interval-set-one/02-continue.c @@ -0,0 +1,20 @@ +// PARAM: --enable ana.int.interval_set --set exp.unrolling-factor 5 --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 +// Simple example +#include + +void main(void) +{ + int j = 0; + + for(int i=0;i < 50;i++) { + if(i < 2) { + continue; + } + if(i>4) { + break; + } + j++; + } + + __goblint_check(j==3); +} diff --git a/tests/regression/66-interval-set-one/03-was_problematic_3.c b/tests/regression/66-interval-set-one/03-was_problematic_3.c new file mode 100644 index 0000000000..59ce6e5cb6 --- /dev/null +++ b/tests/regression/66-interval-set-one/03-was_problematic_3.c @@ -0,0 +1,52 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +struct some_struct +{ + int dir[7]; + int length; + double coeff; + double forwback; +}; + +struct some_struct q_paths[200]; +int num_q_paths; + +int add_basic_path(int length, double coeff) +{ + int ir[4]; + int j; + int flag; + + ir[2] = 0; + while (ir[2] < 2) + { + ir[3] = 0; + while (ir[3] < 2) + { + j = 0; + while (j < num_q_paths) + { + if (flag == 1) + { + break; + } + j++; + } + + q_paths[num_q_paths].length = length; + + num_q_paths++; + + (ir[3])++; + } + (ir[2])++; + } + + return 42; +} + +int main(int argc, char **argv) +{ + double this_coeff; + int pl; + add_basic_path(pl, this_coeff); +} diff --git a/tests/regression/66-interval-set-one/04-interval-overflow.c b/tests/regression/66-interval-set-one/04-interval-overflow.c new file mode 100644 index 0000000000..77ca6c5b59 --- /dev/null +++ b/tests/regression/66-interval-set-one/04-interval-overflow.c @@ -0,0 +1,44 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.congruence --disable ana.int.def_exc +// Overflow information should be passed from the interval domain to congruences +#include +#include + +int main(){ + signed char r; + + if (r) { + r = -68; + } else { + r = -63; + } + + signed char k = r - 80; + __goblint_check(k == 0); //UNKNOWN! + + signed char non_ov = r - 10; + __goblint_check(non_ov == -78); //UNKNOWN! + + signed char m = r * 2; + + __goblint_check(m == 0); //UNKNOWN! + + signed char l = r + (-80); + __goblint_check(l == 0); //UNKNOWN! + + int g; + + if (g) { + g = -126; + } else { + g = -128; + } + + signed char f = g / (-1); + __goblint_check(f == 1); //UNKNOWN! + + signed char d = -g; + __goblint_check(d == 1); //UNKNOWN! + + return 0; + +} diff --git a/tests/regression/66-interval-set-one/05-sync.c b/tests/regression/66-interval-set-one/05-sync.c new file mode 100644 index 0000000000..deea7768ff --- /dev/null +++ b/tests/regression/66-interval-set-one/05-sync.c @@ -0,0 +1,41 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[-] threadJoins +// Inspired by 36/87 +#include +#include +#include + +int g; +int h; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex); + __goblint_check(g==h); + pthread_mutex_unlock(&mutex); + return NULL; +} + + +int main(void) { + int top2; + + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&mutex); + if(top2) { + g=34; + h=77; + } + + g=0; + h=0; + pthread_mutex_unlock(&mutex); + + pthread_mutex_lock(&mutex); + __goblint_check(g==h); + pthread_mutex_unlock(&mutex); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/06-ints.c b/tests/regression/66-interval-set-one/06-ints.c new file mode 100644 index 0000000000..9c65d45ed7 --- /dev/null +++ b/tests/regression/66-interval-set-one/06-ints.c @@ -0,0 +1,194 @@ +// PARAM: --enable ana.int.interval_set --set sem.int.signed_overflow assume_wraparound +// With sem.int.signed_overflow set to assume_wraparound to assume two's complement representation for signed ints and don't go to top on every signed overflow. +#include + +#define RANGE(x, l, u) x >= l && x <= u + +int main() { + main2(); + + + int x, y; + if (x+1 == 2) { + __goblint_check(x == 1); + } else { + __goblint_check(x != 1); + } + if (5-x == 3) + __goblint_check(x == 2); + else + __goblint_check(x != 2); + if (5-x == 3 && x+y == x*3) + __goblint_check(x == 2 && y == 4); + if (x == 3 && y/x == 2) { + __goblint_check(y == 6); // UNKNOWN! + __goblint_check(RANGE(y, 6, 8)); + } + if (y/3 == -2) + __goblint_check(RANGE(y, -8, -6)); + if (y/-3 == -2) + __goblint_check(RANGE(y, 6, 8)); + if (y/x == 2 && x == 3) + __goblint_check(x == 3); // TO-DO y == [6,8]; this does not work because CIL transforms this into two if-statements + if (2+(3-x)*4/5 == 6 && 2*y >= x+5) + __goblint_check(RANGE(x, -3, -2) && y >= 1); // UNKNOWN + if (x > 1 && x < 5 && x % 2 == 1) // x = [2,4] && x % 2 = 1 => x = 3 + __goblint_check(x == 3); + + + long xl, yl, zl; + if (xl+1 == 2) { + __goblint_check(xl == 1); + } else { + __goblint_check(xl != 1); + } + if (5-xl == 3) + __goblint_check(xl == 2); + if (5-xl == 3 && xl+yl == xl*3) + __goblint_check(xl == 2 && yl == 4); + if (xl == 3 && yl/xl == 2) + // yl could for example also be 7 + __goblint_check(yl == 6); // UNKNOWN! + if (yl/xl == 2 && xl == 3) + __goblint_check(xl == 3); // TO-DO yl == 6 + if (2+(3-xl)*4/5 == 6 && 2*yl >= xl+4) + // xl could also be -3 + __goblint_check(xl == -2 && yl >= 1); //UNKNOWN! + if (xl > 1 && xl < 5 && xl % 2 == 1) { + __goblint_check(xl != 2); // [2,4] -> [3,4] TO-DO x % 2 == 1 + } + + + short xs, ys, zs; + if (xs+1 == 2) { + __goblint_check(xs == 1); + } else { + // Does not survive the casts inserted by CIL + // __goblint_check(xs != 1); + } + if (5-xs == 3) + __goblint_check(xs == 2); + if (5-xs == 3 && xs+ys == xs*3) + __goblint_check(xs == 2 && ys == 4); + if (xs == 3 && ys/xs == 2) { + // ys could for example also be 7 + __goblint_check(ys == 6); // UNKNOWN! + __goblint_check(RANGE(ys, 6, 8)); + } + if (ys/3 == -2) + __goblint_check(RANGE(ys, -8, -6)); + if (ys/-3 == -2) + __goblint_check(RANGE(ys, 6, 8)); + if (ys/xs == 2 && xs == 3) + __goblint_check(xs == 3); // TO-DO yl == 6 + if (2+(3-xs)*4/5 == 6 && 2*ys >= xs+5) { + // xs could also be -3 + __goblint_check(xs == -2 && ys >= 1); //UNKNOWN! + __goblint_check(RANGE(xs, -3, -2) && ys >= 1); // UNKNOWN + } + if (xs > 1 && xs < 5 && xs % 2 == 1) { + __goblint_check(xs != 2); + } + +} + +int main2() { + int one = 1; + int two = 2; + int three = 3; + int four = 4; + int five = 5; + int six = 6; + + int x, y, z; + if (x+one == two) { + __goblint_check(x == one); + } else { + __goblint_check(x != one); + } + if (five-x == three) + __goblint_check(x == two); + if (five-x == three && x+y == x*three) + __goblint_check(x == two && y == four); + if (x == three && y/x == two) { + // y could for example also be 7 + __goblint_check(y == six); // UNKNOWN! + __goblint_check(RANGE(y, 6, 8)); + } + if (y/x == two && x == three) + __goblint_check(x == three); // TO-DO y == six + if (two+(three-x)*four/five == six && two*y >= x+four) + // x could also be -three + __goblint_check(x == -two && y >= one); //UNKNOWN! + if (x > one && x < five && x % two == one) + __goblint_check(x != two); // [two,four] -> [three,four] TO-DO x % two == one + + if (y/three == -two) + __goblint_check(RANGE(y, -8, -6)); + if (y/-three == -two) + __goblint_check(RANGE(y, 6, 8)); + if (y/x == two && x == three) + __goblint_check(x == 3); // TO-DO y == [6,8]; this does not work because CIL transforms this into two if-statements + if (two+(three-x)*four/five == six && two*y >= x+five) + __goblint_check(RANGE(x, -3, -2) && y >= 1); // UNKNOWN + if (x > one && x < five && x % two == one) // x = [2,4] && x % 2 = 1 => x = 3 + __goblint_check(x != 2); // [3,4] TO-DO [3,3] + + + + long xl, yl, zl; + if (xl+one == two) { + __goblint_check(xl == one); + } else { + __goblint_check(xl != one); + } + if (five-xl == three) + __goblint_check(xl == two); + if (five-xl == three && xl+yl == xl*three) + __goblint_check(xl == two && yl == four); + if (xl == three && yl/xl == two) + // yl could for example also be 7 + __goblint_check(yl == six); // UNKNOWN! + if (yl/xl == two && xl == three) + __goblint_check(xl == three); // TO-DO yl == six + if (two+(three-xl)*four/five == six && two*yl >= xl+four) + // xl could also be -three + __goblint_check(xl == -two && yl >= one); //UNKNOWN! + if (xl > one && xl < five && xl % two == one) { + __goblint_check(xl != two); // [two,four] -> [three,four] TO-DO x % two == one + } + + + short xs, ys, zs; + if (xs+one == two) { + __goblint_check(xs == one); + } else { + // Does not survive the casts inserted by CIL + // __goblint_check(xs != one); + } + if (five-xs == three) + __goblint_check(xs == two); + if (five-xs == three && xs+ys == xs*three) + __goblint_check(xs == two && ys == four); + if (xs == three && ys/xs == two) { + // ys could for example also be 7 + __goblint_check(ys == six); // UNKNOWN! + __goblint_check(RANGE(ys, six, 8)); + } + if (ys/xs == two && xs == three) + __goblint_check(xs == three); // TO-DO yl == six + if (two+(three-xs)*four/five == six && two*ys >= xs+five) { + // xs could also be -three + __goblint_check(xs == -two && ys >= one); //UNKNOWN! + __goblint_check(RANGE(xs, -three, -two) && ys >= one); // UNKNOWN + } + if (xs > one && xs < five && xs % two == one) { + __goblint_check(xs != two); + } + if (ys/three == -two) + __goblint_check(RANGE(ys, -8, -6)); + if (ys/-three == -two) + __goblint_check(RANGE(ys, 6, 8)); + if (ys/xs == two && xs == three) + __goblint_check(xs == 3); // TO-DO y == [6,8]; this does not work because CIL transforms this into two if-statements +} diff --git a/tests/regression/66-interval-set-one/07-no-int-context-attribute.c b/tests/regression/66-interval-set-one/07-no-int-context-attribute.c new file mode 100644 index 0000000000..ed8666081c --- /dev/null +++ b/tests/regression/66-interval-set-one/07-no-int-context-attribute.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen --enable ana.base.context.int +#include + +int f(int x) __attribute__((goblint_context("base.no-int"))); // attributes are not permitted in a function definition +int f(int x) { + if (x) + return f(x+1); + else + return x; +} + +int main () { + int a = f(1); + __goblint_check(!a); + return 0; +} diff --git a/tests/regression/66-interval-set-one/08-base-priv-sync-prune.c b/tests/regression/66-interval-set-one/08-base-priv-sync-prune.c new file mode 100644 index 0000000000..1687659fd4 --- /dev/null +++ b/tests/regression/66-interval-set-one/08-base-priv-sync-prune.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.base.privatization write+lock --enable ana.int.interval_set --set witness.yaml.validate 04-base-priv-sync-prune.yml +// TODO: fix unsoundness in base priv syncs +#include + +int g = 0; + +void *t_fun(void *arg) { + ; // FAIL (witness) + return NULL; +} + +int main() { + int r, r2; // rand + + if (r) + g = 1; + + pthread_t id; + for (int i = 0; i < r2; r++) + pthread_create(&id, NULL, t_fun, NULL); + return 0; +} diff --git a/tests/regression/66-interval-set-one/09-intervals-large.c b/tests/regression/66-interval-set-one/09-intervals-large.c new file mode 100644 index 0000000000..0c98cdfef1 --- /dev/null +++ b/tests/regression/66-interval-set-one/09-intervals-large.c @@ -0,0 +1,26 @@ +//PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --disable ana.int.enums +#include + +int main(){ + int a = 0; + + // maximum value for ulonglong + unsigned long long x = 18446744073709551615ull; + if(x > 18446744073709551612ull){ + a = 1; + } + __goblint_check(a); + + unsigned long long y = x + 4; + __goblint_check(y == 3); + + // maximum value for long long + signed long long s = 9223372036854775807; + __goblint_check(s > 9223372036854775806); + + signed long long t = s + 2; + // Signed overflow -- The following assertion must be UNKNOWN! + __goblint_check(t == -9223372036854775807); // UNKNOWN! + + return 0; +} diff --git a/tests/regression/66-interval-set-one/10-calloc_struct.c b/tests/regression/66-interval-set-one/10-calloc_struct.c new file mode 100644 index 0000000000..a4981b7c20 --- /dev/null +++ b/tests/regression/66-interval-set-one/10-calloc_struct.c @@ -0,0 +1,42 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +typedef struct { + int x; + int y; +} data; + +data *d; + +int main(void) { + d = calloc(1,sizeof(data)); + d -> x = 0; + d -> y = 0; + + data e = {.x = 0, .y = 0}; + + __goblint_check(d->x == e.x); + __goblint_check(d->y == e.y); + + int a = d -> x; + int b = d -> y; + + __goblint_check(a != 3); + __goblint_check(b != 4); + + d -> x = 3; + d -> y = 4; + + data f = {.x = 3, .y = 3}; + + __goblint_check(d->x == f.x); //UNKNOWN + __goblint_check(d->y != f.y); + + a = d -> x; + b = d -> y; + + __goblint_check(a == 3); //UNKNOWN + __goblint_check(b == 4); //UNKNOWN +} diff --git a/tests/regression/66-interval-set-one/11-nesting_arrays.c b/tests/regression/66-interval-set-one/11-nesting_arrays.c new file mode 100644 index 0000000000..37b25dfe8a --- /dev/null +++ b/tests/regression/66-interval-set-one/11-nesting_arrays.c @@ -0,0 +1,211 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable annotation.int.enabled --set ana.int.refinement fixpoint +#include + +struct kala { + int i; + int a[5]; +}; + +struct kalaw { + int* a; +}; + +struct kass { + int v; +}; + +union uArray { + int a[5]; + int b[5]; +}; + +union uStruct { + int b; + struct kala k; +}; + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1() __attribute__((goblint_precision("no-def_exc"))); +void example2() __attribute__((goblint_precision("no-def_exc"))); +void example3() __attribute__((goblint_precision("no-def_exc"))); +void example4() __attribute__((goblint_precision("no-def_exc"))); +void example5() __attribute__((goblint_precision("no-def_exc"))); +void example6() __attribute__((goblint_precision("no-def_exc"))); +void example7() __attribute__((goblint_precision("no-def_exc"))); +void example8() __attribute__((goblint_precision("no-def_exc"))); + + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + return 0; +} + +void example1() { + struct kala l; + int i = 0; + int top; + + while (i < 5) { + l.a[i] = 42; + i++; + + // Check assertion that should only hold later does not already hold here + __goblint_check(l.a[4] == 42); //UNKNOWN + } + + // Check the array is correctly initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); + + // Destructively assign to i + i = top; + + // Check the array is still known to be completly initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); +} + +void example2() { + struct kala kalas[5]; + + int i2 = 0; + + while (i2 < 4) { + int j2 = 0; + while (j2 < 5) { + kalas[i2].a[j2] = 8; + j2++; + } + i2++; + } + + // Initialization has not proceeded this far + __goblint_check(kalas[4].a[0] == 8); //UNKNOWN + __goblint_check(kalas[0].a[0] == 8); +} + +void example3() { + struct kala xnn; + for(int l=0; l < 5; l++) { + xnn.a[l] = 42; + } + + __goblint_check(xnn.a[3] == 42); +} + +void example4() { + struct kala xn; + + struct kala xs[5]; + + for(int j=0; j < 4; j++) { + xs[j] = xn; + for(int k=0; k < 5; k++) { + xs[j].a[k] = 7; + } + } + + __goblint_check(xs[3].a[0] == 7); +} + +void example5() { + // This example is a bit contrived to show that splitting and moving works for + // unions + union uArray ua; + int i3 = 0; + int top; + int *i = ⊤ + + ua.a[*i] = 1; + + while (i3 < 5) { + ua.a[i3] = 42; + i3++; + } + + __goblint_check(ua.a[i3 - 1] == 42); + + ua.b[0] = 3; + __goblint_check(ua.b[0] == 3); + + // ------------------------------- + union uStruct us; + int i4 = 0; + + us.b = 4; + us.k.a[i4] = 0; + __goblint_check(us.b == 4); // UNKNOWN + __goblint_check(us.k.a[0] == 0); + __goblint_check(us.k.a[3] == 0); // UNKNOWN + + while (i4 < 5) { + us.k.a[i4] = 42; + i4++; + } + + __goblint_check(us.k.a[1] == 42); + __goblint_check(us.k.a[0] == 0); // FAIL +} + +void example6() { + int a[42]; + int i = 0; + + struct kass k; + k.v = 7; + + while(i < 42) { + a[i] = 0; + i++; + } + + i = 0; + + a[k.v] = 2; + k.v = k.v+1; + + __goblint_check(a[k.v] != 3); +} + +void example7() { + // Has no asserts, just checks this doesn't cause an infinite loop + int a[42]; + int i = 0; + + while(i < 40) { + a[i] = 0; + i++; + } + + a[a[0]] = 2; +} + +// Test correct behavior with more involved expression in subscript operator +void example8() { + int a[42]; + union uArray ua; + + ua.a[0] = 0; + ua.a[1] = 0; + ua.a[2] = 0; + ua.a[3] = 0; + ua.a[4] = 0; + + int i = 0; + int *ip = &i; + + a[ua.a[*ip]] = 42; + ip++; + __goblint_check(a[ua.a[*ip]] == 42); //UNKNOWN +} diff --git a/tests/regression/66-interval-set-one/12-tid-toy10.c b/tests/regression/66-interval-set-one/12-tid-toy10.c new file mode 100644 index 0000000000..26bfb6e363 --- /dev/null +++ b/tests/regression/66-interval-set-one/12-tid-toy10.c @@ -0,0 +1,48 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins +// Inspired by 36/80 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_benign(void *arg) { + pthread_mutex_lock(&A); + g = 10; + h = 10; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t = 10; + + // Force multi-threaded handling + pthread_t id2; + pthread_create(&id2, NULL, t_benign, NULL); + + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + pthread_join(id2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); //UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + int t = 9; + + pthread_mutex_lock(&A); + __goblint_check(g == h); //FAIL + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/13-glob_interval.c b/tests/regression/66-interval-set-one/13-glob_interval.c new file mode 100644 index 0000000000..40a23e93de --- /dev/null +++ b/tests/regression/66-interval-set-one/13-glob_interval.c @@ -0,0 +1,36 @@ +// PARAM: --set ana.int.interval_set true +#include +#include + +int glob = 0; +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mtx); + glob = 999; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + int i = 3; + pthread_t id; + + __goblint_check(glob == 0); + + // Create the thread + pthread_create(&id, NULL, t_fun, NULL); + + // Simple assignments to only locals + __goblint_check(i == 3); + i = 9; + __goblint_check(i == 9); + + glob = 10; + + i = glob; + __goblint_check(i >= 0); + __goblint_check(i > 100); // UNKNOWN + + return 0; +} diff --git a/tests/regression/66-interval-set-one/14-no-int-context.c b/tests/regression/66-interval-set-one/14-no-int-context.c new file mode 100644 index 0000000000..69d2aa2aa7 --- /dev/null +++ b/tests/regression/66-interval-set-one/14-no-int-context.c @@ -0,0 +1,12 @@ +// PARAM: --enable ana.int.interval_set --set solver slr3t --disable ana.base.context.int + +int f (int i) { // -2 + return i+1; } // -3 +void g(int j) { // -4 + f(j); } // -5 +int main(){ + int x; + x = f(1); // -1 + g(x); // 0 + return 0; +} diff --git a/tests/regression/66-interval-set-one/16-simple.c b/tests/regression/66-interval-set-one/16-simple.c new file mode 100644 index 0000000000..e84e727ba7 --- /dev/null +++ b/tests/regression/66-interval-set-one/16-simple.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval_set --set exp.unrolling-factor 5 --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 +// Simple example +#include + +void main(void) +{ + int a[5]; + int i = 0; + + while (i < 5) { + a[i] = i; + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 3); +} diff --git a/tests/regression/66-interval-set-one/17-hybrid.c b/tests/regression/66-interval-set-one/17-hybrid.c new file mode 100644 index 0000000000..eaad9c41c1 --- /dev/null +++ b/tests/regression/66-interval-set-one/17-hybrid.c @@ -0,0 +1,20 @@ +// PARAM: --enable ana.int.interval_set --set solver td3 --enable solvers.td3.restart.wpoint.enabled --disable solvers.td3.restart.wpoint.once --set sem.int.signed_overflow assume_none +// ALSO: --enable ana.int.interval_set --set solver sl4 --set sem.int.signed_overflow assume_none +// Example from Amato-Scozzari, SAS 2013, based on Halbwachs-Henry, SAS 2012. +// Localized narrowing with restart policy should be able to prove that +// 0 <= i <= 10 inside the inner loop. +#include + +void main() +{ + int i = 0; + while (1) { + i++; + for (int j=0; j < 10; j++) { + __goblint_check(0 <= i); + __goblint_check(i <= 10); + } + if (i>9) i=0; + } + return; +} diff --git a/tests/regression/66-interval-set-one/19-simple_array.c b/tests/regression/66-interval-set-one/19-simple_array.c new file mode 100644 index 0000000000..d5e1ae785b --- /dev/null +++ b/tests/regression/66-interval-set-one/19-simple_array.c @@ -0,0 +1,188 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --enable annotation.int.enabled --set ana.int.refinement fixpoint +// skipped because https://github.com/goblint/analyzer/issues/468 +#include + +int global; + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2(void) __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example7(void) __attribute__((goblint_precision("no-def_exc"))); +void example8() __attribute__((goblint_precision("no-def_exc"))); + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[42]; + int i = 0; + int top; + + while (i < 42) { + a[i] = 0; + __goblint_check(a[i] == 0); + __goblint_check(a[0] == 0); + __goblint_check(a[17] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// More complicated expression to index rather than just a variable +void example2(void) { + int a[42]; + int i = 1; + + while (i < 43) { + a[i - 1] = 0; + __goblint_check(a[i - 1] == 0); + __goblint_check(a[38] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// Two values initialized in one loop +void example3(void) { + int a[42]; + int i = 0; + + while (i < 42) { + a[i] = 0; + i++; + a[i] = 1; + i++; + } + + __goblint_check(a[0] == 2); // FAIL + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[41] == 1); // UNKNOWN + __goblint_check(a[41] == -1); // FAIL +} + +// Example where initialization proceeds backwards +void example4(void) { + int a[42]; + int i = 41; + + while(i >= 12) { + a[i] = 0; + i--; + } + + __goblint_check(a[i+2] == 0); + __goblint_check(a[41] == 0); + __goblint_check(a[i] == 0); //UNKNOWN + __goblint_check(a[0] == 0); //UNKNOWN +} + +// Example having two arrays partitioned according to one expression +void example5(void) { + int a[42]; + int b[42]; + int i = 0; + + while(i < 42) { + a[i] = 2; + b[41-i] = 0; + + __goblint_check(b[7] == 0); //UNKNOWN + __goblint_check(a[5] == 2); //UNKNOWN + i++; + } + + __goblint_check(a[0] == 2); + __goblint_check(a[41] == 2); + __goblint_check(b[0] == 0); + __goblint_check(b[41] == 0); +} + +// Example showing array becoming partitioned according to different expressions +void example6(void) { + int a[42]; + int i = 0; + int j = 0; + int top; + + while(i < 42) { + a[i] = 4; + i++; + } + + __goblint_check(a[17] == 4); + __goblint_check(a[9] == 4); + __goblint_check(a[3] == 4); + __goblint_check(a[i-1] == 4); + + while(j<10) { + a[j] = -1; + j++; + } + + __goblint_check(a[3] == -1); + __goblint_check(a[0] == -1); + __goblint_check(a[j-1] == -1); + __goblint_check(a[j] == 4); + __goblint_check(a[17] == 4); + __goblint_check(a[j+5] == 4); +} + +// This was the case where we thought we needed path-splitting +void example7(void) { + int a[42]; + int i = 0; + int top; + + if(top) { + while(i < 41) { + a[i] = 0; + __goblint_check(a[i] == 0); + i++; + } + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[7] == 0); // UNKNOWN + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[top] == 0); // UNKNOWN +} + +// Check that the global variable is not used for partitioning +void example8() { + int a[10]; + + a[global] = 4; + __goblint_check(a[global] == 4); // UNKNOWN + + for(int i=0; i <5; i++) { + a[i] = 42; + } + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + __goblint_check(a[2] == 42); + __goblint_check(a[3] == 42); + __goblint_check(a[global] == 42); +} diff --git a/tests/regression/66-interval-set-one/20-slr-glob_interval.c b/tests/regression/66-interval-set-one/20-slr-glob_interval.c new file mode 100644 index 0000000000..85c90c7dc2 --- /dev/null +++ b/tests/regression/66-interval-set-one/20-slr-glob_interval.c @@ -0,0 +1,37 @@ +// PARAM: --set ana.int.interval_set true --set solver new +// https://github.com/goblint/analyzer/pull/805#discussion_r933232518 +#include +#include + +int glob = 0; +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mtx); + glob = 999; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + int i = 3; + pthread_t id; + + __goblint_check(glob == 0); + + // Create the thread + pthread_create(&id, NULL, t_fun, NULL); + + // Simple assignments to only locals + __goblint_check(i == 3); + i = 9; + __goblint_check(i == 9); + + glob = 10; + + i = glob; + __goblint_check(i >= 0); + __goblint_check(i > 100); // UNKNOWN + + return 0; +} diff --git a/tests/regression/66-interval-set-one/21-strange-ulong.c b/tests/regression/66-interval-set-one/21-strange-ulong.c new file mode 100644 index 0000000000..31e561d3f7 --- /dev/null +++ b/tests/regression/66-interval-set-one/21-strange-ulong.c @@ -0,0 +1,88 @@ +// PARAM: --enable ana.int.interval_set --set ana.int.refinement once +#include + +int main(); + +int withint() { + int i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} + +int withuint() { + unsigned int i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} + +int withlong() { + long i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} + +int withlonglong() { + long long i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} + +int withulonglong() { + unsigned long long i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} + +int main() { + withint(); + withuint(); + withlong(); + withlonglong(); + withulonglong(); + + unsigned long i = 0; + void* bla; + + while(i < 10000) { + i++; + bla = &main; + } + + __goblint_check(1); // reachable + return 0; +} diff --git a/tests/regression/66-interval-set-one/22-calloc_globmt.c b/tests/regression/66-interval-set-one/22-calloc_globmt.c new file mode 100644 index 0000000000..a86b04dacd --- /dev/null +++ b/tests/regression/66-interval-set-one/22-calloc_globmt.c @@ -0,0 +1,32 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned +#include +#include +#include + +int *x; +int *y; + +void *t_fun(void *arg) { + *x = 3; + return NULL; +} + +int main() { + pthread_t id; + + x = calloc(1, sizeof(int)); + y = calloc(1, sizeof(int)); + + *x = 0; + *y = 1; + + __goblint_check(*x == 0); + __goblint_check(*y == 1); // UNKNOWN + + pthread_create(&id, NULL, t_fun, NULL); + + __goblint_check(*x == 0); // UNKNOWN + __goblint_check(*y == 1); // UNKNOWN + + return 0; +} diff --git a/tests/regression/66-interval-set-one/23-publish-regression.c b/tests/regression/66-interval-set-one/23-publish-regression.c new file mode 100644 index 0000000000..914cbf21e0 --- /dev/null +++ b/tests/regression/66-interval-set-one/23-publish-regression.c @@ -0,0 +1,36 @@ +// PARAM: --set ana.int.interval_set true + +#include +#include + +int glob1 = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + +// The question is how to compute these S[g] sets? +// They are given in the paper. Should it be as large as possible? + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + pthread_mutex_lock(&mutex2); + glob1 = 5; + pthread_mutex_unlock(&mutex2); + // But if s[g] = {mutex1,mutex2}, we publish here. + pthread_mutex_lock(&mutex2); + __goblint_check(glob1 == 5); + glob1 = 0; + pthread_mutex_unlock(&mutex1); + pthread_mutex_unlock(&mutex2); + return NULL; +} + +int main(void) { + pthread_t id; + __goblint_check(glob1 == 0); + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex1); + __goblint_check(glob1 == 0); + pthread_mutex_unlock(&mutex1); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/66-interval-set-one/25-simple_array.c b/tests/regression/66-interval-set-one/25-simple_array.c new file mode 100644 index 0000000000..85933d2310 --- /dev/null +++ b/tests/regression/66-interval-set-one/25-simple_array.c @@ -0,0 +1,209 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int global; + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[42]; + int i = 0; + int top; + + while (i < 42) { + a[i] = 0; + __goblint_check(a[i] == 0); + __goblint_check(a[0] == 0); + __goblint_check(a[17] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// More complicated expression to index rather than just a variable +void example2(void) { + int a[42]; + int i = 1; + + while (i < 43) { + a[i - 1] = 0; + __goblint_check(a[i - 1] == 0); + __goblint_check(a[38] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// Two values initialized in one loop +void example3(void) { + int a[42]; + int i = 0; + + while (i < 42) { + a[i] = 0; + i++; + a[i] = 1; + i++; + } + + __goblint_check(a[0] == 2); // FAIL + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[41] == 1); // UNKNOWN + __goblint_check(a[41] == -1); // FAIL +} + +// Example where initialization proceeds backwards +void example4(void) { + int a[42]; + int i = 41; + + while(i >= 12) { + a[i] = 0; + i--; + } + + __goblint_check(a[i+2] == 0); + __goblint_check(a[41] == 0); + __goblint_check(a[i] == 0); //UNKNOWN + __goblint_check(a[0] == 0); //UNKNOWN +} + +// Example having two arrays partitioned according to one expression +void example5(void) { + int a[42]; + int b[42]; + int i = 0; + + while(i < 42) { + a[i] = 2; + b[41-i] = 0; + + __goblint_check(b[7] == 0); //UNKNOWN + __goblint_check(a[5] == 2); //UNKNOWN + i++; + } + + __goblint_check(a[0] == 2); + __goblint_check(a[41] == 2); + __goblint_check(b[0] == 0); + __goblint_check(b[41] == 0); +} + +// Example showing array becoming partitioned according to different expressions +void example6(void) { + int a[42]; + int i = 0; + int j = 0; + int top; + + while(i < 42) { + a[i] = 4; + i++; + } + + __goblint_check(a[17] == 4); + __goblint_check(a[9] == 4); + __goblint_check(a[3] == 4); + __goblint_check(a[i-1] == 4); + + while(j<10) { + a[j] = -1; + j++; + } + + __goblint_check(a[3] == -1); + __goblint_check(a[0] == -1); + __goblint_check(a[j-1] == -1); + __goblint_check(a[j] == 4); + __goblint_check(a[17] == 4); + __goblint_check(a[j+5] == 4); +} + +// This was the case where we thought we needed path-splitting +void example7(void) { + int a[42]; + int i = 0; + int top; + + if(top) { + while(i < 41) { + a[i] = 0; + __goblint_check(a[i] == 0); + i++; + } + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[7] == 0); // UNKNOWN + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[top] == 0); // UNKNOWN +} + +// Check that the global variable is not used for partitioning +void example8() { + int a[10]; + + a[global] = 4; + __goblint_check(a[global] == 4); // UNKNOWN + + for(int i=0; i <5; i++) { + a[i] = 42; + } + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + __goblint_check(a[2] == 42); + __goblint_check(a[3] == 42); + __goblint_check(a[global] == 42); +} + +// Check that arrays of types different from int are handeled correctly +void example9() { + // no char because char has unknown signedness (particularly, unsigned on arm64) + signed char a[10]; + int n; + __goblint_check(a[3] == 800); // FAIL + + for(int i=0;i < 10; i++) { + a[i] = 7; + } + + __goblint_check(a[0] == 7); + __goblint_check(a[3] == 7); + + a[3] = (signed char) n; + __goblint_check(a[3] == 800); //FAIL + __goblint_check(a[3] == 127); //UNKNOWN + __goblint_check(a[3] == -128); //UNKNOWN + __goblint_check(a[3] == -129); //FAIL +} + +void example10() { + int a[20]; + a[5] = 3; + + int i=5; + a[i] = 7; + __goblint_check(a[5] == 7); +} diff --git a/tests/regression/66-interval-set-one/26-float.c b/tests/regression/66-interval-set-one/26-float.c new file mode 100644 index 0000000000..d0c1cac8d7 --- /dev/null +++ b/tests/regression/66-interval-set-one/26-float.c @@ -0,0 +1,26 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc --enable ana.sv-comp.functions --set ana.activated[+] var_eq --set ana.activated[+] region +#include + +int isNan(float arg) { + float x; + return arg != arg; +} + +int main(){ + struct blub { float f; } s; + float fs[3]; + + float top; + // float may be NaN here, therefore the comaprison should be unknown + __goblint_check(top == top); //UNKNOWN! + __goblint_check(s.f == s.f); //UNKNOWN! + __goblint_check(fs[1] == fs[1]); //UNKNOWN! + + int r = isNan(top); + + if(r) { + __goblint_check(1); + } else { + __goblint_check(1); + } + } diff --git a/tests/regression/66-interval-set-one/27-calloc_int.c b/tests/regression/66-interval-set-one/27-calloc_int.c new file mode 100644 index 0000000000..826c770bc2 --- /dev/null +++ b/tests/regression/66-interval-set-one/27-calloc_int.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned +#include +#include + +int main(void) { + int *r = calloc(1,sizeof(int)); + + r[0] = 0; + + __goblint_check(r[0] != 5); + __goblint_check(r[0] == 0); + + r[0] = 5; + + __goblint_check(r[0] == 5); //UNKNOWN + __goblint_check(r[0] != 0); //UNKNOWN + __goblint_check(r[0] != -10); + __goblint_check(r[0] != 100); +} diff --git a/tests/regression/66-interval-set-one/28-performance.c b/tests/regression/66-interval-set-one/28-performance.c new file mode 100644 index 0000000000..e1f6312421 --- /dev/null +++ b/tests/regression/66-interval-set-one/28-performance.c @@ -0,0 +1,11 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable exp.fast_global_inits +// Without fast_global_inits this takes >150s, when it is enabled < 0.1s +#include + +int global_array[50][500][20]; + +int main(void) { + for(int i =0; i < 50; i++) { + __goblint_check(global_array[i][42][7] == 0); + } +} diff --git a/tests/regression/66-interval-set-one/29-global_array.c b/tests/regression/66-interval-set-one/29-global_array.c new file mode 100644 index 0000000000..6c92f6915f --- /dev/null +++ b/tests/regression/66-interval-set-one/29-global_array.c @@ -0,0 +1,24 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int global_array[50]; + +int main(void) { + some_func(); + + int x = global_array[5]; + __goblint_check(x == 0); //UNKNOWN + __goblint_check(x == 42); //UNKNOWN +} + + +void some_func(void) { + global_array[0] = 5; + + for(int i=1; i < 50; i++) { + global_array[i] = 42; + } + + int x = global_array[0]; + __goblint_check(x == 42); //FAIL +} diff --git a/tests/regression/66-interval-set-one/31-interval-arith.c b/tests/regression/66-interval-set-one/31-interval-arith.c new file mode 100644 index 0000000000..1a3a1dc14a --- /dev/null +++ b/tests/regression/66-interval-set-one/31-interval-arith.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --disable ana.int.enums +#include +#include + +int main(){ + unsigned int i = 3; + + // 3 * 2^30 == 3221225472u is outside of the range that Intervall32 can represent + // Therefore, when trying to refine i, Base.invariant meets i -> [3;3] with i -> [(-2^31) / 2^30; ((2^31)-1) / 2^30] = [-2; 1] + // We thus get i -> Bottom, and the code after the condition is considered unreachable + if(i * 1073741824u == 3221225472u){ + printf("%u\n", i); + __goblint_check(i == 3); // SUCCESS + } + __goblint_check(i == 3); // SUCCESS + return 0; +} diff --git a/tests/regression/66-interval-set-one/33-was_problematic.c b/tests/regression/66-interval-set-one/33-was_problematic.c new file mode 100644 index 0000000000..f2c042ff40 --- /dev/null +++ b/tests/regression/66-interval-set-one/33-was_problematic.c @@ -0,0 +1,34 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int main(int argc, char **argv) +{ + int unLo; + int sp = 1; + int nextD[3]; + + // nextD[0] = 2; // When we have this and the one in line 25 it is fine + + int x; + + while (sp > 0) + { + sp--; + + while (1) + { + if (x+1 <= 100 || x+1 > 100) + { + break; + } + } + + // If we have only this one there is a problem + nextD[0] = 2; + + int y = 27; + } + + __goblint_check(1 == 1); // Was reported as unreachable before + return 0; +} diff --git a/tests/regression/66-interval-set-one/34-calloc_glob.c b/tests/regression/66-interval-set-one/34-calloc_glob.c new file mode 100644 index 0000000000..4f8cb99efb --- /dev/null +++ b/tests/regression/66-interval-set-one/34-calloc_glob.c @@ -0,0 +1,27 @@ +// Made after 02 22 + +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +int *x; +int *y; + +int main() { + int *p; + x = calloc(1, sizeof(int)); + y = calloc(1, sizeof(int)); + + *x = 0; + *y = 1; + + __goblint_check(*x == 0); + __goblint_check(*y == 1); //UNKNOWN + + p = x; x = y; y = p; + __goblint_check(*x == 1); //UNKNOWN + __goblint_check(*y == 0); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/36-one_by_one.c b/tests/regression/66-interval-set-one/36-one_by_one.c new file mode 100644 index 0000000000..78f3ec2fa0 --- /dev/null +++ b/tests/regression/66-interval-set-one/36-one_by_one.c @@ -0,0 +1,28 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int main(void) { + int a[4]; + int b[4]; + + a[0] = 42; + a[1] = 42; + a[2] = 42; + a[3] = 42; + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + __goblint_check(a[2] == 42); + __goblint_check(a[3] == 42); + + int *ptr = &b; + *ptr = 1; ptr++; + *ptr = 1; ptr++; + *ptr = 1; ptr++; + *ptr = 1; ptr++; + + __goblint_check(b[0] == 1); + __goblint_check(b[1] == 1); + __goblint_check(b[2] == 1); + __goblint_check(b[3] == 1); +} diff --git a/tests/regression/66-interval-set-one/37-on-attribute.c b/tests/regression/66-interval-set-one/37-on-attribute.c new file mode 100644 index 0000000000..300ed32561 --- /dev/null +++ b/tests/regression/66-interval-set-one/37-on-attribute.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen +#include + +int f(int x) __attribute__((goblint_context("widen"))); // attributes are not permitted in a function definition +int f(int x) { + if (x) + return f(x+1); + else + return x; +} + +int main () { + int a = f(1); + __goblint_check(!a); + return 0; +} diff --git a/tests/regression/66-interval-set-one/38-interval-congruence.c b/tests/regression/66-interval-set-one/38-interval-congruence.c new file mode 100644 index 0000000000..da8606dea1 --- /dev/null +++ b/tests/regression/66-interval-set-one/38-interval-congruence.c @@ -0,0 +1,34 @@ +// PARAM: --disable ana.int.def_exc --enable ana.int.interval_set --disable ana.int.congruence --set ana.int.refinement fixpoint +#include + +int main(){ + int r; + r=r; + + if (r) { + r = 2; + } else { + r = 7; + } + + // At this point r in the congr. dom should be 2 + 5Z + int k = r; + if (k >= 3) { + + // After refinement with congruences, the lower bound should be 7 as the numbers 3 - 6 are not in the congr. class + __goblint_check(k < 7); // FAIL + } + + int l; + if (l) { + l = 37; + } else { + l = 42; + } + + if (l <= 41) { + // Similarly to before, the upper bound should be 37 now. + __goblint_check(l > 37); // FAIL + } + return 0; +} diff --git a/tests/regression/66-interval-set-one/39-calls.c b/tests/regression/66-interval-set-one/39-calls.c new file mode 100644 index 0000000000..67ff46ad77 --- /dev/null +++ b/tests/regression/66-interval-set-one/39-calls.c @@ -0,0 +1,55 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +// Variable-sized arrays +void foo(int n, int a[n]); +void foo2(int n, int a[30][n]); +void foo3(int n, int a[n][30]); + +int main(void) +{ + int a[40]; + foo(40, a); + + int n = 30; + int b[n][n]; + b[29][0] = 0; + foo2(30, b); + foo3(30, b); +} + +int somefunction() { + return 42; +} + +//Two variable-sized arrays +//In CIL, a is changed to a pointer, and b is left alone +void foo(int n, int a[n]) { + + double b[n]; + a[n-1] = 0; + b[n-1] = 0.0; + printf("sizeof(a) = %d, sizeof(b) = %d\n", sizeof(a), sizeof(b)); + + + int m = 78; + char boom[n][somefunction()]; + char boom2[somefunction()][n]; + char boom3[somefunction()][somefunction()]; + char boom4[somefunction()][17][somefunction()][m]; + + //formals should be promoted to pointers (int*, in this case) + int* p = a; + p++; + if (sizeof(a) != sizeof(p)) E(2); + + //locals should keep their array type. CIL rewrites sizeof(b) + // as (n * sizeof(*b)) + if (sizeof(b) != (n * sizeof(double))) E(3); +} + +void foo2(int n, int a[30][n]) { + if(a[29][0] != 0) E(4); +} + +void foo3(int n, int a[n][30]) { + if(a[29][0] != 0) E(4); +} diff --git a/tests/regression/66-interval-set-one/40-priv_interval.c b/tests/regression/66-interval-set-one/40-priv_interval.c new file mode 100644 index 0000000000..047e04875a --- /dev/null +++ b/tests/regression/66-interval-set-one/40-priv_interval.c @@ -0,0 +1,32 @@ +// PARAM: --set ana.int.interval_set true +#include +#include + +int glob1 = 5; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int t; + pthread_mutex_lock(&mutex1); + t = glob1; + __goblint_check(t == 5); + glob1 = -10; + __goblint_check(glob1 == -10); + glob1 = t; + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + __goblint_check(glob1 == 5); + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex1); + glob1++; + __goblint_check(glob1 == 6); + glob1--; + pthread_mutex_unlock(&mutex1); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/66-interval-set-one/44-calloc_zero_init.c b/tests/regression/66-interval-set-one/44-calloc_zero_init.c new file mode 100644 index 0000000000..004387a7b4 --- /dev/null +++ b/tests/regression/66-interval-set-one/44-calloc_zero_init.c @@ -0,0 +1,13 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +int main(void) { + int *ro = calloc(2,sizeof(int)); + __goblint_check(ro[0] == 0); + __goblint_check(ro[1] == 0); + + ro[0] = 3; + __goblint_check(ro[1] != 3); //UNKNOWN +} diff --git a/tests/regression/66-interval-set-one/46-calloc_matrix.c b/tests/regression/66-interval-set-one/46-calloc_matrix.c new file mode 100644 index 0000000000..8b94b3116a --- /dev/null +++ b/tests/regression/66-interval-set-one/46-calloc_matrix.c @@ -0,0 +1,12 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +int main(void) { + int (*r)[5] = calloc(2, sizeof(int[5])); + r[0][1] = 3; + int* z = &r[0][1]; + + __goblint_check(*z == 3); //UNKNOWN +} diff --git a/tests/regression/66-interval-set-one/47-only-intervals.c b/tests/regression/66-interval-set-one/47-only-intervals.c new file mode 100644 index 0000000000..952aa95e89 --- /dev/null +++ b/tests/regression/66-interval-set-one/47-only-intervals.c @@ -0,0 +1,9 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc +#include + +int main() { + for(int i=2; i < 42; i++) { + int x = i==2; // NOWARN + __goblint_check(1); + } +} diff --git a/tests/regression/66-interval-set-one/48-tid-toy12.c b/tests/regression/66-interval-set-one/48-tid-toy12.c new file mode 100644 index 0000000000..4da779c541 --- /dev/null +++ b/tests/regression/66-interval-set-one/48-tid-toy12.c @@ -0,0 +1,80 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins +// Inspired by 36/82 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +pthread_t other_t; + +void *t_fun(void *arg) { + int x = 8; + int y; + + pthread_mutex_lock(&A); + g = x; + h = y; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = x; + h = x; + pthread_mutex_unlock(&A); + return NULL; +} + +void *t_benign(void *arg) { + // Without this, it would even succeed without the must joined analysis. + // With it, that is required! + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_create(&other_t, NULL, t_fun, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = 10; + h = 10; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t; + + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + // Force multi-threaded handling + pthread_t id2; + pthread_t id3; + pthread_create(&id2, NULL, t_benign, NULL); + pthread_create(&id3, NULL, t_benign, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); //UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_join(id2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); // UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_join(other_t, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == h); // UNKNOWN! + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/49-simple-cases-unrolled.c b/tests/regression/66-interval-set-one/49-simple-cases-unrolled.c new file mode 100644 index 0000000000..bd4b7c8d06 --- /dev/null +++ b/tests/regression/66-interval-set-one/49-simple-cases-unrolled.c @@ -0,0 +1,196 @@ +// PARAM: --enable ana.int.interval_set --set exp.unrolling-factor 5 --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 +#include + +int global; + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[5]; + int i = 0; + + while (i < 5) { + a[i] = i; + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 3); +} + +// Do-while loop simple example +void example2(void) +{ + int a[5]; + int i = 0; + + do { + a[i] = i; + i++; + } while (i<=5); + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 3); +} + +// Initialization not completed, yet the array representation is not precise +void example3(void) +{ + int a[10]; + int i = 0; + + while (i < 5) { + a[i] = i; + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 0); // FAIL + __goblint_check(a[7] == 0); // UNKNOWN +} + +// Example with increased precision. Goblint detects in which iteration it is during the unrolled part. +void example4(void) +{ + int a[10]; + int i = 0; + int first_iteration = 1; + + while (i < 10) { + if (first_iteration == 1) __goblint_check(i==0); + else if (i > 5) __goblint_check(i == 6); // UNKNOWN + first_iteration = 0; + a[i] = 0; + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(first_iteration == 0); +} + + +// Example where the precision increase can be appreciated by a variable that +// is modified in the loop other than the ones used in the loop head +void example5(void) +{ + int a[4]; + int i = 0; + int top = 0; + + while (i < 4) { + a[i] = 0; + top += i; + if(i==2){ + __goblint_check(top == 3); + } + else{ + __goblint_check(top == 3); // FAIL + } + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 0); + __goblint_check(top == 6); +} + +// Loop has less iterations than the unrolling factor +void example6(void) +{ + int a[5]; + int i = 0; + int top = 0; + + while (i < 3) { + a[i] = 0; + __goblint_check(a[0]==0); + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 0); //UNKNOWN! + __goblint_check(top == 6); // FAIL +} + +// There is a call on the loop's condition +int update(int i) { + if (i>5){ + return 0; + } + else{ + return 1; + } +} +void example7(void) +{ + int a[10]; + int i = 0; + while(update(i)){ + a[i] = i; + ++i; + } + __goblint_check(a[0] == 0); //UNKNOWN + __goblint_check(a[6] == 0); //UNKNOWN +} + +// nested loops +void example8(void) +{ + int a[5]; + int b[] = {0,0,0,0,0}; + int i = 0; + while(i < 5){ + a[i] = i; + int j = 0; + while(j < 5){ + b[j] += a[i]; + ++j; + } + ++i; + } + return 0; +} + +// example with loop like the ones CIL does internally (while(1) + break) +void example9(void) +{ + int a[5]; + int i = 0; + while(1){ + a[i] = i; + ++i; + if (i == 5) break; + } + return 0; +} + +// example with loop containing a "continue" instruction +void example10(void) +{ + int a[5]; + int i = 0; + while(i<5){ + if (i == 3) { + i++; + continue; + } + a[i] = i; + ++i; + } + return 0; +} diff --git a/tests/regression/66-interval-set-one/50-interprocedural.c b/tests/regression/66-interval-set-one/50-interprocedural.c new file mode 100644 index 0000000000..a0d9b05597 --- /dev/null +++ b/tests/regression/66-interval-set-one/50-interprocedural.c @@ -0,0 +1,69 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int main(void) { + example1(); + example2(); +} + +// ----------------------------------- Example 1 ------------------------------------------------------------------------------ +void example1() { + int a[20]; + int b[20]; + + init_array(a, 42); + + __goblint_check(a[2] == 42); + __goblint_check(a[10] == 42); + + do_first(a); + __goblint_check(a[0] == 3); + + init_array(b,12); + __goblint_check(b[2] == 12); + __goblint_check(b[10] == 12); +} + +void do_first(int* arr) { + int x = arr[0]; + arr[0] = 3; +} + +void init_array(int* arr, int val) { + for(int i = 0; i < 20; i++) { + arr[i] = val; + } + arr[0] = val; + + __goblint_check(arr[2] == val); + __goblint_check(arr[10] == val); +} + +// ----------------------------------- Example 2 ------------------------------------------------------------------------------ + +void example2(void) { + int arr[20]; + + for(int i = 0; i < 20; i++) + { + arr[i] = 42; + __goblint_check(arr[i] == 42); + callee(arr); + } + + __goblint_check(arr[0] == 100); //FAIL + __goblint_check(arr[0] == 7); //UNKNOWN + __goblint_check(arr[0] == 42); //UNKNOWN + + __goblint_check(arr[7] == 100); //FAIL + __goblint_check(arr[7] == 7); //UNKNOWN + __goblint_check(arr[7] == 42); //UNKNOWN + + __goblint_check(arr[20] == 100); //FAIL + __goblint_check(arr[20] == 7); //UNKNOWN + __goblint_check(arr[20] == 42); //UNKNOWN +} + +void callee(int* arr) { + arr[0] = 7; +} diff --git a/tests/regression/66-interval-set-one/51-widen-sides.c b/tests/regression/66-interval-set-one/51-widen-sides.c new file mode 100644 index 0000000000..72eb1396b1 --- /dev/null +++ b/tests/regression/66-interval-set-one/51-widen-sides.c @@ -0,0 +1,30 @@ +// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval_set --sets solvers.td3.side_widen sides-local +#include + +int further(int n) { + // Even sides-local can not save us here :( + __goblint_check(n <= 1); //TODO +} + + +int fun(int n, const char* arg) { + // Fails with solvers.td3.side_widen sides, needs sides-local + __goblint_check(n <= 1); + further(n); +} + +void doIt(char* const arg) { + // These calls cause side-effects to the start state of [fun] + // As the calls happen after each other, we have two increasing contributions to [fun] from this unknown + // In the setting with solvers.td3.side_widen sides, [fun] thus becomes a wpoint + fun(0, arg); +} + + +int main() { + doIt("one"); + doIt("two"); + + // In the setting with solvers.td3.side_widen sides, widening happens and the bound is lost + fun(1, "org"); +} diff --git a/tests/regression/66-interval-set-one/53-simple_array.c b/tests/regression/66-interval-set-one/53-simple_array.c new file mode 100644 index 0000000000..79fa6a03dd --- /dev/null +++ b/tests/regression/66-interval-set-one/53-simple_array.c @@ -0,0 +1,222 @@ +// SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable annotation.int.enabled --set ana.int.refinement fixpoint +// skipped because https://github.com/goblint/analyzer/issues/468 +#include + +int global; + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2(void) __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example7(void) __attribute__((goblint_precision("no-def_exc"))); +void example8() __attribute__((goblint_precision("no-def_exc"))); +void example9() __attribute__((goblint_precision("no-def_exc"))); +void example10() __attribute__((goblint_precision("no-def_exc"))); + + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[42]; + int i = 0; + int top; + + while (i < 42) { + a[i] = 0; + __goblint_check(a[i] == 0); + __goblint_check(a[0] == 0); + __goblint_check(a[17] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// More complicated expression to index rather than just a variable +void example2(void) { + int a[42]; + int i = 1; + + while (i < 43) { + a[i - 1] = 0; + __goblint_check(a[i - 1] == 0); + __goblint_check(a[38] == 0); // UNKNOWN + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + +// Two values initialized in one loop +void example3(void) { + int a[42]; + int i = 0; + + while (i < 42) { + a[i] = 0; + i++; + a[i] = 1; + i++; + } + + __goblint_check(a[0] == 2); // FAIL + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[41] == 1); // UNKNOWN + __goblint_check(a[41] == -1); // FAIL +} + +// Example where initialization proceeds backwards +void example4(void) { + int a[42]; + int i = 41; + + while(i >= 12) { + a[i] = 0; + i--; + } + + __goblint_check(a[i+2] == 0); + __goblint_check(a[41] == 0); + __goblint_check(a[i] == 0); //UNKNOWN + __goblint_check(a[0] == 0); //UNKNOWN +} + +// Example having two arrays partitioned according to one expression +void example5(void) { + int a[42]; + int b[42]; + int i = 0; + + while(i < 42) { + a[i] = 2; + b[41-i] = 0; + + __goblint_check(b[7] == 0); //UNKNOWN + __goblint_check(a[5] == 2); //UNKNOWN + i++; + } + + __goblint_check(a[0] == 2); + __goblint_check(a[41] == 2); + __goblint_check(b[0] == 0); + __goblint_check(b[41] == 0); +} + +// Example showing array becoming partitioned according to different expressions +void example6(void) { + int a[42]; + int i = 0; + int j = 0; + int top; + + while(i < 42) { + a[i] = 4; + i++; + } + + __goblint_check(a[17] == 4); + __goblint_check(a[9] == 4); + __goblint_check(a[3] == 4); + __goblint_check(a[i-1] == 4); + + while(j<10) { + a[j] = -1; + j++; + } + + __goblint_check(a[3] == -1); + __goblint_check(a[0] == -1); + __goblint_check(a[j-1] == -1); + __goblint_check(a[j] == 4); + __goblint_check(a[17] == 4); + __goblint_check(a[j+5] == 4); +} + +// This was the case where we thought we needed path-splitting +void example7(void) { + int a[42]; + int i = 0; + int top; + + if(top) { + while(i < 41) { + a[i] = 0; + __goblint_check(a[i] == 0); + i++; + } + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[7] == 0); // UNKNOWN + __goblint_check(a[41] == 0); // UNKNOWN + __goblint_check(a[top] == 0); // UNKNOWN +} + +// Check that the global variable is not used for partitioning +void example8() { + int a[10]; + + a[global] = 4; + __goblint_check(a[global] == 4); // UNKNOWN + + for(int i=0; i <5; i++) { + a[i] = 42; + } + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + __goblint_check(a[2] == 42); + __goblint_check(a[3] == 42); + __goblint_check(a[global] == 42); +} + +// Check that arrays of types different from int are handeled correctly +void example9() { + char a[10]; + int n; + __goblint_check(a[3] == 800); // FAIL + + for(int i=0;i < 10; i++) { + a[i] = 7; + } + + __goblint_check(a[0] == 7); + __goblint_check(a[3] == 7); + + a[3] = (char) n; + __goblint_check(a[3] == 800); //FAIL + __goblint_check(a[3] == 127); //UNKNOWN + __goblint_check(a[3] == -128); //UNKNOWN + __goblint_check(a[3] == -129); //FAIL +} + +void example10() { + int a[20]; + a[5] = 3; + + int i=5; + a[i] = 7; + __goblint_check(a[5] == 7); +} diff --git a/tests/regression/66-interval-set-one/54-interval-and-enums.c b/tests/regression/66-interval-set-one/54-interval-and-enums.c new file mode 100644 index 0000000000..2ec4687309 --- /dev/null +++ b/tests/regression/66-interval-set-one/54-interval-and-enums.c @@ -0,0 +1,74 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc --enable ana.int.enums +#include +#include + + +int main () { + int a = 1,b = 2,c = 3; + int x,y,z; + int w; + int false = 0; + int true = 42; + + if (x){ + __goblint_check(x != 0); + } else { + __goblint_check(x == 0); + } + + __goblint_check(!! true); + __goblint_check(! false); + + if (a){ + a = a; + } else + __goblint_check(0); // NOWARN + + + if (!a) + __goblint_check(0); // NOWARN + else + a = a; + + if (z != 0){ + a = 8; + b = 9; + } else { + a = 9; + b = 8; + } + + __goblint_check(a); + __goblint_check(a!=b); //UNKNOWN + __goblint_check(a<10); + __goblint_check(a<=9); + __goblint_check(!(a<8)); + __goblint_check(a==8); //UNKNOWN + __goblint_check(b>7); + __goblint_check(b>=8); + __goblint_check(!(a>9)); + __goblint_check(b==8); //UNKNOWN + + for(x = 0; x < 10; x++){ + __goblint_check(x >= 0); + // Because the false branch remained unreachable for more iterations, the analysis behaved differently, meaning + // with ana.int.enums enabled, we didn't know (x >= 0) here + __goblint_check(x <= 9); + } + __goblint_check(x == 10); + + if (0 <= w) + { + } + else + { + return 0; + } + + if (w > 0) + { + __goblint_check(1); + } + + return 0; +} diff --git a/tests/regression/66-interval-set-one/55-advantage_for_last.c b/tests/regression/66-interval-set-one/55-advantage_for_last.c new file mode 100644 index 0000000000..e544577fee --- /dev/null +++ b/tests/regression/66-interval-set-one/55-advantage_for_last.c @@ -0,0 +1,20 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.partition-arrays.keep-expr last --set ana.base.arrays.domain partitioned +#include + +void main(void) { + example1(); +} + +void example1(void) { + int a[42]; + a[40] = 2; + int i = 0; + + while(i < 41) { + a[i] = 0; + i++; + } + + __goblint_check(a[2] == 0); + __goblint_check(a[3] == 0); +} diff --git a/tests/regression/66-interval-set-one/56-modulo-interval.c b/tests/regression/66-interval-set-one/56-modulo-interval.c new file mode 100644 index 0000000000..c561892604 --- /dev/null +++ b/tests/regression/66-interval-set-one/56-modulo-interval.c @@ -0,0 +1,21 @@ +// PARAM: --disable ana.int.def_exc --enable ana.int.interval_set +#include +int main() { + int x = -1; + int m = x % 5; + int r = x /5; + __goblint_check(m == -1); + __goblint_check(r == 0); + + x = 1; + m = x%-5; + r = x/-5; + __goblint_check(m == 1); + __goblint_check(r ==0); + + x = -1; + m = x%-5; + r = x/-5; + __goblint_check(m == -1); + __goblint_check(r == 0); +} diff --git a/tests/regression/66-interval-set-one/57-passing_ptr_to_array.c b/tests/regression/66-interval-set-one/57-passing_ptr_to_array.c new file mode 100644 index 0000000000..a84ab962d0 --- /dev/null +++ b/tests/regression/66-interval-set-one/57-passing_ptr_to_array.c @@ -0,0 +1,72 @@ +//PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +#include + +void foo(int (*a)[40]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + int y = (*(a + 7))[13]; + __goblint_check(y == 23); + + __goblint_check(a[7][13] == 23); +} + +void foo2(int n,int (*a)[n]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + int y = (*(a + 7))[13]; + __goblint_check(y == 23); + + __goblint_check(a[7][13] == 23); +} + +void foo3(int n,int a[][n]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + int y = (*(a + 7))[13]; + __goblint_check(y == 23); + + __goblint_check(a[7][13] == 23); +} + +void foo4(int n,int a[n][n]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + int y = (*(a + 7))[13]; + __goblint_check(y == 23); + + __goblint_check(a[7][13] == 23); +} + +void foo5(int n, int m, int a[n][m]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + int y = (*(a + 7))[13]; + __goblint_check(y == 23); + + __goblint_check(a[7][13] == 23); +} + +int main(void) +{ + int n =40; + int b[n][n]; + + for(int i=0;i < 40; i++) { + for(int j=0; j<40;j++) { + b[i][j] = 0; + } + } + + b[7][13] = 23; + + foo(b); + foo2(40,b); + foo3(40,b); + foo4(40,b); + foo5(40,40,b); +} diff --git a/tests/regression/66-interval-set-one/59-replace_with_const.c b/tests/regression/66-interval-set-one/59-replace_with_const.c new file mode 100644 index 0000000000..2233920d49 --- /dev/null +++ b/tests/regression/66-interval-set-one/59-replace_with_const.c @@ -0,0 +1,38 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --enable ana.base.partition-arrays.partition-by-const-on-return +#include + +int main(void) { + example1(); +} + +// ----------------------------------- Example 1 ------------------------------------------------------------------------------ +void example1() { + int a[20]; + int b[20]; + + init_array(a, 42); + + __goblint_check(a[2] == 42); + __goblint_check(a[10] == 42); + + do_first(a); + __goblint_check(a[0] == 3); + + init_array(b,12); + __goblint_check(b[2] == 12); + __goblint_check(b[10] == 12); +} + +void do_first(int* arr) { + int x = arr[0]; + arr[0] = 3; +} + +void init_array(int* arr, int val) { + for(int i = 0; i < 15; i++) { + arr[i] = val; + } + + __goblint_check(arr[2] == val); + __goblint_check(arr[10] == val); +} diff --git a/tests/regression/66-interval-set-one/60-intervals-test.c b/tests/regression/66-interval-set-one/60-intervals-test.c new file mode 100644 index 0000000000..37067b03f7 --- /dev/null +++ b/tests/regression/66-interval-set-one/60-intervals-test.c @@ -0,0 +1,14 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --disable ana.int.enums +#include + +void main(){ + int n = 7; + for (; n; n--) { + __goblint_check(n==1); // UNKNOWN! + } + int i; + if(i-1){ + __goblint_check(i==2); // UNKNOWN! + } + return; +} diff --git a/tests/regression/66-interval-set-one/61-arithm.c b/tests/regression/66-interval-set-one/61-arithm.c new file mode 100644 index 0000000000..90b9c82bb3 --- /dev/null +++ b/tests/regression/66-interval-set-one/61-arithm.c @@ -0,0 +1,18 @@ +// PARAM: --enable ana.int.def_exc --disable ana.int.interval_set +#include +#include +// 2 ^ 30 +#define MULT 1073741824 + +int main(){ + unsigned int top; + unsigned int result; + // top = 7; + if(top != 3){ + result = top * MULT; + // if top == 7 then we have (2 + 1) * 2^30 == (4 + 2 + 1) * 2^30 (mod 2^32) + __goblint_check(result != 3221225472); // UNKNOWN! + printf("%u\n", result); + } + return result; +} diff --git a/tests/regression/66-interval-set-one/62-pfscan_widen_dependent_minimal.c b/tests/regression/66-interval-set-one/62-pfscan_widen_dependent_minimal.c new file mode 100644 index 0000000000..792be97752 --- /dev/null +++ b/tests/regression/66-interval-set-one/62-pfscan_widen_dependent_minimal.c @@ -0,0 +1,80 @@ +// PARAM: --enable ana.int.interval_set --enable exp.priv-distr-init +extern int __VERIFIER_nondet_int(); + +#include +#include + +// protection priv succeeds +// write fails due to [1,1] widen [0,1] -> [-inf,1] +// sensitive to eval and widen order! + +struct __anonstruct_PQUEUE_63 { + int qsize ; + int occupied ; + pthread_mutex_t mtx ; +}; +typedef struct __anonstruct_PQUEUE_63 PQUEUE; + +PQUEUE pqb ; + +int pqueue_init(PQUEUE *qp , int qsize ) +{ + qp->qsize = qsize; + qp->occupied = 0; + pthread_mutex_init(& qp->mtx, NULL); + return (0); +} + +int pqueue_put(PQUEUE *qp) +{ + pthread_mutex_lock(& qp->mtx); + while (qp->occupied >= qp->qsize) { + + } + __goblint_check(qp->occupied >= 0); // precise privatization fails + (qp->occupied) ++; + pthread_mutex_unlock(& qp->mtx); + return (1); +} + +int pqueue_get(PQUEUE *qp) +{ + int got = 0; + pthread_mutex_lock(& qp->mtx); + while (qp->occupied <= 0) { + + } + __goblint_check(qp->occupied > 0); // precise privatization fails + if (qp->occupied > 0) { + (qp->occupied) --; + got = 1; + pthread_mutex_unlock(& qp->mtx); + } else { + pthread_mutex_unlock(& qp->mtx); + } + return (got); +} + + +void *worker(void *arg ) +{ + while (1) { + pqueue_get(& pqb); + } + return NULL; +} + +int main(int argc , char **argv ) +{ + pthread_t tid; + int qsize = __VERIFIER_nondet_int(); + + PQUEUE *qp = &pqb; + pqueue_init(& pqb, qsize); + pthread_create(& tid, NULL, & worker, NULL); + + for (int i = 1; i < argc; i++) { + pqueue_put(& pqb); + } + return 0; +} diff --git a/tests/regression/66-interval-set-one/64-loc.c b/tests/regression/66-interval-set-one/64-loc.c new file mode 100644 index 0000000000..9c4a628f41 --- /dev/null +++ b/tests/regression/66-interval-set-one/64-loc.c @@ -0,0 +1,71 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins --enable ana.thread.include-node +// Inspired by 36/98 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_benign(void *arg) { + pthread_mutex_lock(&A); + g = 20; + pthread_mutex_unlock(&A); + return NULL; +} + +void *u_benign(void *arg) { + pthread_mutex_lock(&A); + h = 20; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t; + + pthread_t id; + + if(t) { + pthread_create(&id, NULL, t_benign, NULL); + } else { + pthread_create(&id, NULL, t_benign, NULL); + } + + // As these two threads are distinguished, we have a non-unique TID for id + pthread_join(id, NULL); + + + pthread_mutex_lock(&A); + g = 12; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == 12); //TODO + pthread_mutex_unlock(&A); + +// --------------------------------------------------------------------------- + + pthread_t id2; + pthread_t id3; + + + pthread_create(&id2, NULL, u_benign, NULL); + pthread_create(&id3, NULL, u_benign, NULL); + + pthread_join(id2, NULL); + + // As these two threads are distinguished, id3 is a unique thread + pthread_join(id3, NULL); + + + pthread_mutex_lock(&A); + h = 12; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(h == 12); + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/65-multidimensional-array-oob-access.c b/tests/regression/66-interval-set-one/65-multidimensional-array-oob-access.c new file mode 100644 index 0000000000..54f81c15e6 --- /dev/null +++ b/tests/regression/66-interval-set-one/65-multidimensional-array-oob-access.c @@ -0,0 +1,20 @@ +// PARAM: --enable ana.arrayoob --enable ana.int.interval_set --enable ana.int.enums +//Multidimensional array: Out of bounds access +#include +int main( ) +{ + int arr[4][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + arr[3][2] = 3; //NOWARN + arr[6][3] = 10; //WARN + arr[3][6] = 10; //WARN + arr[0][1] = 4; //NOWARN + arr[-6][3] = 10; //WARN + arr[3][-3] = 10; //WARN + + for (int i = 0; i < 10; ++i) + { + arr[i][i] = 5; //WARN + } + return 0; +} + diff --git a/tests/regression/66-interval-set-one/66-large-n-div2.c b/tests/regression/66-interval-set-one/66-large-n-div2.c new file mode 100644 index 0000000000..759b98ce5e --- /dev/null +++ b/tests/regression/66-interval-set-one/66-large-n-div2.c @@ -0,0 +1,23 @@ +//PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +#include + +int main(){ + int top; + // 2^33 + long long x = 8589934592l; + // 2^31 - 1 + long long y = 2147483647; + + if(top) { + x = x - 1; + } + + long long z = x/y; + + if(z == 4){ + // Should be reachable + __goblint_check(1); + } + + __goblint_check(z == 4); +} diff --git a/tests/regression/66-interval-set-one/69-even_more_passing.c b/tests/regression/66-interval-set-one/69-even_more_passing.c new file mode 100644 index 0000000000..5cac0d8d86 --- /dev/null +++ b/tests/regression/66-interval-set-one/69-even_more_passing.c @@ -0,0 +1,42 @@ +//PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +#include + +void foo2(int n , int (*a)[n] ) +{ + int x ; + int y ; + + int *ptr = *(a+7); + __goblint_check(ptr[13] == 23); + + x = (*(a + 29))[7]; + __goblint_check(x == 23); //FAIL + + y = (*(a + 7))[13]; + __goblint_check(y == 23); + + return; +} + +int main(void) +{ + int r = 40; + int c[40][40]; + int d[r][r]; + + for(int i = 0; i < 40;i++) { + for(int j=0;j < 40;j++) { + c[i][j] = 0; + d[i][j] = 0; + } + } + + c[7][13] = 23; + d[7][13] = 23; + + + foo2(40, c); + foo2(40, d); + + return (0); +} diff --git a/tests/regression/66-interval-set-one/70-simple-cases.c b/tests/regression/66-interval-set-one/70-simple-cases.c new file mode 100644 index 0000000000..9007e56f5b --- /dev/null +++ b/tests/regression/66-interval-set-one/70-simple-cases.c @@ -0,0 +1,196 @@ +// PARAM: --enable ana.int.interval_set --set exp.unrolling-factor 5 +#include + +int global; + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[5]; + int i = 0; + + while (i < 5) { + a[i] = i; + i++; + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[3] == 3); // UNKNOWN +} + +// Do-while loop simple example +void example2(void) +{ + int a[5]; + int i = 0; + + do { + a[i] = i; + i++; + } while (i<=5); + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[3] == 3); // UNKNOWN +} + +// Initialization not completed, yet the array representation is not precise +void example3(void) +{ + int a[10]; + int i = 0; + + while (i < 5) { + a[i] = i; + i++; + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[3] == 0); // UNKNOWN + __goblint_check(a[7] == 0); // UNKNOWN +} + +// Example with increased precision. Goblint detects in which iteration it is during the unrolled part. +void example4(void) +{ + int a[10]; + int i = 0; + int first_iteration = 1; + + while (i < 10) { + if (first_iteration == 1) __goblint_check(i==0); + else if (i > 5) __goblint_check(i == 6); // UNKNOWN + first_iteration = 0; + a[i] = 0; + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(first_iteration == 0); +} + + +// Example where the precision increase can be appreciated by a variable that +// is modified in the loop other than the ones used in the loop head +void example5(void) +{ + int a[4]; + int i = 0; + int top = 0; + + while (i < 4) { + a[i] = 0; + top += i; + if(i==2){ + __goblint_check(top == 3); + } + else{ + __goblint_check(top == 3); // FAIL + } + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 0); + __goblint_check(top == 6); +} + +// Loop has less iterations than the unrolling factor +void example6(void) +{ + int a[5]; + int i = 0; + int top = 0; + + while (i < 3) { + a[i] = 0; + __goblint_check(a[0]==0); + i++; + } + + __goblint_check(a[0] == 0); + __goblint_check(a[3] == 0); + __goblint_check(top == 6); // FAIL +} + +// There is a call on the loop's condition +int update(int i) { + if (i>5){ + return 0; + } + else{ + return 1; + } +} +void example7(void) +{ + int a[10]; + int i = 0; + while(update(i)){ + a[i] = i; + ++i; + } + __goblint_check(a[0] == 0); //UNKNOWN + __goblint_check(a[6] == 0); //UNKNOWN +} + +// nested loops +void example8(void) +{ + int a[5]; + int b[] = {0,0,0,0,0}; + int i = 0; + while(i < 5){ + a[i] = i; + int j = 0; + while(j < 5){ + b[j] += a[i]; + ++j; + } + ++i; + } + return 0; +} + +// example with loop like the ones CIL does internally (while(1) + break) +void example9(void) +{ + int a[5]; + int i = 0; + while(1){ + a[i] = i; + ++i; + if (i == 5) break; + } + return 0; +} + +// example with loop containing a "continue" instruction +void example10(void) +{ + int a[5]; + int i = 0; + while(i<5){ + if (i == 3) { + i++; + continue; + } + a[i] = i; + ++i; + } + return 0; +} \ No newline at end of file diff --git a/tests/regression/66-interval-set-one/71-int-context-option.c b/tests/regression/66-interval-set-one/71-int-context-option.c new file mode 100644 index 0000000000..5f31a25a8a --- /dev/null +++ b/tests/regression/66-interval-set-one/71-int-context-option.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen --disable ana.base.context.int --set annotation.goblint_context.f[+] base.int +#include + +int f(int x) { + if (x) + return x * f(x - 1); + else + return 1; +} + +int main () { + int a = f(10); + __goblint_check(a == 3628800); + return 0; +} diff --git a/tests/regression/66-interval-set-one/73-intervals.c b/tests/regression/66-interval-set-one/73-intervals.c new file mode 100644 index 0000000000..cfaf9ed3a0 --- /dev/null +++ b/tests/regression/66-interval-set-one/73-intervals.c @@ -0,0 +1,11 @@ +// PARAM: --set sem.int.signed_overflow assume_none --enable ana.int.interval_set --disable ana.int.def_exc +#include + +int main(void) { + int x = 0; + while(x != 42) { + x++; + __goblint_check(x >= 1); + } + +} diff --git a/tests/regression/66-interval-set-one/74-testfive-intervals-protection.c b/tests/regression/66-interval-set-one/74-testfive-intervals-protection.c new file mode 100644 index 0000000000..513707b871 --- /dev/null +++ b/tests/regression/66-interval-set-one/74-testfive-intervals-protection.c @@ -0,0 +1,34 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.privatization protection --set solvers.td3.side_widen sides-pp +// also needs sides-pp now that protected and unprotected use different global constraint variables +#include +#include + +int myglobal; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void lock() { + pthread_mutex_lock(&mutex); +} + +void unlock() { + pthread_mutex_unlock(&mutex); +} + + +void *t_fun(void *arg) { + lock(); + myglobal++; // NORACE + unlock(); + return NULL; +} + + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + lock(); + myglobal++; // NORACE + unlock(); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/66-interval-set-one/75-22_02-pointers_array.c b/tests/regression/66-interval-set-one/75-22_02-pointers_array.c new file mode 100644 index 0000000000..e140dbbeb8 --- /dev/null +++ b/tests/regression/66-interval-set-one/75-22_02-pointers_array.c @@ -0,0 +1,283 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable annotation.int.enabled --set ana.int.refinement fixpoint +#include + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2() __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example7(void) __attribute__((goblint_precision("no-def_exc"))); +void example8(void) __attribute__((goblint_precision("no-def_exc"))); + +struct a { + int x[42]; + int y; +}; + +void example9() __attribute__((goblint_precision("no-def_exc"))); +int example10() __attribute__((goblint_precision("no-def_exc"))); +void foo(int (*a)[40]) __attribute__((goblint_precision("no-def_exc"))); +void example11() __attribute__((goblint_precision("no-def_exc"))); + + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + example11(); + return 0; +} + +// Initializing an array with pointers +void example1(void) { + int top; + + int a[42]; + int *ptr = &a; + + *ptr = 42; + ptr++; + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); // UNKNOWN + + *ptr = 42; + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + ptr++; + + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + + + int i = 5; + __goblint_check(a[i] == 42); + + if(top) { + i++; + } + + __goblint_check(a[i] == 42); // UNKNOWN +} + +// Tests correct handling when pointers may point to several different things +void example2() { + int array1[10000000]; + int array2[10000000]; + + int* ptr; + + if(rand()) { + ptr = &array1; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + else { + ptr = &array2; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + + // Since ptr could point to different arrays, the update here can not be precise + *ptr = 6; + + __goblint_check(*ptr == 6); // UNKNOWN +} + +void example3(void) { + int array1[5]; + int *ptr = &array1; + + for(int i =0; i <5; i++) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } +} + +void example4(void) { + int array1[5]; + int *ptr = &array1; + int *end = &(array1[5]); + + while(ptr <= end) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } + + // In an ideal world, I would like to have information about array1[0] and so on. For this the <= would need to improve, so that ptr is known to point to {array1[5,5]} +} + +void example5(void) { + int array1[5]; + int *ptr = &(array1[4]); + + *ptr = 42; + ptr--; + *ptr = 42; + ptr--; + *ptr = 40; + + __goblint_check(*ptr == 40); + __goblint_check(array1[4] == 42); + __goblint_check(array1[3] == 42); + __goblint_check(array1[2] == 40); + __goblint_check(array1[0] == 42); // UNKNOWN +} + +void example6(void) { + int array1[100]; + int* ptr = &array1; + + *ptr = 5; + int v = *ptr; + __goblint_check(v == 5); + + ptr++; + *ptr = 6; + ptr++; + *ptr = 7; + + // This is necessary for the tests that we are doing later + int k = ptr-&array1; + __goblint_check(k == 2); + int m = ptr-array1; + __goblint_check(m == 2); + + int* np = &array1; + np++; + np++; + int x = *np; + __goblint_check(x==7); +} + +void example7(void) { + int top; + + int arr1[42]; + int arr2[42]; + int *ptr; + + for(int i = 0; i < 42; i++) { + arr1[i] = 4; + arr2[i] = 4; + } + + ptr = &arr1[7]; + + if(top) { + ptr = &arr2[7]; + } + + *ptr = 9; + + // Case ptr = &arr1[7] + // arr1 -> (ptr-arr1, ([4,4], [9,9],[4,4])) + // arr2 -> (-,[4,4]) + + // Case ptr = &arr2[7] + // arr1 -> (-, [4,4]) + // arr2 -> (ptr-arr2, ([4,4], [9,9],[4,4])) + + // LUB: + // arr1 -> (-, [4,9]) + // arr2 -> (-, [4,9]) + int x = arr1[7]; + __goblint_check(x == 3); // FAIL + __goblint_check(x == 4); // UNKNOWN + __goblint_check(x == 9); // UNKNOWN + __goblint_check(x == 10); // FAIL +} + +void example8(void) { + int a[42][42]; + + for(int i = 0; i < 42; i++) { + for(int j=0;j < 42; j++) { + a[i][j] = 0; + } + } + + a[14][0] = 3; + + int* ptr = a[7]; + int x = *(ptr+7); + __goblint_check(x == 3); //FAIL + + int (*ptr2)[42]; + ptr2 = a+7; + x = (*ptr2)[6]; + __goblint_check(x == 3); //FAIL + printf("x is %d\n", x); +} + +struct a { + int x[42]; + int y; +}; + +void example9() { + int a[42][42]; + int (*ptr2)[42]; + int *y; + int i, j, x; + + for(i = 0; i < 42; i++) { + for(j=0;j < 42; j++) { + a[i][j] = 0; + } + } + + a[14][0] = 3; + ptr2 = a+7; + y = (ptr2+1)[6]; + __goblint_check(*y == 3); +} + +int example10() { + struct a x[42]; + int i, j, y, *ptr; + + for(i = 0; i < 42; i++) { + for(j=0;j < 42; j++) { + x[i].x[j] = 0; + } + } + x[3].x[3] = 7; + + ptr = x[3].x; + y = *(ptr + 3); + __goblint_check(y == 0); //FAIL + printf("y is %d", y); +} + +void foo(int (*a)[40]) { + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //UNKNOWN +} + +void example11() +{ + int b[40][40]; + b[7][7] = 23; + + foo(b); +} diff --git a/tests/regression/66-interval-set-one/76-calloc_loop.c b/tests/regression/66-interval-set-one/76-calloc_loop.c new file mode 100644 index 0000000000..4b0cfee477 --- /dev/null +++ b/tests/regression/66-interval-set-one/76-calloc_loop.c @@ -0,0 +1,19 @@ +// Made after 02 21 +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +int main() { + int* x[10]; + int i = 0; + + while (i < 10) + x[i++] = calloc(1, sizeof(int)); + + *x[3] = 50; + *x[7] = 100; + __goblint_check(*x[8] == 100); // UNKNOWN + + return 0; +} diff --git a/tests/regression/66-interval-set-one/77-more-problem.c b/tests/regression/66-interval-set-one/77-more-problem.c new file mode 100644 index 0000000000..726baeb999 --- /dev/null +++ b/tests/regression/66-interval-set-one/77-more-problem.c @@ -0,0 +1,18 @@ +//PARAM: --set ana.int.refinement once --enable ana.int.interval_set --enable ana.int.congruence --disable ana.int.def_exc +#include + +int main(void) +{ + int ret = 0; + unsigned int s__version; + if (s__version + 65280 != 768) + { + ret = 0; + } + else + { + ret = 1; + } + + __goblint_check(ret == 0); //UNKNOWN! +} diff --git a/tests/regression/66-interval-set-one/78-pointers_array.c b/tests/regression/66-interval-set-one/78-pointers_array.c new file mode 100644 index 0000000000..7bda3549b4 --- /dev/null +++ b/tests/regression/66-interval-set-one/78-pointers_array.c @@ -0,0 +1,262 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + example11(); + return 0; +} + +// Initializing an array with pointers +void example1(void) { + int top; + + int a[42]; + int *ptr = &a; + + *ptr = 42; + ptr++; + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); // UNKNOWN + + *ptr = 42; + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + ptr++; + + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + + + int i = 5; + __goblint_check(a[i] == 42); + + if(top) { + i++; + } + + __goblint_check(a[i] == 42); // UNKNOWN +} + +// Tests correct handling when pointers may point to several different things +void example2() { + int array1[10000000]; + int array2[10000000]; + + int* ptr; + + if(rand()) { + ptr = &array1; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + else { + ptr = &array2; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + + // Since ptr could point to different arrays, the update here can not be precise + *ptr = 6; + + __goblint_check(*ptr == 6); // UNKNOWN +} + +void example3(void) { + int array1[5]; + int *ptr = &array1; + + for(int i =0; i <5; i++) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } +} + +void example4(void) { + int array1[5]; + int *ptr = &array1; + int *end = &(array1[5]); + + while(ptr <= end) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } + + // In an ideal world, I would like to have information about array1[0] and so on. For this the <= would need to improve, so that ptr is known to point to {array1[5,5]} +} + +void example5(void) { + int array1[5]; + int *ptr = &(array1[4]); + + *ptr = 42; + ptr--; + *ptr = 42; + ptr--; + *ptr = 40; + + __goblint_check(*ptr == 40); + __goblint_check(array1[4] == 42); + __goblint_check(array1[3] == 42); + __goblint_check(array1[2] == 40); + __goblint_check(array1[0] == 42); // UNKNOWN +} + +void example6(void) { + int array1[100]; + int* ptr = &array1; + + *ptr = 5; + int v = *ptr; + __goblint_check(v == 5); + + ptr++; + *ptr = 6; + ptr++; + *ptr = 7; + + // This is necessary for the tests that we are doing later + int k = ptr-&array1; + __goblint_check(k == 2); + int m = ptr-array1; + __goblint_check(m == 2); + + int* np = &array1; + np++; + np++; + int x = *np; + __goblint_check(x==7); +} + +void example7(void) { + int top; + + int arr1[42]; + int arr2[42]; + int *ptr; + + for(int i = 0; i < 42; i++) { + arr1[i] = 4; + arr2[i] = 4; + } + + ptr = &arr1[7]; + + if(top) { + ptr = &arr2[7]; + } + + *ptr = 9; + + // Case ptr = &arr1[7] + // arr1 -> (ptr-arr1, ([4,4], [9,9],[4,4])) + // arr2 -> (-,[4,4]) + + // Case ptr = &arr2[7] + // arr1 -> (-, [4,4]) + // arr2 -> (ptr-arr2, ([4,4], [9,9],[4,4])) + + // LUB: + // arr1 -> (-, [4,9]) + // arr2 -> (-, [4,9]) + int x = arr1[7]; + __goblint_check(x == 3); // FAIL + __goblint_check(x == 4); // UNKNOWN + __goblint_check(x == 9); // UNKNOWN + __goblint_check(x == 10); // FAIL +} + +void example8(void) { + int a[42][42]; + + for(int i = 0; i < 42; i++) { + for(int j=0;j < 42; j++) { + a[i][j] = 0; + } + } + + a[14][0] = 3; + + int* ptr = a[7]; + int x = *(ptr+7); + __goblint_check(x == 3); //FAIL + + int (*ptr2)[42]; + ptr2 = a+7; + x = (*ptr2)[6]; + __goblint_check(x == 3); //FAIL + printf("x is %d\n", x); +} + +struct a { + int x[42]; + int y; +}; + +void example9() { + int a[42][42]; + int (*ptr2)[42]; + int *y; + int i, j, x; + + for(i = 0; i < 42; i++) { + for(j=0;j < 42; j++) { + a[i][j] = 0; + } + } + + a[14][0] = 3; + ptr2 = a+7; + y = (ptr2+1)[6]; + __goblint_check(*y == 3); +} + +int example10() { + struct a x[42]; + int i, j, y, *ptr; + + for(i = 0; i < 42; i++) { + for(j=0;j < 42; j++) { + x[i].x[j] = 0; + } + } + x[3].x[3] = 7; + + ptr = x[3].x; + y = *(ptr + 3); + __goblint_check(y == 0); //FAIL + printf("y is %d", y); +} + +void foo(int (*a)[40]){ + int x = (*(a + 29))[7]; + __goblint_check(x == 23); //UNKNOWN +} + +void example11() +{ + int b[40][40]; + b[7][7] = 23; + + foo(b); +} diff --git a/tests/regression/66-interval-set-one/79-tid-toy13.c b/tests/regression/66-interval-set-one/79-tid-toy13.c new file mode 100644 index 0000000000..c52a14bbac --- /dev/null +++ b/tests/regression/66-interval-set-one/79-tid-toy13.c @@ -0,0 +1,80 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins +// Inspired by 36/83 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +pthread_t other_t; + +void *t_fun(void *arg) { + int x = 10; + int y; + + pthread_mutex_lock(&A); + g = x; + h = y; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = x; + h = x; + pthread_mutex_unlock(&A); + return NULL; +} + +void *t_benign(void *arg) { + // Without this, it would even succeed without the must joined analysis. + // With it, that is required! + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_create(&other_t, NULL, t_fun, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = 10; + h = 10; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t; + + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + // Force multi-threaded handling + pthread_t id2; + for(int i = 0; i < 10;i++) { + pthread_create(&id2, NULL, t_benign, NULL); + } + + pthread_mutex_lock(&A); + __goblint_check(g == h); //UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_join(id2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); // UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_join(other_t, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == h); // UNKNOWN! + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/66-interval-set-one/80-lustre-minimal.c b/tests/regression/66-interval-set-one/80-lustre-minimal.c new file mode 100644 index 0000000000..abc33c3031 --- /dev/null +++ b/tests/regression/66-interval-set-one/80-lustre-minimal.c @@ -0,0 +1,11 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +// issue #120 +#include + +int main() { + // should be LP64 + unsigned long n = 16; + unsigned long size = 4912; + + __goblint_check(!(0xffffffffffffffffUL / size < n)); +} diff --git a/tests/regression/66-interval-set-one/82-malloc_array.c b/tests/regression/66-interval-set-one/82-malloc_array.c new file mode 100644 index 0000000000..e64d539e59 --- /dev/null +++ b/tests/regression/66-interval-set-one/82-malloc_array.c @@ -0,0 +1,14 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned +#include +#include + +int main(void) { + int *r = malloc(5 * sizeof(int)); + + r[3] = 2; + + __goblint_check(r[4] == 2); + /* Here we only test our implementation. Concretely, accessing the uninitialised r[4] is undefined behavior. + In our implementation we keep the whole memory allocated by malloc as one Blob and the whole Blob contains 2 after it was assigned to r[3]. + This is more useful than keeping the Blob unknown. */ +} diff --git a/tests/regression/66-interval-set-one/84-non-zero.c b/tests/regression/66-interval-set-one/84-non-zero.c new file mode 100644 index 0000000000..bf79c3279b --- /dev/null +++ b/tests/regression/66-interval-set-one/84-non-zero.c @@ -0,0 +1,29 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable exp.fast_global_inits +// This checks that partitioned arrays and fast_global_inits are no longer incompatible +#include + +int global_array[5] = {9, 0, 3, 42, 11}; +int global_array_multi[2][5] = {{9, 0, 3, 42, 11}, {9, 0, 3, 42, 11}}; + +int main(void) { + __goblint_check(global_array[0] == 9); //UNKNOWN + __goblint_check(global_array[1] == 0); //UNKNOWN + __goblint_check(global_array[2] == 3); //UNKNOWN + __goblint_check(global_array[3] == 42); //UNKNOWN + __goblint_check(global_array[4] == 11); //UNKNOWN + __goblint_check(global_array[1] == -1); //FAIL + + __goblint_check(global_array_multi[0][0] == 9); //UNKNOWN + __goblint_check(global_array_multi[0][1] == 0); //UNKNOWN + __goblint_check(global_array_multi[0][2] == 3); //UNKNOWN + __goblint_check(global_array_multi[0][3] == 42); //UNKNOWN + __goblint_check(global_array_multi[0][4] == 11); //UNKNOWN + __goblint_check(global_array_multi[0][1] == -1); //FAIL + + __goblint_check(global_array_multi[1][0] == 9); //UNKNOWN + __goblint_check(global_array_multi[1][1] == 0); //UNKNOWN + __goblint_check(global_array_multi[1][2] == 3); //UNKNOWN + __goblint_check(global_array_multi[1][3] == 42); //UNKNOWN + __goblint_check(global_array_multi[1][4] == 11); //UNKNOWN + __goblint_check(global_array_multi[1][1] == -1); //FAIL +} diff --git a/tests/regression/66-interval-set-one/85-cast-unsigned-to-signed.c b/tests/regression/66-interval-set-one/85-cast-unsigned-to-signed.c new file mode 100644 index 0000000000..e3fbefcc32 --- /dev/null +++ b/tests/regression/66-interval-set-one/85-cast-unsigned-to-signed.c @@ -0,0 +1,9 @@ +// PARAM: --enable ana.int.interval_set --set sem.int.signed_overflow assume_none +#include + +int main(void) { + unsigned long x; + long y = x; + __goblint_check(y >= 0); // UNKNOWN! + return 0; +} diff --git a/tests/regression/66-interval-set-one/86-large-n-div.c b/tests/regression/66-interval-set-one/86-large-n-div.c new file mode 100644 index 0000000000..2ad14368b2 --- /dev/null +++ b/tests/regression/66-interval-set-one/86-large-n-div.c @@ -0,0 +1,18 @@ +//PARAM: --enable ana.int.interval_set --disable ana.int.def_exc +#include + +int main(){ + // 2^33 + long long x = 8589934592l; + // 2^31 - 1 + long long y = 2147483647; + + long long z = x/y; + + if(z == 4){ + // Should be reachable + __goblint_check(1); + } + + __goblint_check(z == 4); +} diff --git a/tests/regression/66-interval-set-one/87-on.c b/tests/regression/66-interval-set-one/87-on.c new file mode 100644 index 0000000000..93e628bbf8 --- /dev/null +++ b/tests/regression/66-interval-set-one/87-on.c @@ -0,0 +1,13 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +// This checks that partitioned arrays and fast_global_inits are no longer incompatible +#include + +int global_array[50]; +int global_array_multi[50][2][2]; + +int main(void) { + for(int i =0; i < 50; i++) { + __goblint_check(global_array[i] == 0); + __goblint_check(global_array_multi[i][1][1] == 0); + } +} diff --git a/tests/regression/66-interval-set-one/88-publish-basic.c b/tests/regression/66-interval-set-one/88-publish-basic.c new file mode 100644 index 0000000000..ee7ecd946b --- /dev/null +++ b/tests/regression/66-interval-set-one/88-publish-basic.c @@ -0,0 +1,24 @@ +// PARAM: --set ana.int.interval_set true +#include +#include + +int glob1 = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex); + glob1 = 5; + __goblint_check(glob1 == 5); + pthread_mutex_unlock(&mutex); + return NULL; +} + +int main(void) { + pthread_t id; + __goblint_check(glob1 == 0); + pthread_create(&id, NULL, t_fun, NULL); + __goblint_check(glob1 == 0); // UNKNOWN! + __goblint_check(glob1 == 5); // UNKNOWN! + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/66-interval-set-one/89-slr-interval.c b/tests/regression/66-interval-set-one/89-slr-interval.c new file mode 100644 index 0000000000..b2f852cd5a --- /dev/null +++ b/tests/regression/66-interval-set-one/89-slr-interval.c @@ -0,0 +1,73 @@ +// PARAM: --set ana.int.interval_set true --set solver new +// https://github.com/goblint/analyzer/pull/805#discussion_r933230577 +#include +#include + + +int main () { + int a = 1,b = 2,c = 3; + int x,y,z; + int w; + int false = 0; + int true = 42; + + if (x){ + __goblint_check(x != 0); + } else { + __goblint_check(x == 0); + } + + __goblint_check(!! true); + __goblint_check(! false); + + if (a){ + a = a; + } else + __goblint_check(0); // NOWARN + + + if (!a) + __goblint_check(0); // NOWARN + else + a = a; + + if (z != 0){ + a = 8; + b = 9; + } else { + a = 9; + b = 8; + } + + __goblint_check(a); + __goblint_check(a!=b); //UNKNOWN + __goblint_check(a<10); + __goblint_check(a<=9); + __goblint_check(!(a<8)); + __goblint_check(a==8); //UNKNOWN + __goblint_check(b>7); + __goblint_check(b>=8); + __goblint_check(!(a>9)); + __goblint_check(b==8); //UNKNOWN + + for(x = 0; x < 10; x++){ + __goblint_check(x >= 0); + __goblint_check(x <= 9); + } + __goblint_check(x == 10); + + if (0 <= w) + { + } + else + { + return 0; + } + + if (w > 0) + { + __goblint_check(1); + } + + return 0; +} diff --git a/tests/regression/66-interval-set-one/90-nesting_arrays.c b/tests/regression/66-interval-set-one/90-nesting_arrays.c new file mode 100644 index 0000000000..8d5bc8cea5 --- /dev/null +++ b/tests/regression/66-interval-set-one/90-nesting_arrays.c @@ -0,0 +1,211 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --enable annotation.int.enabled --set ana.int.refinement fixpoint +#include + +struct kala { + int i; + int a[5]; +}; + +struct kalaw { + int* a; +}; + +struct kass { + int v; +}; + +union uArray { + int a[5]; + int b[5]; +}; + +union uStruct { + int b; + struct kala k; +}; + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1() __attribute__((goblint_precision("no-def_exc"))); +void example2() __attribute__((goblint_precision("no-def_exc"))); +void example3() __attribute__((goblint_precision("no-def_exc"))); +void example4() __attribute__((goblint_precision("no-def_exc"))); +void example5() __attribute__((goblint_precision("no-def_exc"))); +void example6() __attribute__((goblint_precision("no-def_exc"))); +void example7() __attribute__((goblint_precision("no-def_exc"))); +void example8() __attribute__((goblint_precision("no-def_exc"))); + + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + return 0; +} + +void example1() { + struct kala l; + int i = 0; + int top; + + while (i < 5) { + l.a[i] = 42; + i++; + + // Check assertion that should only hold later does not already hold here + __goblint_check(l.a[4] == 42); //UNKNOWN + } + + // Check the array is correctly initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); + + // Destructively assign to i + i = top; + + // Check the array is still known to be completely initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); +} + +void example2() { + struct kala kalas[5]; + + int i2 = 0; + + while (i2 < 4) { + int j2 = 0; + while (j2 < 5) { + kalas[i2].a[j2] = 8; + j2++; + } + i2++; + } + + // Initialization has not proceeded this far + __goblint_check(kalas[4].a[0] == 8); //UNKNOWN + __goblint_check(kalas[0].a[0] == 8); +} + +void example3() { + struct kala xnn; + for(int l=0; l < 5; l++) { + xnn.a[l] = 42; + } + + __goblint_check(xnn.a[3] == 42); +} + +void example4() { + struct kala xn; + + struct kala xs[5]; + + for(int j=0; j < 4; j++) { + xs[j] = xn; + for(int k=0; k < 5; k++) { + xs[j].a[k] = 7; + } + } + + __goblint_check(xs[3].a[0] == 7); +} + +void example5() { + // This example is a bit contrived to show that splitting and moving works for + // unions + union uArray ua; + int i3 = 0; + int top; + int *i = ⊤ + + ua.a[*i] = 1; + + while (i3 < 5) { + ua.a[i3] = 42; + i3++; + } + + __goblint_check(ua.a[i3 - 1] == 42); + + ua.b[0] = 3; + __goblint_check(ua.b[0] == 3); + + // ------------------------------- + union uStruct us; + int i4 = 0; + + us.b = 4; + us.k.a[i4] = 0; + __goblint_check(us.b == 4); // UNKNOWN + __goblint_check(us.k.a[0] == 0); + __goblint_check(us.k.a[3] == 0); // UNKNOWN + + while (i4 < 5) { + us.k.a[i4] = 42; + i4++; + } + + __goblint_check(us.k.a[1] == 42); + __goblint_check(us.k.a[0] == 0); // FAIL +} + +void example6() { + int a[42]; + int i = 0; + + struct kass k; + k.v = 7; + + while(i < 42) { + a[i] = 0; + i++; + } + + i = 0; + + a[k.v] = 2; + k.v = k.v+1; + + __goblint_check(a[k.v] != 3); +} + +void example7() { + // Has no asserts, just checks this doesn't cause an infinite loop + int a[42]; + int i = 0; + + while(i < 40) { + a[i] = 0; + i++; + } + + a[a[0]] = 2; +} + +// Test correct behavior with more involved expression in subscript operator +void example8() { + int a[42]; + union uArray ua; + + ua.a[0] = 0; + ua.a[1] = 0; + ua.a[2] = 0; + ua.a[3] = 0; + ua.a[4] = 0; + + int i = 0; + int *ip = &i; + + a[ua.a[*ip]] = 42; + ip++; + __goblint_check(a[ua.a[*ip]] == 42); //UNKNOWN +} diff --git a/tests/regression/66-interval-set-one/92-assert-infinite-loop.c b/tests/regression/66-interval-set-one/92-assert-infinite-loop.c new file mode 100644 index 0000000000..920acf2dc9 --- /dev/null +++ b/tests/regression/66-interval-set-one/92-assert-infinite-loop.c @@ -0,0 +1,19 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc +// This is a pattern we saw in some examples for SVCOMP, where instead of the __goblint_check(0) there was a call to verifier error. +// Because of the demand-driven nature of our solvers, we never looked at the code inside fail since there is no edge from the loop to the endpoint of f. +// However, the __goblint_check(0) (verifier error) is still reachable from main. +#include + +void f(void) { + fail: + __goblint_check(0); //FAIL + goto fail; +} + +int main(void) { + int top; + + if(top) { + f(); + } +} diff --git a/tests/regression/66-interval-set-one/93-enum.c b/tests/regression/66-interval-set-one/93-enum.c new file mode 100644 index 0000000000..5d2b45043d --- /dev/null +++ b/tests/regression/66-interval-set-one/93-enum.c @@ -0,0 +1,7 @@ +// PARAM: --disable ana.int.interval_set --disable ana.int.def_exc --enable ana.int.enums +void main(){ + int n = 1; + for (; n; n++) { // fixed point not reached here + } + return; +} diff --git a/tests/regression/66-interval-set-one/94-widen-dependent.c b/tests/regression/66-interval-set-one/94-widen-dependent.c new file mode 100644 index 0000000000..0bbe2fa8f5 --- /dev/null +++ b/tests/regression/66-interval-set-one/94-widen-dependent.c @@ -0,0 +1,38 @@ +// PARAM: --enable ana.int.interval_set --enable exp.priv-distr-init +#include +#include + +// protection priv succeeds +// write fails due to [1,1] widen [0,1] -> [-inf,1] +// sensitive to eval and widen order! + +int g = 0; + +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *worker(void *arg ) +{ + pthread_mutex_lock(&A); + while (g <= 0) { + + } + __goblint_check(g > 0); // precise privatization fails + g--; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(int argc , char **argv ) +{ + pthread_t tid; + pthread_create(& tid, NULL, & worker, NULL); + + pthread_mutex_lock(&A); + while (g >= 10) { + + } + __goblint_check(g >= 0); // precise privatization fails + g++; + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/66-interval-set-one/95-large_arrays-nocalloc.c b/tests/regression/66-interval-set-one/95-large_arrays-nocalloc.c new file mode 100644 index 0000000000..ad1476a6ba --- /dev/null +++ b/tests/regression/66-interval-set-one/95-large_arrays-nocalloc.c @@ -0,0 +1,41 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include +#include +#include +#include +#include + +// Test to check whether partitioned arrays can have an index expression evaluating to values largers than the max value of int64 + +#define LENGTH (LONG_MAX - 600) +#define STOP (LENGTH - 1) + +int main(){ + // Check that ptrdiff_t is at least as big as long, so we can index arrays with non-negative longs + __goblint_check(sizeof(ptrdiff_t) >= sizeof(long)); + + char arr[LENGTH]; + + for(unsigned long i = 0; i < STOP; i++){ + arr[i] = 1; + } + + // arr[0] ... arr[STOP - 1] should be 1, the others equal to 0 + __goblint_check(arr[0] == 1); + __goblint_check(arr[INT_MAX + 1l] == 1); + + // j is the smallest index where checking it used to yield an unsound value + // long j = ((long) INT_MAX) * INT_MAX * 2 + INT_MAX - 1; + long j = LONG_MAX - 6442450943; + __goblint_check(0 < j); + __goblint_check(j < STOP); + + __goblint_check(arr[j - 1] == 1); + + __goblint_check(arr[j] == 1); + __goblint_check(arr[STOP - 1] == 1); + + __goblint_check(arr[STOP] == 0); //UNKNOWN! + __goblint_check(arr[LENGTH - 1] == 0); //UNKNOWN! + return 0; +} diff --git a/tests/regression/66-interval-set-one/96-more_passing.c b/tests/regression/66-interval-set-one/96-more_passing.c new file mode 100644 index 0000000000..5dbf6c6d54 --- /dev/null +++ b/tests/regression/66-interval-set-one/96-more_passing.c @@ -0,0 +1,75 @@ +//PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +#include +#include + +void foo(int n, int a[n]) { + int x = a[7]; + __goblint_check(x == 42); +} + +void fooo(int n, int a[n][n]) { + __goblint_check(a[29][7] == 42); + int *ptr = a[29]; + int x = *(ptr+7); + printf("x is %d", x); + __goblint_check(x == 42); +} + +void foo2(int n, int a[50][n]) { + __goblint_check(a[29][7] == 42); + __goblint_check(a[29][7] == 0); //FAIL +} + +// This is quite ugly, but valid C99 +void foo3(int n, int b[n], int a[n][b[0]]) { + __goblint_check(a[29][7] == 42); +} + +void foo4(int n, int m, int r, int a[n][m][r]) { + __goblint_check(a[3][3][2] == 42); +} + +int main(void) +{ + // One-dimensional arrays + int a[40]; + a[7] = 42; + foo(40, a); + + int x; + + if(x < 8) { + x = 347; + } + + int b[x]; + b[7] = 42; + + foo(x, b); + + //Two dimensional arrays + int c[50][50]; + + for(int i = 0; i < 50;i++) { + for(int j=0;j < 50;j++) { + c[i][j] = 0; + } + } + + c[29][7] = 42; + + foo2(50,c); + fooo(50, c); + + int x[50]; + b[0] = 50; + foo3(50, x, c); + + int n = 15; + int m = 47; + int r = 11; + + int d[n][m][r]; + d[3][3][2] = 42; + foo4(n,m,r,d); +} diff --git a/tests/regression/66-interval-set-one/97-casts.c b/tests/regression/66-interval-set-one/97-casts.c new file mode 100644 index 0000000000..95b2026abe --- /dev/null +++ b/tests/regression/66-interval-set-one/97-casts.c @@ -0,0 +1,109 @@ +// PARAM: --enable ana.int.interval_set --enable ana.float.interval +#include + +#define RANGE(val, min, max) \ + if (rnd) \ + { \ + val = min; \ + } \ + else \ + { \ + val = max; \ + } + +int main() +{ + int rnd; + + double value; + float value2; + long double value3; + int i; + long l; + unsigned u; + + // Casts from double/float/long double into different variants of ints + __goblint_check((int)0.0); // FAIL + __goblint_check((long)0.0); // FAIL + __goblint_check((unsigned)0.0); // FAIL + __goblint_check((int)0.0f); // FAIL + __goblint_check((long)0.0f); // FAIL + __goblint_check((unsigned)0.0f); // FAIL + __goblint_check((int)0.0l); // FAIL + __goblint_check((long)0.0l); // FAIL + __goblint_check((unsigned)0.0l); // FAIL + + __goblint_check((unsigned)1.0); // SUCCESS + __goblint_check((long)2.0); // SUCCESS + __goblint_check((int)3.0); // SUCCESS + __goblint_check((unsigned)1.0f); // SUCCESS + __goblint_check((long)2.0f); // SUCCESS + __goblint_check((int)3.0f); // SUCCESS + __goblint_check((unsigned)1.0l); // SUCCESS + __goblint_check((long)2.0l); // SUCCESS + __goblint_check((int)3.0l); // SUCCESS + + // Cast from int into double/float/long double + __goblint_check((double)0); // FAIL + __goblint_check((double)0l); // FAIL + __goblint_check((double)0u); // FAIL + + __goblint_check((double)1u); // SUCCESS + __goblint_check((double)2l); // SUCCESS + __goblint_check((double)3); // SUCCESS + + __goblint_check((float)0); // FAIL + __goblint_check((float)0l); // FAIL + __goblint_check((float)0u); // FAIL + + __goblint_check((float)1u); // SUCCESS + __goblint_check((float)2l); // SUCCESS + __goblint_check((float)3); // SUCCESS + + __goblint_check((long double)0); // FAIL + __goblint_check((long double)0l); // FAIL + __goblint_check((long double)0u); // FAIL + + __goblint_check((long double)1u); // SUCCESS + __goblint_check((long double)2l); // SUCCESS + __goblint_check((long double)3); // SUCCESS + + // cast with ranges + RANGE(i, -5, 5); + value = (double)i; + __goblint_check(-5. <= value && value <= 5.f); // SUCCESS + value2 = (float)i; + __goblint_check(-5.f <= value2 && value2 <= 5.); // SUCCESS + value3 = (long double)i; + __goblint_check(-5.f <= value3 && value3 <= 5.l); // SUCCESS + + RANGE(l, 10, 20); + value = l; + __goblint_check(10.f <= value && value <= 20.); // SUCCESS + value2 = l; + __goblint_check(10.l <= value2 && value2 <= 20.f); // SUCCESS + value3 = l; + __goblint_check(10. <= value2 && value2 <= 20.); // SUCCESS + + RANGE(u, 100, 1000); + value = u; + __goblint_check(value > 1.); // SUCCESS + value2 = u; + __goblint_check(value2 > 1.f); // SUCCESS + value3 = u; + __goblint_check(value2 > 1.l); // SUCCESS + + RANGE(value, -10.f, 10.); + i = (int)value; + __goblint_check(-10 <= i && i <= 10); // SUCCESS + + RANGE(value2, -10.f, 10.); + i = (int)value2; + __goblint_check(-10 <= i && i <= 10); // SUCCESS + + RANGE(value3, -10.l, 10.); + i = (int)value3; + __goblint_check(-10 <= i && i <= 10); // SUCCESS + + return 0; +} diff --git a/tests/regression/66-interval-set-one/98-widen-dependent-local.c b/tests/regression/66-interval-set-one/98-widen-dependent-local.c new file mode 100644 index 0000000000..a5661256c0 --- /dev/null +++ b/tests/regression/66-interval-set-one/98-widen-dependent-local.c @@ -0,0 +1,45 @@ +// PARAM: --enable ana.int.interval_set --enable exp.priv-distr-init +extern int __VERIFIER_nondet_int(); + +#include +#include + +// protection priv succeeds +// write fails due to [1,+inf] widen ([1,+inf] join [0,+inf]) -> [-inf,+inf] +// sensitive to eval and widen order! + +int g = 0; +int limit; // unknown + +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *worker(void *arg ) +{ + // just for going to multithreaded mode + return NULL; +} + +int put() { + pthread_mutex_lock(&A); + while (g >= limit) { // problematic widen + + } + __goblint_check(g >= 0); // precise privatization fails + g++; + pthread_mutex_unlock(&A); +} + +int main(int argc , char **argv ) +{ + pthread_t tid; + pthread_create(& tid, NULL, & worker, NULL); + + int r = __VERIFIER_nondet_int(); + limit = r; // only problematic if limit unknown + + while (1) { + // only problematic if not inlined + put(); + } + return 0; +} diff --git a/tests/regression/66-interval-set-one/99-off.c b/tests/regression/66-interval-set-one/99-off.c new file mode 100644 index 0000000000..337616b9f2 --- /dev/null +++ b/tests/regression/66-interval-set-one/99-off.c @@ -0,0 +1,13 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --disable exp.fast_global_inits +// This checks that partitioned arrays and fast_global_inits are no longer incompatible +#include + +int global_array[50]; +int global_array_multi[50][2][2]; + +int main(void) { + for(int i =0; i < 50; i++) { + __goblint_check(global_array[i] == 0); + __goblint_check(global_array_multi[i][1][1] == 0); + } +} diff --git a/tests/regression/67-interval-sets-two/00-large_arrays.c b/tests/regression/67-interval-sets-two/00-large_arrays.c new file mode 100644 index 0000000000..85a1ea59c2 --- /dev/null +++ b/tests/regression/67-interval-sets-two/00-large_arrays.c @@ -0,0 +1,47 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include +#include +#include +#include +#include + +// Test to check whether partitioned arrays can have an index expression evaluating to values larger than the max value of int64 + +#define LENGTH (LONG_MAX - 600) +#define STOP (LENGTH - 1) + +int main(){ + // Check that ptrdiff_t is at least as big as long, so we can index arrays with non-negative longs + __goblint_check(sizeof(ptrdiff_t) >= sizeof(long)); + + char *arr = calloc(LENGTH, sizeof(char)); + if(arr == NULL){ + printf("Could not allocate array, exiting.\n"); + return 1; + } + + for(unsigned long i = 0; i < STOP; i++){ + arr[i] = 1; + } + + // arr[0] ... arr[STOP - 1] should be 1, the others equal to 0 + __goblint_check(arr[0] == 1); // UNKNOWN + __goblint_check(arr[INT_MAX + 1l] == 1); //UNKNOWN + + // j is the smallest index where checking it used to yield an unsound value + // long j = ((long) INT_MAX) * INT_MAX * 2 + INT_MAX - 1; + long j = LONG_MAX - 6442450943; + __goblint_check(0 < j); + __goblint_check(j < STOP); + + // This check is imprecise, but not unsound + __goblint_check(arr[j - 1] == 1); //UNKNOWN + + // These two asserts used to fail somehow + __goblint_check(arr[j] == 1); //UNKNOWN + __goblint_check(arr[STOP - 1] == 1); //UNKNOWN + + __goblint_check(arr[STOP] == 0); //UNKNOWN + __goblint_check(arr[LENGTH - 1] == 0); //UNKNOWN + return 0; +} diff --git a/tests/regression/67-interval-sets-two/01-array-out-of-bounds.c b/tests/regression/67-interval-sets-two/01-array-out-of-bounds.c new file mode 100644 index 0000000000..5ce4e65da6 --- /dev/null +++ b/tests/regression/67-interval-sets-two/01-array-out-of-bounds.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.arrayoob --enable ana.int.interval_set --enable ana.int.enums +#include +//This is the most basic case +int main() +{ + int arr[] = {1, 2, 3, 4, 5, 6}; + arr[2] = 0; //NOWARN + arr[6] = 10; //WARN + arr[-1] = 10; //WARN + + for (int i = 0; i < 10; ++i) + { + arr[i] = 5; //WARN + } + return 0; +} diff --git a/tests/regression/67-interval-sets-two/02-pointers_array.c b/tests/regression/67-interval-sets-two/02-pointers_array.c new file mode 100644 index 0000000000..34013210a3 --- /dev/null +++ b/tests/regression/67-interval-sets-two/02-pointers_array.c @@ -0,0 +1,193 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" --enable annotation.int.enabled --set ana.int.refinement fixpoint +#include + +int main(void) __attribute__((goblint_precision("no-interval"))); +void example1(void) __attribute__((goblint_precision("no-def_exc"))); +void example2() __attribute__((goblint_precision("no-def_exc"))); +void example3(void) __attribute__((goblint_precision("no-def_exc"))); +void example4(void) __attribute__((goblint_precision("no-def_exc"))); +void example5(void) __attribute__((goblint_precision("no-def_exc"))); +void example6(void) __attribute__((goblint_precision("no-def_exc"))); +void example7(void) __attribute__((goblint_precision("no-def_exc"))); + + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + return 0; +} + +// Initializing an array with pointers +void example1(void) { + int top; + + int a[42]; + int *ptr = &a; + + *ptr = 42; + ptr++; + + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); // UNKNOWN + + *ptr = 42; + __goblint_check(a[0] == 42); + __goblint_check(a[1] == 42); + ptr++; + + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + *ptr = 42; + ptr++; + + + int i = 5; + __goblint_check(a[i] == 42); + + if(top) { + i++; + } + + __goblint_check(a[i] == 42); // UNKNOWN +} + +// Tests correct handling when pointers may point to several different things +void example2() { + int array1[10000000]; + int array2[10000000]; + + int* ptr; + + if(rand()) { + ptr = &array1; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + else { + ptr = &array2; + *ptr = 5; + + __goblint_check(*ptr == 5); + } + + // Since ptr could point to different arrays, the update here can not be precise + *ptr = 6; + + __goblint_check(*ptr == 6); // UNKNOWN +} + +void example3(void) { + int array1[5]; + int *ptr = &array1; + + for(int i =0; i <5; i++) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } +} + +void example4(void) { + int array1[5]; + int *ptr = &array1; + int *end = &(array1[5]); + + while(ptr <= end) { + *ptr = 42; + __goblint_check(*ptr == 42); + ptr++; + } + + // In an ideal world, I would like to have information about array1[0] and so on. For this the <= would need yo improve +} + +void example5(void) { + int array1[5]; + int *ptr = &(array1[4]); + + *ptr = 42; + ptr--; + *ptr = 42; + ptr--; + *ptr = 40; + + __goblint_check(*ptr == 40); + __goblint_check(array1[4] == 42); + __goblint_check(array1[3] == 42); + __goblint_check(array1[2] == 40); + __goblint_check(array1[0] == 42); // UNKNOWN +} + +void example6(void) { + int array1[100]; + int* ptr = &array1; + + *ptr = 5; + int v = *ptr; + __goblint_check(v == 5); + + ptr++; + *ptr = 6; + ptr++; + *ptr = 7; + + // This is necessary for the tests that we are doing later + int k = ptr-&array1; + __goblint_check(k == 2); + int m = ptr-array1; + __goblint_check(m == 2); + + int* np = &array1; + np++; + np++; + int x = *np; + __goblint_check(x==7); +} + +void example7(void) { + int top; + + int arr1[42]; + int arr2[42]; + int *ptr; + + for(int i = 0; i < 42; i++) { + arr1[i] = 4; + arr2[i] = 4; + } + + ptr = &arr1[7]; + + if(top) { + ptr = &arr2[7]; + } + + *ptr = 9; + + // Case ptr = &arr1[7] + // arr1 -> (ptr-arr1, ([4,4], [9,9],[4,4])) + // arr2 -> (-,[4,4]) + + // Case ptr = &arr2[7] + // arr1 -> (-, [4,4]) + // arr2 -> (ptr-arr2, ([4,4], [9,9],[4,4])) + + // LUB: + // arr1 -> (-, [4,9]) + // arr2 -> (-, [4,9]) + int x = arr1[7]; + __goblint_check(x == 3); // FAIL + __goblint_check(x == 4); // UNKNOWN + __goblint_check(x == 9); // UNKNOWN + __goblint_check(x == 10); // FAIL +} diff --git a/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c new file mode 100644 index 0000000000..8d56fa0e3c --- /dev/null +++ b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c @@ -0,0 +1,23 @@ +// PARAM: --enable ana.int.def_exc --enable ana.int.interval_set --enable ana.sv-comp.functions --set sem.int.signed_overflow assume_none --set ana.int.refinement never +// used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) +// manually minimized from sv-benchmarks/c/recursive/MultCommutative-2.c +extern int __VERIFIER_nondet_int(void); + +void f(int m) { + if (m < 0) { + f(-m); + } + if (m == 0) { + return; + } + f(m - 1); +} + +int main() { + int m = __VERIFIER_nondet_int(); + if (m < 0 || m > 1) { + return 0; + } + f(m); // m=[0,1] + return 0; +} diff --git a/tests/regression/67-interval-sets-two/04-unsupported.c b/tests/regression/67-interval-sets-two/04-unsupported.c new file mode 100644 index 0000000000..97ce11258a --- /dev/null +++ b/tests/regression/67-interval-sets-two/04-unsupported.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval_set --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned + +// This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +int main(void) { + callok(); +} + +struct blub +{ + int blubby; +}; + +struct ms +{ + struct blub b; + int x; + int y; + int z; +}; + + +void fun(int *ptr) { + *ptr++; +} + +void callok(void) { + struct ms *ptr = kzalloc(sizeof(struct ms)); + + fun(&ptr->b.blubby); + + ptr->b.blubby = 8; + + ptr->x = 47; + ptr->y = 11; + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/05-tid-toy11.c b/tests/regression/67-interval-sets-two/05-tid-toy11.c new file mode 100644 index 0000000000..3dee221f7d --- /dev/null +++ b/tests/regression/67-interval-sets-two/05-tid-toy11.c @@ -0,0 +1,78 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins +// Inspired by 36/81 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +pthread_t other_t; + +void *t_fun(void *arg) { + int x = 10; + int y; + + pthread_mutex_lock(&A); + g = x; + h = y; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = x; + h = x; + pthread_mutex_unlock(&A); + return NULL; +} + +void *t_benign(void *arg) { + // Without this, it would even succeed without the must joined analysis. + // With it, that is required! + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_create(&other_t, NULL, t_fun, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + g = 10; + h = 10; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t; + + pthread_mutex_lock(&A); + g = 12; + h = 14; + pthread_mutex_unlock(&A); + + // Force multi-threaded handling + pthread_t id2; + pthread_create(&id2, NULL, t_benign, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); //UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_join(id2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(g == h); // UNKNOWN! + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + pthread_join(other_t, NULL); + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == h); + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/06-no-int-context.c b/tests/regression/67-interval-sets-two/06-no-int-context.c new file mode 100644 index 0000000000..d4ff46b0b7 --- /dev/null +++ b/tests/regression/67-interval-sets-two/06-no-int-context.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen --disable ana.base.context.int +#include + +int f(int x) { + if (x) + return f(x+1); + else + return x; +} + +int main () { + int a = f(1); + __goblint_check(!a); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/07-var_eq.c b/tests/regression/67-interval-sets-two/07-var_eq.c new file mode 100644 index 0000000000..6e7eedc3bb --- /dev/null +++ b/tests/regression/67-interval-sets-two/07-var_eq.c @@ -0,0 +1,31 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +#include + +int global; + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + example9(); + example10(); + return 0; +} + +// Simple example +void example1(void) +{ + int top; + int top2; + int arr[10]; + + arr[top] = 42; + top2 = top; + __goblint_check(arr[top2] == 42); +} diff --git a/tests/regression/67-interval-sets-two/08-nested-unroll.c b/tests/regression/67-interval-sets-two/08-nested-unroll.c new file mode 100644 index 0000000000..2e5051a652 --- /dev/null +++ b/tests/regression/67-interval-sets-two/08-nested-unroll.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval_set --set exp.unrolling-factor 5 --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 +#include +int main(void) { + int arr[10][10]; + + for(int i=0;i<10; i++) { + for(int j=0;j <10; j++) { + arr[i][j] = i+j; + } + } + + for(int i=0;i<5; i++) { + for(int j=0;j <5; j++) { + __goblint_check(arr[i][j] == i+j); + } + } +} diff --git a/tests/regression/67-interval-sets-two/09-mm-reentrant.c b/tests/regression/67-interval-sets-two/09-mm-reentrant.c new file mode 100644 index 0000000000..87f9419fd5 --- /dev/null +++ b/tests/regression/67-interval-sets-two/09-mm-reentrant.c @@ -0,0 +1,59 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.privatization mutex-meet --disable sem.unknown_function.invalidate.globals --disable sem.unknown_function.spawn +#include +#include +#include +#include +#include +#include + +pthread_mutex_t mt; +int i = 0; + +void* fn1(void* agr) +{ + int top = rand(); + + pthread_mutex_lock(&mt); + if(top) { + i = 5; + } + pthread_mutex_lock(&mt); + __goblint_check(i == 0); //UNKNOWN! + i = 0; + pthread_mutex_unlock(&mt); + pthread_mutex_unlock(&mt); +} + +void* fn2(void* agr) +{ + int top = rand(); + + pthread_mutex_lock(&mt); + if(top) { + i = 5; + } + top = pthread_mutex_lock(&mt); + __goblint_check(i == 0); //UNKNOWN! + i = 0; + pthread_mutex_unlock(&mt); + pthread_mutex_unlock(&mt); +} + + +int main() +{ + pthread_t tid1; + pthread_t tid2; + + pthread_mutexattr_t mat; + pthread_mutexattr_init(&mat); + + //The type of lock set is recursive + pthread_mutexattr_settype(&mat, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mt, &mat); + + pthread_create(&tid1, NULL, fn1, NULL); + pthread_create(&tid2, NULL, fn2, NULL); + + pthread_join(tid1,NULL); +} diff --git a/tests/regression/67-interval-sets-two/10-cast-return-void-ptr.c b/tests/regression/67-interval-sets-two/10-cast-return-void-ptr.c new file mode 100644 index 0000000000..c93bde44d1 --- /dev/null +++ b/tests/regression/67-interval-sets-two/10-cast-return-void-ptr.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.interval_set --set sem.int.signed_overflow assume_none +#include + +int empty() { + return -1; // return shouldn't cast to void* generally, but just for thread return +} + +int main(void) { + if (!empty()==-1) { // if -1 is cast to void*, it makes both branches dead! + __goblint_check(1); // NOWARN (unreachable) + } + + __goblint_check(1); // reachable + return 0; +} diff --git a/tests/regression/67-interval-sets-two/13-loop.c b/tests/regression/67-interval-sets-two/13-loop.c new file mode 100644 index 0000000000..8df2802574 --- /dev/null +++ b/tests/regression/67-interval-sets-two/13-loop.c @@ -0,0 +1,62 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +#include + +int main(void) +{ + example1(); + example2(); + example3(); + example4(); +} + +void example1(void) { + for(int i=1;i<10;i++) { + int a[i]; + a[i-1] = 0; + __goblint_check(a[i-1] == 0); + } +} + +void example2(void) { + for(int i=0; i < 47; i++) { + int a[i+2]; + + for(int j = 0; j < 2; j++) { + a[j] = 0; + } + + __goblint_check(a[0] == 0); + } +} + +void example3(void) { + for(int i = 2; i < 47; i++) { + int n = 1; + int a[1]; + + if(i == 2) { + a[0] = 42; + } + + __goblint_check(a[0] == 42); //UNKNOWN + } +} + +void example4(void) { + int top; + int l = 5; + + if(top) { + l = 6; + } + + int a[l]; + + for(int i=0; i < l-1; i++) { + a[i] = 42; + } + + for(int i=0; i < 4; i++) { + __goblint_check(a[i] == 42); + } +} diff --git a/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c new file mode 100644 index 0000000000..af46023135 --- /dev/null +++ b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c @@ -0,0 +1,25 @@ +// PARAM: --enable ana.int.interval_set --set solver slr3t +#include + +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +long counter = 0; + +void *counter_thread (void *arg) { + int tmp = counter; + int i = 0; + pthread_mutex_lock (&mutex); + while(i<5){ + tmp = counter; + tmp++; + counter = tmp; + i++; + } + pthread_mutex_unlock (&mutex); + tmp = 1; +} + +int main (int argc, char *argv[]) { + pthread_t counter_thread_id; + pthread_create (&counter_thread_id, NULL, counter_thread, NULL); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/15-interval-bot.c b/tests/regression/67-interval-sets-two/15-interval-bot.c new file mode 100644 index 0000000000..ab7d043b92 --- /dev/null +++ b/tests/regression/67-interval-sets-two/15-interval-bot.c @@ -0,0 +1,11 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc + +int main(){ + + unsigned long long a ; + unsigned long long addr; + + if(a + addr > 0x0ffffffffULL){ + return 1; + } +} diff --git a/tests/regression/67-interval-sets-two/16-branched-thread-creation.c b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c new file mode 100644 index 0000000000..c309ec8ffd --- /dev/null +++ b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c @@ -0,0 +1,50 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[-] threadJoins +// Inspired by 36/86 +#include +#include +#include + +int g; +int h; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int t = 4; + + pthread_mutex_lock(&mutex); + g=t; + h=t; + pthread_mutex_unlock(&mutex); + return NULL; +} + + +int main(void) { + int top; + int mt = 0; + + if(top) { + + g = 8; + h = 7; + + } else { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&mutex); + g=top; + h=top; + pthread_mutex_unlock(&mutex); + mt=1; + } + + if(!mt) { + pthread_mutex_lock(&mutex); + __goblint_check(g==h); //MAYFAIL + pthread_mutex_unlock(&mutex); + } + + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/17-intervals-branching-meet-keyed.c b/tests/regression/67-interval-sets-two/17-intervals-branching-meet-keyed.c new file mode 100644 index 0000000000..8f37662abf --- /dev/null +++ b/tests/regression/67-interval-sets-two/17-intervals-branching-meet-keyed.c @@ -0,0 +1,42 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.structs.domain "keyed" + +#include + +struct Pair { + int first; + int second; +}; + +void example1() { + int a; + int b; + + struct Pair pair; + + if (a) { + pair.first = 10; + pair.second = 20; + } else { + pair.first = 20; + pair.second = 30; + } + + if (pair.first == 15) { + // This should be unreachable! + b = 0; // This line is not dead if we --disable ana.base.structs.meet-condition + } else if (pair.first == 10) { + __goblint_check(pair.second == 20); + b = 1; + } else if (pair.first == 20) { + __goblint_check(pair.second == 30); + b = 1; + } + __goblint_check(b == 1); +} + + +int main() { + example1(); + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/18-adapted_from_01_09_array.c b/tests/regression/67-interval-sets-two/18-adapted_from_01_09_array.c new file mode 100644 index 0000000000..9e95421320 --- /dev/null +++ b/tests/regression/67-interval-sets-two/18-adapted_from_01_09_array.c @@ -0,0 +1,122 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include +#include + +int fun_5() { return 5; } +int fun_6() { return 6; } +int fun_5b() { return 5; } + +struct kala { + int a[5]; +}; + +struct kass { + int v; +}; + +int main () { + int i,t, k1,k2,top; + + int a[] = {2,2,2}; + int b[2], c[3]; + int (*f[2])() = {fun_5, fun_6}; + int (*g[2])() = {fun_5, fun_5b}; + int (*fp)(); + int *ip; + int (*iap)[]; + + // really really top + if (i) top = (int) ⊤ + else top = 5; + + __goblint_check(a[0] == 2); + __goblint_check(a[1] == 2); + __goblint_check(a[2] == 2); + + // writing to unknown index: + // NB! We assume the index is in bounds! + if (k1) i=0; else i=1; + a[i] = 0; + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[1] == 0); // UNKNOWN + __goblint_check(a[2] == 0); // FAIL + + // reading from unknown index: + b[0] = 2; b[1] = 2; + __goblint_check(b[i] == 2); + b[0] = 3; + __goblint_check(b[i] == 2); // UNKNOWN + + // function arrays + t = f[i](); + __goblint_check(t == 5); // UNKNOWN + t = g[i](); + __goblint_check(t == 5); + + // array has set of addresses: + if (k2) f[i] = fun_5b; + t = f[1](); + __goblint_check(t == 5); // UNKNOWN + + // now we collect all the sets: + fp = f[i]; + t = fp(); + __goblint_check(t == 5); // UNKNOWN + fp = g[i]; + t = fp(); + __goblint_check(t == 5); + + // NASTY ARRAY OPS: + c[0] = 5; c[1] = 5; c[2] = 5; + // this is not usual: a pointer to an array (easy!) + iap = &c; + t = (*iap)[2]; + __goblint_check(t == 5); + + // Typical C: a pointer to first element of array (difficult!) + ip = c; // this means &c[0] + + // dereferencing... + __goblint_check(*ip == 5); + + // pointing into the array + ip = &c[1]; + __goblint_check(*ip == 5); + + // and some pointer arithmetic (tests are meaningless) + *ip = 6; + ip++; + __goblint_check(*ip == 5); + + // Now testing arrays inside structs. + struct kala x; + ip = x.a; + x.a[0] = 7; + __goblint_check(*ip == 7); + + // (typeless) Top index + __goblint_check(x.a[top] == 7); // UNKNOWN + + // And finally array of structs + struct kala xs[5]; + xs[0] = x; + ip = &xs[0].a[0]; + + struct kass k[1]; + k[0].v = 42; + __goblint_check(k[0].v == 42); + + // multi-dim arrays + int ma[1][1]; + ma[0][0] = 42; + __goblint_check(ma[0][0] == 42); + + //i = hash("kala"); + //printf("Hash value: %d", i); + + // NB arrays must be in bounds... otherwise everything fails! + // It's not possible to analyze this: + // a[3] = 666; + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/19-arrays-within-structures.c b/tests/regression/67-interval-sets-two/19-arrays-within-structures.c new file mode 100644 index 0000000000..2c6698fc60 --- /dev/null +++ b/tests/regression/67-interval-sets-two/19-arrays-within-structures.c @@ -0,0 +1,28 @@ +// PARAM: --enable ana.arrayoob --enable ana.int.interval_set --enable ana.int.interval_set --enable ana.int.enums +// Arrays within structures. Source of sample struct: +// https://codeforwin.org/2018/07/how-to-declare-initialize-and-access-array-of-structure.html +#include +int main() { + struct student { + char name[100]; + int roll; + float marks; + }; + // Structure array declaration + struct student stu[] = { + {"vandah", 12, 89.5f}, + {"edin", 15, 98.0f}, + {"david", 17, 90.0f}, + }; + stu[0].roll = 2; //NOWARN + stu[-1].roll = 10; // WARN + stu[1].marks = 90.5f; //NOWARN + stu[20].marks = 89.5f; //WARN + for (int i = 0; i < 3; ++i) { + stu[i].roll = 5; // NOWARN + } + for (int i = 0; i < 10; ++i) { + stu[i].roll = 5; // WARN + } + return 0; +} diff --git a/tests/regression/67-interval-sets-two/20-no-loc.c b/tests/regression/67-interval-sets-two/20-no-loc.c new file mode 100644 index 0000000000..cdb21b6424 --- /dev/null +++ b/tests/regression/67-interval-sets-two/20-no-loc.c @@ -0,0 +1,71 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins --disable ana.thread.include-node +// Inspired by 36/97 +#include +#include + +int g = 10; +int h = 10; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_benign(void *arg) { + pthread_mutex_lock(&A); + g = 20; + pthread_mutex_unlock(&A); + return NULL; +} + +void *u_benign(void *arg) { + pthread_mutex_lock(&A); + h = 20; + pthread_mutex_unlock(&A); + return NULL; +} + +int main(void) { + int t; + + pthread_t id; + + if(t) { + pthread_create(&id, NULL, t_benign, NULL); + } else { + pthread_create(&id, NULL, t_benign, NULL); + } + + // As these two threads are not distinguished, we have a unique TID for id + pthread_join(id, NULL); + + + pthread_mutex_lock(&A); + g = 12; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == 12); + pthread_mutex_unlock(&A); + +// --------------------------------------------------------------------------- + + pthread_t id2; + pthread_t id3; + + + pthread_create(&id2, NULL, u_benign, NULL); + pthread_create(&id3, NULL, u_benign, NULL); + + pthread_join(id2, NULL); + + // As these two threads are not distinguished, id3 is a not unique thread + pthread_join(id3, NULL); + + + pthread_mutex_lock(&A); + h = 12; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(h == 12); //TODO + pthread_mutex_unlock(&A); + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/21-thread_ret.c b/tests/regression/67-interval-sets-two/21-thread_ret.c new file mode 100644 index 0000000000..2a4583528e --- /dev/null +++ b/tests/regression/67-interval-sets-two/21-thread_ret.c @@ -0,0 +1,34 @@ +//PARAM: --enable ana.int.interval_set --set ana.activated[-] threadreturn + +#include +#include +#include +#include +#include + +int myglobal; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + + +int f() { + return 1; +} + +void *t_fun(void *arg) { + myglobal=f(); + return NULL; +} + +int main(void) { + pthread_t id; + void *ptr; + void **pptr = &ptr; + pthread_create(&id, NULL, t_fun, NULL); + pthread_join (id, pptr); + int v = *((int*) pptr); + // If we don't have the threadreturn analysis running, all returns from all functions called by the t_fun thread, as well as of t_fun itself are joined together + // But we still should get a value better than top! + __goblint_check(v!=2); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/23-testfive-intervals.c b/tests/regression/67-interval-sets-two/23-testfive-intervals.c new file mode 100644 index 0000000000..b5f9f49fc5 --- /dev/null +++ b/tests/regression/67-interval-sets-two/23-testfive-intervals.c @@ -0,0 +1,33 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.privatization mutex-meet --set solvers.td3.side_widen sides-pp +#include +#include + +int myglobal; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void lock() { + pthread_mutex_lock(&mutex); +} + +void unlock() { + pthread_mutex_unlock(&mutex); +} + + +void *t_fun(void *arg) { + lock(); + myglobal++; // NORACE + unlock(); + return NULL; +} + + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + lock(); + myglobal++; // NORACE + unlock(); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/24-arithmetic-bot.c b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c new file mode 100644 index 0000000000..f238788bb3 --- /dev/null +++ b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c @@ -0,0 +1,40 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +// from: ldv-linux-3.0/usb_urb-drivers-vhost-vhost_net.ko.cil.out.i +typedef unsigned long long u64; + +int main( ) +{ u64 a ; + unsigned long flag ; + unsigned long roksum ; + struct thread_info *tmp___7 ; + int tmp___8 ; + long tmp___9; + void *log_base; + unsigned long sz; + u64 addr; + { + a = (addr / 4096ULL) / 8ULL; + if (a > (u64 )(0x0fffffffffffffffUL - (unsigned long )log_base)) { + return (0); + } else { + if (a + (u64 )((unsigned long )log_base) > 0x0fffffffffffffffULL) { + return (0); + } else { + } + } + { + tmp___7 = current_thread_info(); + // __asm__ ("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0": "=&r" (flag), "=r" (roksum): "1" (log_base + a), + // "g" ((long )((((sz + 32768UL) - 1UL) / 4096UL) / 8UL)), "rm" (tmp___7->addr_limit.seg)); + } + if (flag == 0UL) { + tmp___8 = 1; + } else { + tmp___8 = 0; + } + { + tmp___9 = __builtin_expect((long )tmp___8, 1L); + } + return ((int )tmp___9); + } +} diff --git a/tests/regression/67-interval-sets-two/25-mine14.c b/tests/regression/67-interval-sets-two/25-mine14.c new file mode 100644 index 0000000000..55596fbb4c --- /dev/null +++ b/tests/regression/67-interval-sets-two/25-mine14.c @@ -0,0 +1,35 @@ +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --enable ana.int.interval_set --set ana.activated[+] threadJoins --enable ana.int.interval_threshold_widening +// Fig 5a from MinĂ© 2014 +#include +#include +#include + +int x; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int top; + while(top) { + pthread_mutex_lock(&mutex); + if(x<100) { + x++; + } + pthread_mutex_unlock(&mutex); + } + return NULL; +} + + +int main(void) { + int top, top2; + + + pthread_t id; + pthread_t id2; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id2, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex); + __goblint_check(x <= 100); + pthread_mutex_unlock(&mutex); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/27-nested2.c b/tests/regression/67-interval-sets-two/27-nested2.c new file mode 100644 index 0000000000..516fcdb381 --- /dev/null +++ b/tests/regression/67-interval-sets-two/27-nested2.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval_set --set solver td3 --set sem.int.signed_overflow assume_none +// ALSO: --enable ana.int.interval_set --set solver slr3 --set sem.int.signed_overflow assume_none +// Example from Amato-Scozzari, SAS 2013 +// Localized narrowing should be able to prove that i >= 0 in the outer loop. +#include + +void main() +{ + int i = 0; + while (1) { + int j = 0; + for (; j<10; j++) ; + i=i+11-j; + __goblint_check(i >= 0); + } + return; +} diff --git a/tests/regression/67-interval-sets-two/28-multidimensional_arrays.c b/tests/regression/67-interval-sets-two/28-multidimensional_arrays.c new file mode 100644 index 0000000000..06f1614232 --- /dev/null +++ b/tests/regression/67-interval-sets-two/28-multidimensional_arrays.c @@ -0,0 +1,63 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +int main(void) { + example1(); + example2(); + return 0; +} + +// Two-dimensional array +void example1(void) { + int a[10][10]; + int i=0; + int j=0; + + while(i < 9) { + + j = 0; + + while(j < 10) { + a[i][j] = 42; + j++; + } + + __goblint_check(a[i][0] == 42); + __goblint_check(a[i][9] == 42); + __goblint_check(a[3][9] == 42); // UNKNOWN + + i++; + } + + __goblint_check(a[0][0] == 42); + __goblint_check(a[2][5] == 42); + __goblint_check(a[8][9] == 42); + __goblint_check(a[3][7] == 42); + __goblint_check(a[9][9] == 42); // UNKNOWN + __goblint_check(a[9][2] == 42); // UNKNOWN +} + +// Combines backwards- and forwards-iteration +void example2(void) { + int array[10][10]; + int i = 9; + + while(i >= 0) { + int j =0; + + while(j < 10) { + array[i][j] = 4711; + __goblint_check(array[i-1][j+1] == 4711); //UNKNOWN + j++; + } + + i--; + } + + __goblint_check(array[2][3] == 4711); + __goblint_check(array[0][9] == 4711); + __goblint_check(array[8][5] == 4711); + __goblint_check(array[2][1] == 4711); + __goblint_check(array[0][0] == 4711); + __goblint_check(array[7][5] == 4711); +} diff --git a/tests/regression/67-interval-sets-two/29-def-exc.c b/tests/regression/67-interval-sets-two/29-def-exc.c new file mode 100644 index 0000000000..b26a4cf8fe --- /dev/null +++ b/tests/regression/67-interval-sets-two/29-def-exc.c @@ -0,0 +1,198 @@ +// PARAM: --enable ana.int.def_exc --enable ana.int.interval_set +#include +#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long)) +#include + +union U1 { + unsigned char f0; + int f1; +}; + + +typedef unsigned long custom_t; +void main() +{ + char yy[256]; + char *ryy_j = &yy[1]; + + int i =0; + + ryy_j++; + ryy_j++; + + while(i < 2) { // Here was an issue with a fixpoint not being reached + ryy_j = ryy_j + 1; + i++; + } + + // The type of ! needs to be IInt + long l; + int r = !l + 4; + + // From dtlk driver example, produces + // "warning: pointer targets in assignment differ in signedness [-Wpointer-sign]" + // in GCC + unsigned char *t; + static char buf[100] = "bliblablubapk\r"; + + t = buf; + t += 2; + + *t = '\r'; + while (*t != '\r') { + t++; + } + + unsigned int v; + unsigned short s1, s2; + + v = v & 0xFFF0FFFF; + v = v << 16; + + v = (unsigned int)(((unsigned int) (s1 ^ s2))); + v = (unsigned int)(((unsigned int) (s1 ^ s2)) << (16)); + + v = (v & 0xFFF0FFFF) | + (((unsigned int) s1 ^ s2) << 16); + + int tmp___1; + int *tmp___2; + + _Bool fclose_fail = (_Bool )(tmp___1 != 0); + + if (! fclose_fail) { + *tmp___2 = 0; + } + + hash_initialize(); + + + + custom_t ci; + void const* b = (void const*) ci; + test((void const *)ci); + + for (i = 0; i < (((20) + (4) - 1)/(4)); i++) { + + } + + + for (i = 0; i < (((20) + (8UL) - 1)/(8UL)); i++) { + + } + + + + + unsigned long v = 8UL; + for (i = 0; i < (((20) + (8UL) - 1)/(v)); i++) { + + } + + i = 0; + if(i < 28UL/v) { + i++; + } + + int gef = 75; + + if(i < 28UL/v) { + i++; + } + + gef = 38; + + i =0; + for(; i < 28UL/v; i++) { + + } + + for (i = 0; i < (((20) + sizeof(unsigned long) - 1)/sizeof(unsigned long)); i++) { + + } + + for (i = 0; i < LONGS(20); i++) { + + } + + int top; + + union U1 g_76; + g_76.f0 = 12; // (f0, (`Definite 12, [0,8])) + if (top) { + g_76.f1 = 5; // (f1, (`Definite 5, [-31,31])) + } + + + unsigned char v; + signed char u; + int r2 = ((int )v == (int )u); + + + signed char l_48 = (signed char )58L; + unsigned char l_67 = (unsigned char)48UL; + + signed char *l_247 ; + unsigned char *l_229 ; + + l_247 = &l_48; + + if (top) { + l_229 = &l_67; + l_247 = (signed char *)l_229; + } + + signed char res = *l_247; + + signed short x; + unsigned int y; + unsigned short z = 0x7ED9L; + + if (((((((signed char)y) == x) && z)))) + { + + } + + return; +} + + +struct hash_table { + custom_t n_buckets ; +}; + +typedef struct hash_table Hash_table; + +Hash_table *hash_initialize() +{ Hash_table *table___0 ; + void *tmp ; + _Bool tmp___0 ; + void *tmp___1 ; + + tmp = malloc(sizeof(*table___0)); + + custom_t n; + table___0 = (Hash_table *)tmp; + table___0->n_buckets = n; + + if (! table___0->n_buckets) { + + goto fail; + } + fail: + + free((void *)table___0); + + return ((Hash_table *)((void *)0)); +} + +int test(void const *ptr) { + if(!ptr) { + __goblint_check(ptr == 0); + int f = 7; + } else { + __goblint_check(ptr == 1); //UNKNOWN! + __goblint_check(ptr != 0); + int f= 38; + } +} diff --git a/tests/regression/67-interval-sets-two/30-no-int-context-option.c b/tests/regression/67-interval-sets-two/30-no-int-context-option.c new file mode 100644 index 0000000000..6801cb908e --- /dev/null +++ b/tests/regression/67-interval-sets-two/30-no-int-context-option.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen --enable ana.base.context.int --set annotation.goblint_context.f[+] base.no-int +#include + +int f(int x) { + if (x) + return f(x+1); + else + return x; +} + +int main () { + int a = f(1); + __goblint_check(!a); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/31-ptrdiff.c b/tests/regression/67-interval-sets-two/31-ptrdiff.c new file mode 100644 index 0000000000..d7bd4667df --- /dev/null +++ b/tests/regression/67-interval-sets-two/31-ptrdiff.c @@ -0,0 +1,20 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +int *tmp; + +int main () +{ + int pathbuf[2]; + + int *bound = pathbuf + sizeof(pathbuf)/sizeof(*pathbuf) - 1; + + int *p = pathbuf; + + while (p <= bound) { + + *p = 1; + + p++; + } + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/32-nested.c b/tests/regression/67-interval-sets-two/32-nested.c new file mode 100644 index 0000000000..bf15ab09da --- /dev/null +++ b/tests/regression/67-interval-sets-two/32-nested.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval_set --set solver td3 +// ALSO: --enable ana.int.interval_set --set solver slr3 +// Example from Halbwachs-Henry, SAS 2012 +// Localized widening should be able to prove that i=10 at the end +// of the nested loops. +#include + +void main() +{ + int i = 0; + + for (; i<10 ; i++) { + for (int j = 0; j < 10 ; j++) ; + } + + __goblint_check(i == 10); +} diff --git a/tests/regression/67-interval-sets-two/33-calloc_array.c b/tests/regression/67-interval-sets-two/33-calloc_array.c new file mode 100644 index 0000000000..4982fc7f41 --- /dev/null +++ b/tests/regression/67-interval-sets-two/33-calloc_array.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +int main(void) { + int *r = calloc(5,sizeof(int)); + + __goblint_check(r[0] == 0); + + r[0] = 3; + + __goblint_check(r[0] == 3); //UNKNOWN + + int z = r[1]; + + __goblint_check(z == 0); //UNKNOWN + __goblint_check(z != 365); +} diff --git a/tests/regression/67-interval-sets-two/34-publish-precision.c b/tests/regression/67-interval-sets-two/34-publish-precision.c new file mode 100644 index 0000000000..28dfe64af7 --- /dev/null +++ b/tests/regression/67-interval-sets-two/34-publish-precision.c @@ -0,0 +1,35 @@ +// PARAM: --set ana.int.interval_set true +#include +#include + +int glob1 = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + pthread_mutex_lock(&mutex2); + glob1 = 5; + + pthread_mutex_unlock(&mutex2); + pthread_mutex_lock(&mutex2); + + __goblint_check(glob1 == 5); + glob1 = 0; + + pthread_mutex_unlock(&mutex2); + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + __goblint_check(glob1 == 0); + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex2); + __goblint_check(glob1 == 0); // UNKNOWN! + __goblint_check(glob1 == 5); // UNKNOWN! + pthread_mutex_unlock(&mutex2); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/35-strict-loop-enter.c b/tests/regression/67-interval-sets-two/35-strict-loop-enter.c new file mode 100644 index 0000000000..f51035736e --- /dev/null +++ b/tests/regression/67-interval-sets-two/35-strict-loop-enter.c @@ -0,0 +1,21 @@ +//PARAM: --disable ana.int.def_exc --enable ana.int.interval_set +#include + + +int main() { + int g = 0; + int x; + + for(x=0; x < 50; x++){ + g = 1; + } + // x = [50, 50] after narrow + if(x>50){ // live after widen, but dead after narrow + // node after Pos(x>50) is marked dead at the end + // but the loop is not with x = [51,2147483647] + for(int i=0; i<=0; i--){ + g = 57; + } + __goblint_check(1); // NOWARN (unreachable) + } +} \ No newline at end of file diff --git a/tests/regression/67-interval-sets-two/36-no-eval-on-write-multi.c b/tests/regression/67-interval-sets-two/36-no-eval-on-write-multi.c new file mode 100644 index 0000000000..5d1a6c1627 --- /dev/null +++ b/tests/regression/67-interval-sets-two/36-no-eval-on-write-multi.c @@ -0,0 +1,35 @@ +//PARAM: --enable ana.int.interval_set --enable ana.int.enums --ana.base.privatization "write" -v +#include +#include + +// Test case that shows how avoiding reading integral globals can reduce the number of solver evaluations. +// Avoiding to evaluate integral globals when setting them reduced the number of necessary evaluations from 62 to 20 in this test case. + +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +int glob = 10; + +void* t_fun(void* ptr) { + pthread_mutex_lock(&mutex); + glob = 3; + glob = 4; + glob = 1; + pthread_mutex_unlock(&mutex); + return NULL; +} + +void bar() { + glob = 2; +} + +int main() { + pthread_t t; + + pthread_create(&t, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex); + bar(); + pthread_mutex_unlock(&mutex); + pthread_join(t, NULL); + __goblint_check(glob >= 1); + __goblint_check(glob <= 10); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/37-int-context-attribute.c b/tests/regression/67-interval-sets-two/37-int-context-attribute.c new file mode 100644 index 0000000000..3f51c839af --- /dev/null +++ b/tests/regression/67-interval-sets-two/37-int-context-attribute.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.int.interval_set --disable ana.context.widen --disable ana.base.context.int +#include + +int f(int x) __attribute__((goblint_context("base.int"))); // attributes are not permitted in a function definition +int f(int x) { + if (x) + return x * f(x - 1); + else + return 1; +} + +int main () { + int a = f(10); + __goblint_check(a == 3628800); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/38-simple.c b/tests/regression/67-interval-sets-two/38-simple.c new file mode 100644 index 0000000000..3e254841f7 --- /dev/null +++ b/tests/regression/67-interval-sets-two/38-simple.c @@ -0,0 +1,33 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned +#include + +int main(void) +{ + int a[40]; + int n = 30; + int m = 20; + + // Check one-dimensional first + int b[n]; + b[29] = 5; + __goblint_check(b[29] = 5); + + int c[n+4]; + c[31] = 2; + __goblint_check(c[31] = 2); + + // Two dimensional, one variable, first + int d[n][30]; + d[2][2] = 42; + __goblint_check(d[2][2] == 42); + + // Two dimensional, one variable, last + int e[20][n]; + e[2][2] = 42; + __goblint_check(e[2][2] == 42); + + // Two dimensional, two variable + int f[m][n]; + f[2][2] = 42; + __goblint_check(f[2][2] == 42); +} diff --git a/tests/regression/67-interval-sets-two/39-div.c b/tests/regression/67-interval-sets-two/39-div.c new file mode 100644 index 0000000000..b3b9f23d4a --- /dev/null +++ b/tests/regression/67-interval-sets-two/39-div.c @@ -0,0 +1,50 @@ +// PARAM: --enable ana.int.def_exc --enable ana.int.interval_set +#include + +int main(void) { + int i = 1; + int v = 8; + + int r = 28/v; + + while(1) { + int zgg = 17; + if (! (i < 28 / v)) { + zgg = 5; + goto while_beak; + } + + int z = 3; + i ++; + z = 7; + } + + + + + + // while(i < 28/v) { + // int z = 3; + // i++; + // z = 7; + // } + while_beak: + __goblint_check(i == 3); + + + + int a = 5; + int b = 7; + + int r = 0; + + if (a == b) { + // Dead + r = 1; + } else { + r = 2; + } + + __goblint_check(r == 1); //FAIL + __goblint_check(r == 2); +} diff --git a/tests/regression/67-interval-sets-two/40-off-attribute.c b/tests/regression/67-interval-sets-two/40-off-attribute.c new file mode 100644 index 0000000000..43236bd624 --- /dev/null +++ b/tests/regression/67-interval-sets-two/40-off-attribute.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.int.interval_set --enable ana.context.widen +#include + +int f(int x) __attribute__((goblint_context("no-widen"))); // attributes are not permitted in a function definition +int f(int x) { + if (x) + return x * f(x - 1); + else + return 1; +} + +int main () { + int a = f(10); + __goblint_check(a == 3628800); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/41-interval-branching.c b/tests/regression/67-interval-sets-two/41-interval-branching.c new file mode 100644 index 0000000000..bcb5e088b1 --- /dev/null +++ b/tests/regression/67-interval-sets-two/41-interval-branching.c @@ -0,0 +1,12 @@ +// PARAM: --enable ana.int.interval_set --disable ana.int.def_exc +#include +#include +int main(){ + int i; + if(i<0){ + __goblint_check(i<0); + } else { + __goblint_check(i>=0); + } + return 0; +} diff --git a/tests/regression/67-interval-sets-two/43-first-reads.c b/tests/regression/67-interval-sets-two/43-first-reads.c new file mode 100644 index 0000000000..852b76a509 --- /dev/null +++ b/tests/regression/67-interval-sets-two/43-first-reads.c @@ -0,0 +1,37 @@ +// PARAM: --set ana.int.interval_set true +extern int __VERIFIER_nondet_int(); + +#include +#include + +int glob1 = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int t = __VERIFIER_nondet_int(); + pthread_mutex_lock(&mutex1); + if(t == 42) { + glob1 = 1; + } + t = glob1; + + __goblint_check(t == 0); //UNKNOWN! + + __goblint_check(t == 1); //UNKNOWN! + + glob1 = 0; + + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + __goblint_check(glob1 == 0); + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex1); + __goblint_check(glob1 == 0); + pthread_mutex_unlock(&mutex1); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/44-comparision-bot.c b/tests/regression/67-interval-sets-two/44-comparision-bot.c new file mode 100644 index 0000000000..5b3622828a --- /dev/null +++ b/tests/regression/67-interval-sets-two/44-comparision-bot.c @@ -0,0 +1,10 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +#include +int main(){ + int a = 0; + unsigned int b = (unsigned int) a - 256U; + if ((unsigned int) a - 256U <= 511U) { + a += 4; + } + printf("%u\n", (unsigned int) a - 256U); +} diff --git a/tests/regression/67-interval-sets-two/45-intervals-branching-meet.c b/tests/regression/67-interval-sets-two/45-intervals-branching-meet.c new file mode 100644 index 0000000000..7da8125bb1 --- /dev/null +++ b/tests/regression/67-interval-sets-two/45-intervals-branching-meet.c @@ -0,0 +1,42 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.structs.domain "sets" + +#include + +struct Pair { + int first; + int second; +}; + +void example1() { + int a; + int b; + + struct Pair pair; + + if (a) { + pair.first = 10; + pair.second = 20; + } else { + pair.first = 20; + pair.second = 30; + } + + if (pair.first == 15) { + // This should be unreachable! + b = 0; // This line is not dead if we --disable ana.base.structs.meet-condition + } else if (pair.first == 10) { + __goblint_check(pair.second == 20); + b = 1; + } else if (pair.first == 20) { + __goblint_check(pair.second == 30); + b = 1; + } + __goblint_check(b == 1); +} + + +int main() { + example1(); + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/46-nesting_arrays.c b/tests/regression/67-interval-sets-two/46-nesting_arrays.c new file mode 100644 index 0000000000..be4a3d996e --- /dev/null +++ b/tests/regression/67-interval-sets-two/46-nesting_arrays.c @@ -0,0 +1,200 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +#include + +struct kala { + int i; + int a[5]; +}; + +struct kalaw { + int* a; +}; + +struct kass { + int v; +}; + +union uArray { + int a[5]; + int b[5]; +}; + +union uStruct { + int b; + struct kala k; +}; + +int main(void) { + example1(); + example2(); + example3(); + example4(); + example5(); + example6(); + example7(); + example8(); + return 0; +} + +void example1() { + struct kala l; + int i = 0; + int top; + + while (i < 5) { + l.a[i] = 42; + i++; + + // Check assertion that should only hold later does not already hold here + __goblint_check(l.a[4] == 42); //UNKNOWN + } + + // Check the array is correctly initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); + + // Destructively assign to i + i = top; + + // Check the array is still known to be completly initialized + __goblint_check(l.a[1] == 42); + __goblint_check(l.a[2] == 42); + __goblint_check(l.a[3] == 42); + __goblint_check(l.a[4] == 42); +} + +void example2() { + struct kala kalas[5]; + + int i2 = 0; + + while (i2 < 4) { + int j2 = 0; + while (j2 < 5) { + kalas[i2].a[j2] = 8; + j2++; + } + i2++; + } + + // Initialization has not proceeded this far + __goblint_check(kalas[4].a[0] == 8); //UNKNOWN + __goblint_check(kalas[0].a[0] == 8); +} + +void example3() { + struct kala xnn; + for(int l=0; l < 5; l++) { + xnn.a[l] = 42; + } + + __goblint_check(xnn.a[3] == 42); +} + +void example4() { + struct kala xn; + + struct kala xs[5]; + + for(int j=0; j < 4; j++) { + xs[j] = xn; + for(int k=0; k < 5; k++) { + xs[j].a[k] = 7; + } + } + + __goblint_check(xs[3].a[0] == 7); +} + +void example5() { + // This example is a bit contrived to show that splitting and moving works for + // unions + union uArray ua; + int i3 = 0; + int top; + int *i = ⊤ + + ua.a[*i] = 1; + + while (i3 < 5) { + ua.a[i3] = 42; + i3++; + } + + __goblint_check(ua.a[i3 - 1] == 42); + + ua.b[0] = 3; + __goblint_check(ua.b[0] == 3); + + // ------------------------------- + union uStruct us; + int i4 = 0; + + us.b = 4; + us.k.a[i4] = 0; + __goblint_check(us.b == 4); // UNKNOWN + __goblint_check(us.k.a[0] == 0); + __goblint_check(us.k.a[3] == 0); // UNKNOWN + + while (i4 < 5) { + us.k.a[i4] = 42; + i4++; + } + + __goblint_check(us.k.a[1] == 42); + __goblint_check(us.k.a[0] == 0); // FAIL +} + +void example6() { + int a[42]; + int i = 0; + + struct kass k; + k.v = 7; + + while(i < 42) { + a[i] = 0; + i++; + } + + i = 0; + + a[k.v] = 2; + k.v = k.v+1; + + __goblint_check(a[k.v] != 3); +} + +void example7() { + // Has no asserts, just checks this doesn't cause an infinite loop + int a[42]; + int i = 0; + + while(i < 40) { + a[i] = 0; + i++; + } + + a[a[0]] = 2; +} + +// Test correct behavior with more involved expression in subscript operator +void example8() { + int a[42]; + union uArray ua; + + ua.a[0] = 0; + ua.a[1] = 0; + ua.a[2] = 0; + ua.a[3] = 0; + ua.a[4] = 0; + + int i = 0; + int *ip = &i; + + a[ua.a[*ip]] = 42; + ip++; + __goblint_check(a[ua.a[*ip]] == 42); //UNKNOWN +} diff --git a/tests/regression/67-interval-sets-two/47-non-zero-performance.c b/tests/regression/67-interval-sets-two/47-non-zero-performance.c new file mode 100644 index 0000000000..322977461a --- /dev/null +++ b/tests/regression/67-interval-sets-two/47-non-zero-performance.c @@ -0,0 +1,26 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --enable exp.fast_global_inits +#include + +int global_array[10000] = {9, 0, 3, 42, 11 }; // All non-specified ones will be zero +int global_array_multi[2][10000] = {{9, 0, 3, 42, 11}, {9, 0, 3, 42, 11}}; // All non-specified ones will be zero + +int main(void) { + __goblint_check(global_array[0] == 9); //UNKNOWN + __goblint_check(global_array[1] == 0); //UNKNOWN + __goblint_check(global_array[2] == 3); //UNKNOWN + __goblint_check(global_array[3] == 42); //UNKNOWN + __goblint_check(global_array[4] == 11); //UNKNOWN + + __goblint_check(global_array_multi[0][0] == 9); //UNKNOWN + __goblint_check(global_array_multi[0][1] == 0); //UNKNOWN + __goblint_check(global_array_multi[0][2] == 3); //UNKNOWN + __goblint_check(global_array_multi[0][3] == 42); //UNKNOWN + __goblint_check(global_array_multi[0][4] == 11); //UNKNOWN + + + __goblint_check(global_array_multi[1][0] == 9); //UNKNOWN + __goblint_check(global_array_multi[1][1] == 0); //UNKNOWN + __goblint_check(global_array_multi[1][2] == 3); //UNKNOWN + __goblint_check(global_array_multi[1][3] == 42); //UNKNOWN + __goblint_check(global_array_multi[1][4] == 11); //UNKNOWN +} diff --git a/tests/regression/67-interval-sets-two/48-calloc_struct_array.c b/tests/regression/67-interval-sets-two/48-calloc_struct_array.c new file mode 100644 index 0000000000..30606fdca5 --- /dev/null +++ b/tests/regression/67-interval-sets-two/48-calloc_struct_array.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.int.interval_set true --set ana.base.arrays.domain partitioned + +#include +#include + +struct h { + int a[2]; + int b; +}; + +int main(void) { + struct h *m = calloc(3,sizeof(int)); + + struct h *a = calloc(10,sizeof(struct h)); + + struct h *b = a+1; + a->a[1] = 3; + int* ptr = &(b->a[1]); + __goblint_check(*ptr == 3); //UNKNOWN +} diff --git a/tests/regression/67-interval-sets-two/49-threshold.c b/tests/regression/67-interval-sets-two/49-threshold.c new file mode 100644 index 0000000000..8ea0730b59 --- /dev/null +++ b/tests/regression/67-interval-sets-two/49-threshold.c @@ -0,0 +1,34 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.interval_threshold_widening + +#include +#include + +int g; + +pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + +void* fun(void* arg) { + pthread_mutex_lock(&m); + if(g < 100) { + g++; + }; + pthread_mutex_unlock(&m); +} + + +int main() { + pthread_t t1; + pthread_create(&t1, 0, fun, 0); + + pthread_mutex_lock(&m); + if(g < 100) { + g++; + }; + pthread_mutex_unlock(&m); + + pthread_join(t1, 0); + + __goblint_check(g <= 100); + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/50-interval.c b/tests/regression/67-interval-sets-two/50-interval.c new file mode 100644 index 0000000000..cef177e1aa --- /dev/null +++ b/tests/regression/67-interval-sets-two/50-interval.c @@ -0,0 +1,72 @@ +// PARAM: --set ana.int.interval_set true +#include +#include + + +int main () { + int a = 1,b = 2,c = 3; + int x,y,z; + int w; + int false = 0; + int true = 42; + + if (x){ + __goblint_check(x != 0); + } else { + __goblint_check(x == 0); + } + + __goblint_check(!! true); + __goblint_check(! false); + + if (a){ + a = a; + } else + __goblint_check(0); // NOWARN + + + if (!a) + __goblint_check(0); // NOWARN + else + a = a; + + if (z != 0){ + a = 8; + b = 9; + } else { + a = 9; + b = 8; + } + + __goblint_check(a); + __goblint_check(a!=b); //UNKNOWN + __goblint_check(a<10); + __goblint_check(a<=9); + __goblint_check(!(a<8)); + __goblint_check(a==8); //UNKNOWN + __goblint_check(b>7); + __goblint_check(b>=8); + __goblint_check(!(a>9)); + __goblint_check(b==8); //UNKNOWN + + for(x = 0; x < 10; x++){ + __goblint_check(x >= 0); + __goblint_check(x <= 9); + } + __goblint_check(x == 10); + + if (0 <= w) + { + } + else + { + return 0; + } + + if (w > 0) + { + __goblint_check(1); + } + + return 0; +} diff --git a/tests/regression/67-interval-sets-two/52-no-eval-on-write.c b/tests/regression/67-interval-sets-two/52-no-eval-on-write.c new file mode 100644 index 0000000000..ad6cb038a4 --- /dev/null +++ b/tests/regression/67-interval-sets-two/52-no-eval-on-write.c @@ -0,0 +1,24 @@ +//PARAM: --enable ana.int.interval_set --enable exp.earlyglobs --enable ana.int.enums -v +#include + +// Test case that shows how avoiding reading integral globals can reduce the number of solver evaluations. +// Avoiding to evaluate integral globals when setting them reduced the number of necessary evaluations from 27 to 14 in this test case. +int glob = 10; + +void foo() { + glob = 3; + glob = 4; + glob = 1; +} + +void bar() { + glob = 2; +} + +int main() { + foo(); + bar(); + __goblint_check(glob >= 1); + __goblint_check(glob <= 10); + return 0; +} diff --git a/tests/regression/67-interval-sets-two/53-pointer-to-arrays-of-different-sizes.c b/tests/regression/67-interval-sets-two/53-pointer-to-arrays-of-different-sizes.c new file mode 100644 index 0000000000..9779efdd52 --- /dev/null +++ b/tests/regression/67-interval-sets-two/53-pointer-to-arrays-of-different-sizes.c @@ -0,0 +1,28 @@ +// PARAM: --enable ana.arrayoob --enable ana.int.interval_set --enable ana.int.enums +//Pointer pointing to arrays of different sizes +#include +int main( ) +{ + int arr[] = {1, 2, 3, 4, 5, 6}; + int arr2[] = {1, 2, 3}; + int * ptr = arr; + + ptr[3] = 4; //NOWARN + ptr[6] = 10; //WARN + ptr[-1] = 10; //WARN + + for (int i = 0; i < 10; ++i) + { + ptr[i] = 5; //WARN + } + int * ptr2 = arr2; + ptr2[1] = 3; //NOWARN + ptr2[3] = 10; //WARN + ptr2[-1] = 10; //WARN + for (int i = 0; i < 5; ++i) + { + ptr2[i] = 5; //WARN + } + return 0; +} + diff --git a/tests/regression/67-interval-sets-two/54-simple_array_in_loops.c b/tests/regression/67-interval-sets-two/54-simple_array_in_loops.c new file mode 100644 index 0000000000..1a52963233 --- /dev/null +++ b/tests/regression/67-interval-sets-two/54-simple_array_in_loops.c @@ -0,0 +1,50 @@ +// PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 2 +#include +int global; + +int main(void) +{ + example1(); + example2(); + return 0; +} + +// Simple example +void example1(void) +{ + int a[42]; + int i = 0; + int top; + + while (i < 42) { + a[i] = 0; + __goblint_check(a[i] == 0); // UNKNOWN + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[17] == 0); + i++; + } + + __goblint_check(a[0] == 0); // UNKNOWN + __goblint_check(a[7] == 0); + __goblint_check(a[41] == 0); +} + + +// Check that arrays of types different from int are handeled correctly +void example2() { + // no char because char has unknown signedness (particularly, unsigned on arm64) + signed char a[10]; + int n; + __goblint_check(a[3] == 800); // FAIL (char cannot be 800) + __goblint_check(a[3] == 127); // UNKNOWN! + + for(int i=0;i < 10; i++) { + a[i] = 7; + } + + a[3] = (signed char) n; + __goblint_check(a[3] == 800); //FAIL + __goblint_check(a[3] == 127); //UNKNOWN + __goblint_check(a[3] == -128); //UNKNOWN + __goblint_check(a[3] == -129); //FAIL +} diff --git a/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c new file mode 100644 index 0000000000..e1345155d9 --- /dev/null +++ b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c @@ -0,0 +1,18 @@ +// PARAM: --enable ana.int.interval_set +#include +#include + +int main() { + + int i; + + if (i > 5 && i < 10) { + i = 1; + } + if (i == 7) { + i = INT_MAX; + i += 1; + } + + return 0; +} \ No newline at end of file diff --git a/tests/regression/67-interval-sets-two/57-interval-set-wrap-around.c b/tests/regression/67-interval-sets-two/57-interval-set-wrap-around.c new file mode 100644 index 0000000000..e711030f08 --- /dev/null +++ b/tests/regression/67-interval-sets-two/57-interval-set-wrap-around.c @@ -0,0 +1,18 @@ +// PARAM: --enable ana.int.interval_set --set sem.int.signed_overflow assume_wraparound +#include +#include + +int main() { + + char i; + + if (i < 126) { + i = 126; + } + + i++; + + __goblint_check(i != 0); + + return 0; +} \ No newline at end of file diff --git a/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c new file mode 100644 index 0000000000..b63ebd6fab --- /dev/null +++ b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c @@ -0,0 +1,27 @@ +// PARAM: --enable ana.int.interval_set +#include +#include + +int sum(int* a, int n) { + int sum = 0; + for (int i = 0; i < n; i++) { + sum += a[i]; + } + return sum + 2 * INT_MAX; +} + +int main() { + int n; + int a[n]; + int x; + + if (x > 1 && x < 11) { + x -= 2; + } + + if (x == 9) { + x = sum(a, n); + } + + return x == 42 ? 1 : 0; +} diff --git a/unittest/cdomains/intDomainTest.ml b/unittest/cdomains/intDomainTest.ml index 858b179135..f803ef1c35 100644 --- a/unittest/cdomains/intDomainTest.ml +++ b/unittest/cdomains/intDomainTest.ml @@ -200,7 +200,7 @@ let test_ex_set _ = module Interval = struct - module I = IntDomain.Interval + module I = IntDomain.SOverflowUnlifter(IntDomain.Interval) let assert_equal x y = assert_equal ~cmp:I.equal ~printer:I.show x y diff --git a/unittest/maindomaintest.ml b/unittest/maindomaintest.ml index d17d2bf891..fd5ff1782b 100644 --- a/unittest/maindomaintest.ml +++ b/unittest/maindomaintest.ml @@ -48,9 +48,10 @@ let domains: (module Lattice.S) list = [ let nonAssocDomains: (module Lattice.S) list = [] let intDomains: (module IntDomainProperties.S) list = [ - (module IntDomain.Interval); + (module IntDomain.SOverflowUnlifter(IntDomain.Interval)); (module IntDomain.Enums); (module IntDomain.Congruence); + (module IntDomain.SOverflowUnlifter(IntDomain.IntervalSet)); (* (module IntDomain.Flattened); *) (* (module IntDomain.Interval32); *) (* (module IntDomain.Booleans); *)