From 96c777d9325641d7d70248769d3ea4ec719a7daf Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 24 Jan 2023 08:45:41 +0100 Subject: [PATCH 001/238] tmpSpecial analysis and printalbe for math library descriptors --- src/analyses/libraryDesc.ml | 183 +++++++++++++++++++++- src/analyses/tmpSpecial.ml | 64 ++++++++ src/domains/queries.ml | 9 ++ tests/regression/57-floats/19-specialEq.c | 20 +++ 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 src/analyses/tmpSpecial.ml create mode 100644 tests/regression/57-floats/19-specialEq.c diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index a477fc1809..1b915faa01 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -1,6 +1,6 @@ (** Library function descriptor (specification). *) module Cil = GoblintCil - +open Cil (** Pointer argument access specification. *) module Access = struct @@ -143,3 +143,184 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): accs = Accesses.of_old old_accesses; special = special_of_old classify_name; } + +module MathPrintable = struct + include Printable.Std + type t = math + + let name () = "MathPrintable" + + let relift = function + | Nan (fk, exp) -> Nan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Inf fk -> Inf (CilType.Fkind.relift fk) + | Isfinite exp -> Isfinite (CilType.Exp.relift exp) + | Isinf exp -> Isinf (CilType.Exp.relift exp) + | Isnan exp -> Isnan (CilType.Exp.relift exp) + | Isnormal exp -> Isnormal (CilType.Exp.relift exp) + | Signbit exp -> Signbit (CilType.Exp.relift exp) + | Isgreater (exp1, exp2) -> Isgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isgreaterequal (exp1, exp2) -> Isgreaterequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isless (exp1, exp2) -> Isless (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Islessequal (exp1, exp2) -> Islessequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Islessgreater (exp1, exp2) -> Islessgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Isunordered (exp1, exp2) -> Isunordered (CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Ceil (fk, exp) -> Ceil (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Floor (fk, exp) -> Floor (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Fabs (fk, exp) -> Fabs (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Fmax (fk, exp1, exp2) -> Fmax (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Fmin (fk, exp1, exp2) -> Fmin (fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Acos (fk, exp) -> Acos (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Asin (fk, exp) -> Asin (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Atan (fk, exp) -> Atan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Atan2 (fk, exp1, exp2) -> Atan2 (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) + | Cos (fk, exp) -> Cos (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Sin (fk, exp) -> Sin (CilType.Fkind.relift fk, CilType.Exp.relift exp) + | Tan (fk, exp) -> Tan (CilType.Fkind.relift fk, CilType.Exp.relift exp) + + + let order = function + | Nan _ -> 1 + | Inf _ -> 2 + | Isfinite _ -> 3 + | Isinf _ -> 4 + | Isnan _ -> 5 + | Isnormal _ -> 6 + | Signbit _ -> 7 + | Isgreater _ -> 8 + | Isgreaterequal _ -> 9 + | Isless _ -> 10 + | Islessequal _ -> 11 + | Islessgreater _ -> 12 + | Isunordered _ -> 13 + | Ceil _ -> 14 + | Floor _ -> 15 + | Fabs _ -> 16 + | Fmax _ -> 17 + | Fmin _ -> 18 + | Acos _ -> 19 + | Asin _ -> 20 + | Atan _ -> 21 + | Atan2 _ -> 22 + | Cos _ -> 23 + | Sin _ -> 24 + | Tan _ -> 25 + + let equal m1 m2 = (compare m1 m2) == 0 + let hash = order + + let cmp_fk_exp (fk1, e1) (fk2, e2) = + let r = (CilType.Fkind.compare fk1 fk2) in + if r <> 0 then + r + else + CilType.Exp.compare e1 e2 + + let cmp_exp_exp (e1, e1') (e2, e2') = + let r = (CilType.Exp.compare e1 e2) in + if r <> 0 then + r + else + CilType.Exp.compare e1' e2' + + let cmp_fk_exp_exp (fk1, e1, e1') (fk2, e2, e2') = + let r = (CilType.Fkind.compare fk1 fk2) in + if r <> 0 then + r + else + cmp_exp_exp (e1, e1') (e2, e2') + + let compare m1 m2 = + let r = Stdlib.compare (order m1) (order m2) in + if r <> 0 then + r + else + match m1, m2 with + | Nan fe1, Nan fe2 -> cmp_fk_exp fe1 fe2 + | Inf fk1, Inf fk2 -> CilType.Fkind.compare fk1 fk2 + | Isfinite e1, Isfinite e2 -> CilType.Exp.compare e1 e2 + | Isinf e1, Isinf e2 -> CilType.Exp.compare e1 e2 + | Isnan e1, Isnan e2 -> CilType.Exp.compare e1 e2 + | Isnormal e1, Isnormal e2 -> CilType.Exp.compare e1 e2 + | Signbit e1, Signbit e2 -> CilType.Exp.compare e1 e2 + | Isgreater ee1, Isgreater ee2 -> cmp_exp_exp ee1 ee2 + | Isgreaterequal ee1, Isgreaterequal ee2 -> cmp_exp_exp ee1 ee2 + | Isless ee1, Isless ee2 -> cmp_exp_exp ee1 ee2 + | Islessequal ee1, Islessequal ee2 -> cmp_exp_exp ee1 ee2 + | Islessgreater ee1, Islessgreater ee2 -> cmp_exp_exp ee1 ee2 + | Isunordered ee1, Isunordered ee2 -> cmp_exp_exp ee1 ee2 + | Ceil fe1, Ceil fe2 -> cmp_fk_exp fe1 fe2 + | Floor fe1, Floor fe2 -> cmp_fk_exp fe1 fe2 + | Fabs fe1, Fabs fe2 -> cmp_fk_exp fe1 fe2 + | Fmax fee1, Fmax fee2 -> cmp_fk_exp_exp fee1 fee2 + | Fmin fee1, Fmin fee2 -> cmp_fk_exp_exp fee1 fee2 + | Acos fe1, Acos fe2 -> cmp_fk_exp fe1 fe2 + | Asin fe1, Asin fe2 -> cmp_fk_exp fe1 fe2 + | Atan fe1, Atan fe2 -> cmp_fk_exp fe1 fe2 + | Atan2 fee1, Atan2 fee2 -> cmp_fk_exp_exp fee1 fee2 + | Cos fe1, Cos fe2 -> cmp_fk_exp fe1 fe2 + | Sin fe1, Sin fe2 -> cmp_fk_exp fe1 fe2 + | Tan fe1, Tan fe2 -> cmp_fk_exp fe1 fe2 + | _ -> failwith "impossible" + + let show = function + | Nan _ -> "nan" + | Inf _ -> "inf" + | Isfinite _ -> "isFinite" + | Isinf _ -> "isInf" + | Isnan _ -> "isNan" + | Isnormal _ -> "isNormal" + | Signbit _ -> "signbit" + | Isgreater _ -> "isGreater" + | Isgreaterequal _ -> "isGreaterEqual" + | Isless _ -> "isLess" + | Islessequal _ -> "isLessEqual" + | Islessgreater _ -> "isLessGreater" + | Isunordered _ -> "isUnordered" + | Ceil _ -> "ceil" + | Floor _ -> "floor" + | Fabs _ -> "fabs" + | Fmax _ -> "fmax" + | Fmin _ -> "fmin" + | Acos _ -> "acos" + | Asin _ -> "asin" + | Atan _ -> "atan" + | Atan2 _ -> "atan2" + | Cos _ -> "cos" + | Sin _ -> "sin" + | Tan _ -> "tan" + + let pretty () = function + | Nan (fk, exp) -> Pretty.dprintf "(%a )nan(%a)" d_fkind fk d_exp exp + | Inf fk -> Pretty.dprintf "(%a )inf()" d_fkind fk + | Isfinite exp -> Pretty.dprintf "isFinite(%a)" d_exp exp + | Isinf exp -> Pretty.dprintf "isInf(%a)" d_exp exp + | Isnan exp -> Pretty.dprintf "isNan(%a)" d_exp exp + | Isnormal exp -> Pretty.dprintf "isNormal(%a)" d_exp exp + | Signbit exp -> Pretty.dprintf "signbit(%a)" d_exp exp + | Isgreater (exp1, exp2) -> Pretty.dprintf "isGreater(%a, %a)" d_exp exp1 d_exp exp2 + | Isgreaterequal (exp1, exp2) -> Pretty.dprintf "isGreaterEqual(%a, %a)" d_exp exp1 d_exp exp2 + | Isless (exp1, exp2) -> Pretty.dprintf "isLess(%a, %a)" d_exp exp1 d_exp exp2 + | Islessequal (exp1, exp2) -> Pretty.dprintf "isLessEqual(%a, %a)" d_exp exp1 d_exp exp2 + | Islessgreater (exp1, exp2) -> Pretty.dprintf "isLessGreater(%a, %a)" d_exp exp1 d_exp exp2 + | Isunordered (exp1, exp2) -> Pretty.dprintf "isUnordered(%a, %a)" d_exp exp1 d_exp exp2 + | Ceil (fk, exp) -> Pretty.dprintf "(%a )ceil(%a)" d_fkind fk d_exp exp + | Floor (fk, exp) -> Pretty.dprintf "(%a )floor(%a)" d_fkind fk d_exp exp + | Fabs (fk, exp) -> Pretty.dprintf "(%a )fabs(%a)" d_fkind fk d_exp exp + | Fmax (fk, exp1, exp2) -> Pretty.dprintf "(%a )fmax(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Fmin (fk, exp1, exp2) -> Pretty.dprintf "(%a )fmin(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Acos (fk, exp) -> Pretty.dprintf "(%a )acos(%a)" d_fkind fk d_exp exp + | Asin (fk, exp) -> Pretty.dprintf "(%a )asin(%a)" d_fkind fk d_exp exp + | Atan (fk, exp) -> Pretty.dprintf "(%a )atan(%a)" d_fkind fk d_exp exp + | Atan2 (fk, exp1, exp2) -> Pretty.dprintf "(%a )atan2(%a, %a)" d_fkind fk d_exp exp1 d_exp exp2 + | Cos (fk, exp) -> Pretty.dprintf "(%a )cos(%a)" d_fkind fk d_exp exp + | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp + | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp + + let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) + let to_yojson _ = failwith "ToDo Implement in future" +end + +module MathLifted = Lattice.Flat (MathPrintable) (struct + let top_name = "Unknown math desc" + let bot_name = "Nonexistent math desc" +end) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml new file mode 100644 index 0000000000..f02f5cb48d --- /dev/null +++ b/src/analyses/tmpSpecial.ml @@ -0,0 +1,64 @@ +(* Analysis that tracks which variables hold the results of calls to math library functions. *) + +open Prelude.Ana +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + let name () = "tmpSpecial" + module ML = LibraryDesc.MathLifted + module D = MapDomain.MapBot (Basetype.Variables) (ML) + module C = Lattice.Unit + + let context _ _ = () + + (* transfer functions *) + let assign ctx (lval:lval) (rval:exp) : D.t = + match lval with + | (Var v, _) -> D.remove v ctx.local + (* TODO: Handle mem -> may point to*) + | _ -> ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + ctx.local + + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [ctx.local, D.bot ()] + + let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + D.bot () + + let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + (* Just dbg prints *) + (match lval with + | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + let desc = LibraryFunctions.find f in + let res = + match lval, desc.special arglist with + | (Some (Var v, _)), (Math { fun_args; }) -> D.add v (ML.lift fun_args) ctx.local + | _ -> ctx.local + in + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty res; + res + + let query ctx (type a) (q: a Queries.t): a Queries.result = + Queries.Result.top q + + let startstate v = D.bot () + let threadenter ctx lval f args = [D.bot ()] + let threadspawn ctx lval f args fctx = ctx.local + let exitstate v = D.bot () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 66db991826..811b3f74c3 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -13,6 +13,8 @@ module TS = SetDomain.ToppedSet (CilType.Typ) (struct let topname = "All" end) module ES = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module VS = SetDomain.ToppedSet (CilType.Varinfo) (struct let topname = "All" end) +module ML = LibraryDesc.MathLifted + module VI = Lattice.Flat (Basetype.Variables) (struct let top_name = "Unknown line" let bot_name = "Unreachable line" @@ -99,6 +101,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t + | TmpSpecial: varinfo -> ML.t t type 'a result = 'a @@ -159,6 +162,7 @@ struct | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module LS) | MayBeModifiedSinceSetjmp _ -> (module VS) + | TmpSpecial _ -> (module ML) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -218,6 +222,7 @@ struct | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> LS.top () | MayBeModifiedSinceSetjmp _ -> VS.top () + | TmpSpecial _ -> ML.top () end (* The type any_query can't be directly defined in Any as t, @@ -274,6 +279,7 @@ struct | Any ActiveJumpBuf -> 46 | Any ValidLongJmp -> 47 | Any (MayBeModifiedSinceSetjmp _) -> 48 + | Any (TmpSpecial _) -> 42 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -316,6 +322,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 + | Any (TmpSpecial vi1), Any (TmpSpecial vi2) -> CilType.Varinfo.compare vi1 vi2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -351,6 +358,7 @@ struct | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e + | Any (TmpSpecial vi) -> CilType.Varinfo.hash vi (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -404,6 +412,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf + | Any (TmpSpecial vi) -> Pretty.dprintf "TmpSpecial %a" CilType.Varinfo.pretty vi end let to_value_domain_ask (ask: ask) = diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c new file mode 100644 index 0000000000..2a181d0588 --- /dev/null +++ b/tests/regression/57-floats/19-specialEq.c @@ -0,0 +1,20 @@ +//PARAM: --set ana.activated[+] tmpSpecial +#include +#include + +void main() { + float f; + int c; + + + + if ( __builtin_isfinite(f) ) { + __goblint_check(f); + } + float x; + x = __builtin_atan2(f, 0.4); + if (__builtin_isnan(f) && __builtin_isinf(__builtin_inff())) + { + f = 0; + } +} \ No newline at end of file From 508180c87201ba70c386c1e7607004d4282b7e2e Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 3 Feb 2023 00:12:11 +0100 Subject: [PATCH 002/238] invalidate if argument is written --- src/analyses/tmpSpecial.ml | 108 +++++++++++++++++++--- tests/regression/57-floats/19-specialEq.c | 34 +++++-- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index f02f5cb48d..b5bda86289 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,4 +1,5 @@ -(* Analysis that tracks which variables hold the results of calls to math library functions. *) +(* Analysis that tracks which variables hold the results of calls to math library functions. + For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) open Prelude.Ana open Analyses @@ -9,17 +10,49 @@ struct let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted - module D = MapDomain.MapBot (Basetype.Variables) (ML) + module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) + module MlLsProd = Lattice.Prod (ML) (LS) + module D = MapDomain.MapBot (Basetype.Variables) (MlLsProd) module C = Lattice.Unit + let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = + match offs with + | NoOffset -> `NoOffset + | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) + | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) + + let ls_of_lv ctx (lval:lval) : LS.t = + match lval with + | (Var v, offs) -> LS.of_list [(v, resolve offs)] + | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) + + let rec ls_of_exp ctx (exp:exp) : LS.t = + match exp with + | Const _ -> LS.bot () + | Lval lv -> ls_of_lv ctx lv + | SizeOf _ -> LS.bot () + | Real e -> ls_of_exp ctx e + | Imag e -> ls_of_exp ctx e + | SizeOfE e -> ls_of_exp ctx e + | SizeOfStr _ -> LS.empty () + | AlignOf _ -> LS.top () (* TODO: what is this*) + | AlignOfE _ -> LS.top () (* TODO: what is this*) + | UnOp (_,e,_) -> ls_of_exp ctx e + | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) + | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) + | CastE (_,e) -> ls_of_exp ctx e + | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) + | AddrOfLabel _ -> LS.top () (* TODO: what is this*) + | StartOf _ -> LS.top () (* TODO: what is this*) + + let context _ _ = () (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - match lval with - | (Var v, _) -> D.remove v ctx.local - (* TODO: Handle mem -> may point to*) - | _ -> ctx.local + (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) + let lvalsWritten = ls_of_lv ctx lval in + D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -37,22 +70,69 @@ struct D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let d = ctx.local in + (* Just dbg prints *) (match lval with | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - let desc = LibraryFunctions.find f in - let res = + (* add new math fun dec*) + let d = match lval, desc.special arglist with - | (Some (Var v, _)), (Math { fun_args; }) -> D.add v (ML.lift fun_args) ctx.local - | _ -> ctx.local + | (Some (Var v, _)), (Math { fun_args; }) -> + let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + if LS.is_top lvalDep then + d + else + D.add v ((ML.lift fun_args, lvalDep)) d + | _ -> d + in + + (* remove entrys, dependent on lvals that were possibly written by the special function *) + let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + let deep_addrs = + if List.mem LibraryDesc.InvalidateGlobals desc.attrs then ( + foldGlobals !Cilfacade.current_file (fun acc global -> + match global with + | GVar (vi, _, _) when not (BaseUtil.is_static vi) -> + mkAddrOf (Var vi, NoOffset) :: acc + (* TODO: what about GVarDecl? (see "base.ml -> special_unknown_invalidate")*) + | _ -> acc + ) deep_addrs + ) + else + deep_addrs + in + let d = List.fold_left (fun accD addr -> + let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in + D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs + in + let d = List.fold_left (fun accD addr -> + let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in + D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs + in + + (* same for lval assignment of the call (though this should always be a "tmp___n")*) + let d = + match lval with + | Some lv -> ( + (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) + let lvalsWritten = ls_of_lv ctx lv in + D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d + ) + | None -> d in - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty res; - res + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; + d - let query ctx (type a) (q: a Queries.t): a Queries.result = - Queries.Result.top q + let query ctx (type a) (q: a Queries.t) : a Queries.result = + match q with + | TmpSpecial v -> let ml = fst (D.find v ctx.local) in + if ML.is_bot ml then Queries.Result.top q + else ml + | _ -> Queries.Result.top q let startstate v = D.bot () let threadenter ctx lval f args = [D.bot ()] diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c index 2a181d0588..e9bf57f3aa 100644 --- a/tests/regression/57-floats/19-specialEq.c +++ b/tests/regression/57-floats/19-specialEq.c @@ -2,19 +2,33 @@ #include #include + void main() { float f; - int c; + float g; + float common; + float* fptr; + float* gptr; + int unk1; + int unk2; + + float other; + + if (unk1) + fptr = &f; + else + fptr = &common; + + if (unk2) + gptr = &g; + else + gptr = &common; + - if ( __builtin_isfinite(f) ) { - __goblint_check(f); - } - float x; - x = __builtin_atan2(f, 0.4); - if (__builtin_isnan(f) && __builtin_isinf(__builtin_inff())) - { - f = 0; - } + other = __builtin_cos(*fptr); + *gptr = 0.7; + + __builtin_inf(); } \ No newline at end of file From f80bd4ff8acc15d3441cb16564a4cd922576a8d3 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 11 Feb 2023 18:34:49 +0100 Subject: [PATCH 003/238] improve baseInvariant for calls to math functions --- src/analyses/baseInvariant.ml | 54 ++++++++++++++++++++++- src/cdomains/floatDomain.ml | 49 +++++++++++++++++++- src/cdomains/floatDomain.mli | 12 +++++ tests/regression/57-floats/19-specialEq.c | 35 ++++----------- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 6b5f03750a..92204d9bea 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -547,6 +547,11 @@ struct in let eval e st = eval_rv a gs st e in let eval_bool e st = match eval e st with `Int i -> ID.to_bool i | _ -> None in + let unroll_fk_of_exp e = + match unrollType (Cilfacade.typeOf e) with + | TFloat (fk, _) -> fk + | _ -> failwith "impossible" + in let rec inv_exp c_typed exp (st:D.t): D.t = (* trying to improve variables in an expression so it is bottom means dead code *) if VD.is_bot_value c_typed then contra st else @@ -682,6 +687,7 @@ struct | Lval x, (`Int _ | `Float _ | `Address _) -> (* meet x with c *) let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) + if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; begin match c_typed with | `Int c -> let c' = match t with @@ -691,7 +697,30 @@ struct | TFloat (fk, _) -> `Float (FD.of_int fk c) | _ -> `Int c in - update_lval c x c' ID.pretty + let st = update_lval c x c' ID.pretty in + (* handle special calls *) + begin match t with + | TInt (ik, _) -> + begin match x with + | ((Var v), _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + let tv = not (ID.leq c (ID.of_bool ik false)) in + begin match ctx.ask (Queries.TmpSpecial v) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | `Lifted (Isunordered (xFloat, yFloat)) -> st (* something can probably be done here *) + | _ -> st + end + | _ -> st + end + | _ -> st + end | `Float c -> let c' = match t with (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) @@ -701,7 +730,28 @@ struct | TFloat (fk, _) -> `Float (FD.cast_to fk c) | _ -> `Float c in - update_lval c x c' FD.pretty + let st = update_lval c x c' FD.pretty in + (* handle special calls *) + begin match t with + | TFloat (fk, _) -> + begin match x with + | ((Var v), _) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + begin match ctx.ask (Queries.TmpSpecial v) with + | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Fabs (ret_fk, xFloat)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (`Float inv) xFloat st + | _ -> st + end + | _ -> st + end + | _ -> st + end | `Address c -> let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) update_lval c x c' AD.pretty diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 4eb024adf9..4bd1017696 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -41,6 +41,13 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + (** {inversions of unary functions}*) + val inv_ceil : t -> t + (** (inv_ceil z -> x) if (z = ceil(x)) *) + val inv_floor : t -> t + (** (inv_floor z -> x) if (z = floor(x)) *) + val inv_fabs : t -> t + (** (inv_fabs z -> x) if (z = fabs(x)) *) (** {b Comparison operators} *) val lt : t -> t -> IntDomain.IntDomTuple.t @@ -88,11 +95,13 @@ module type FloatDomainBase = sig val starting : float -> t val ending_before : float -> t val starting_after : float -> t + val finite : t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool end module FloatIntervalImpl(Float_t : CFloatType) = struct @@ -173,6 +182,10 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | MinusInfinity -> true | _ -> false + let is_interval = function + | Interval _ -> true + | _ -> false + let norm = function | Interval (low, high) as x -> if Float_t.is_finite low && Float_t.is_finite high then @@ -210,6 +223,7 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let ending_before e = of_interval' (Float_t.lower_bound, Float_t.pred @@ Float_t.of_float Up e) let starting s = of_interval' (Float_t.of_float Down s, Float_t.upper_bound) let starting_after s = of_interval' (Float_t.succ @@ Float_t.of_float Down s, Float_t.upper_bound) + let finite = of_interval' (Float_t.lower_bound, Float_t.upper_bound) let minimal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "minimal %s" (show Bot))) @@ -661,6 +675,16 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) + let eval_inv_ceil = function + | (l, h) -> Interval (Float_t.floor (Float_t.pred l), h) + + let eval_inv_floor = function + | (l, h) -> Interval (l, Float_t.ceil (Float_t.succ h)) + + let eval_inv_fabs = function + | (_, h) when h > Float_t.zero -> Interval (Float_t.neg h, h) + | _ -> top () + let isfinite op = match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) @@ -733,6 +757,10 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let cos = eval_unop (top ()) eval_cos let sin = eval_unop (top ()) eval_sin let tan = eval_unop (top ()) eval_tan + + let inv_ceil = eval_unop (top ()) eval_inv_ceil + let inv_floor = eval_unop (top ()) eval_inv_floor + let inv_fabs = eval_unop (top ()) eval_inv_fabs end module F64Interval = FloatIntervalImpl(CDouble) @@ -761,11 +789,13 @@ module type FloatDomain = sig val starting : Cil.fkind -> float -> t val ending_before : Cil.fkind -> float -> t val starting_after : Cil.fkind -> float -> t + val finite : Cil.fkind -> t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end @@ -836,6 +866,9 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) + let inv_ceil = lift (F1.inv_ceil, F2.inv_ceil) + let inv_floor = lift (F1.inv_floor, F2.inv_floor) + let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) let sub = lift2 (F1.sub, F2.sub) let mul = lift2 (F1.mul, F2.mul) @@ -860,7 +893,7 @@ module FloatIntervalImplLifted = struct let is_bot = dispatch (F1.is_bot, F2.is_bot) let top_of fkind = dispatch_fkind fkind (F1.top, F2.top) let top () = failwith "top () is not implemented for FloatIntervalImplLifted." - let is_top = dispatch (F1.is_bot, F2.is_bot) + let is_top = dispatch (F1.is_top, F2.is_top) let nan_of fkind = dispatch_fkind fkind (F1.nan, F2.nan) let is_nan = dispatch (F1.is_nan, F2.is_nan) @@ -869,6 +902,7 @@ module FloatIntervalImplLifted = struct let minus_inf_of fkind = dispatch_fkind fkind (F1.minus_inf, F2.minus_inf) let is_inf = dispatch (F1.is_inf, F2.is_inf) let is_neg_inf = dispatch (F1.is_minus_inf, F2.is_minus_inf) + let is_interval = dispatch (F1.is_interval, F2.is_interval) let get_fkind = function | F32 _ -> FFloat @@ -900,6 +934,7 @@ module FloatIntervalImplLifted = struct let of_interval fkind i = dispatch_fkind fkind ((fun () -> F1.of_interval i), (fun () -> F2.of_interval i)) let starting fkind s = dispatch_fkind fkind ((fun () -> F1.starting s), (fun () -> F2.starting s)) let starting_after fkind s = dispatch_fkind fkind ((fun () -> F1.starting_after s), (fun () -> F2.starting_after s)) + let finite fkind = dispatch_fkind fkind ((fun () -> F1.finite), (fun () -> F2.finite)) let ending fkind e = dispatch_fkind fkind ((fun () -> F1.ending e), (fun () -> F2.ending e)) let ending_before fkind e = dispatch_fkind fkind ((fun () -> F1.ending_before e), (fun () -> F2.ending_before e)) let minimal = dispatch (F1.minimal, F2.minimal) @@ -1003,6 +1038,8 @@ module FloatDomTupleImpl = struct create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.ending_before fkind); } let starting_after fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.starting_after fkind); } + let finite = + create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } let of_string fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_string fkind); } @@ -1031,6 +1068,9 @@ module FloatDomTupleImpl = struct let is_exact = exists % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_exact); } + let is_interval = + for_all + % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_interval); } let is_top = for_all % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_top); } @@ -1080,6 +1120,13 @@ module FloatDomTupleImpl = struct let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } + let inv_ceil = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil); } + let inv_floor = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor); } + let inv_fabs = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } + (* f2: binary ops *) let join = map2 { f2= (fun (type a) (module F : FloatDomain with type t = a) -> F.join); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 8be4304c5e..67ec9d17dd 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -42,6 +42,14 @@ module type FloatArith = sig val tan : t -> t (** tan(x) *) + (** {inversions of unary functions}*) + val inv_ceil : t -> t + (** (inv_ceil z -> x) if (z = ceil(x)) *) + val inv_floor : t -> t + (** (inv_floor z -> x) if (z = floor(x)) *) + val inv_fabs : t -> t + (** (inv_fabs z -> x) if (z = fabs(x)) *) + (** {b Comparison operators} *) val lt : t -> t -> IntDomain.IntDomTuple.t @@ -89,11 +97,13 @@ module type FloatDomainBase = sig val starting : float -> t val ending_before : float -> t val starting_after : float -> t + val finite : t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool end (* Only exposed for testing *) @@ -123,11 +133,13 @@ module type FloatDomain = sig val starting : Cil.fkind -> float -> t val ending_before : Cil.fkind -> float -> t val starting_after : Cil.fkind -> float -> t + val finite : Cil.fkind -> t val minimal: t -> float option val maximal: t -> float option val is_exact : t -> bool + val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c index e9bf57f3aa..05eacbfa8d 100644 --- a/tests/regression/57-floats/19-specialEq.c +++ b/tests/regression/57-floats/19-specialEq.c @@ -4,31 +4,12 @@ void main() { - float f; - float g; - float common; - float* fptr; - float* gptr; - int unk1; - int unk2; - - float other; - - if (unk1) - fptr = &f; - else - fptr = &common; - - if (unk2) - gptr = &g; - else - gptr = &common; - - - - - other = __builtin_cos(*fptr); - *gptr = 0.7; - - __builtin_inf(); + double f; + int unk; + + if (__builtin_isnan(f) ) { + __goblint_check( __builtin_isfinite(f)); + } else { + __goblint_check( __builtin_isnan(f)); + } } \ No newline at end of file From 13ab91b2654e42c235e9fa8953ef370858ccfcdd Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 7 Mar 2023 22:53:17 +0100 Subject: [PATCH 004/238] tests --- src/cdomains/floatDomain.ml | 45 +++++++------ .../57-floats/19-library-invariant.c | 65 +++++++++++++++++++ tests/regression/57-floats/19-specialEq.c | 15 ----- .../20-library-invariant-invalidate.c | 15 +++++ 4 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 tests/regression/57-floats/19-library-invariant.c delete mode 100644 tests/regression/57-floats/19-specialEq.c create mode 100644 tests/regression/57-floats/20-library-invariant-invalidate.c diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 4bd1017696..a95926b29e 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -326,13 +326,13 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct warn_on_special "Second operand" "comparison" op2 (** evaluation of the unary and binary operations *) - let eval_unop onTop eval_operation op = - warn_on_specials_unop op; + let eval_unop ?(warn=false) eval_operation op = + if warn then warn_on_specials_unop op; match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) | Interval v -> eval_operation v - | Top -> onTop - | _ -> onTop (* TODO: Do better *) + | Top -> top () + | _ -> top () (* TODO: Do better *) let eval_binop eval_operation v1 v2 = let is_exact_before = is_exact (Interval v1) && is_exact (Interval v2) in @@ -675,15 +675,15 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function - | (l, h) -> Interval (Float_t.floor (Float_t.pred l), h) + let eval_inv_ceil = function (*TODO*) + | (l, h) -> Interval (Float_t.succ( Float_t.floor (Float_t.pred l)), h) - let eval_inv_floor = function - | (l, h) -> Interval (l, Float_t.ceil (Float_t.succ h)) + let eval_inv_floor = function (*TODO*) + | (l, h) -> Interval (l, Float_t.pred (Float_t.ceil (Float_t.succ h))) let eval_inv_fabs = function - | (_, h) when h > Float_t.zero -> Interval (Float_t.neg h, h) - | _ -> top () + | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) + | (_, h) -> Interval (Float_t.neg h, h) let isfinite op = match op with @@ -751,16 +751,23 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | PlusInfinity -> PlusInfinity | MinusInfinity -> MinusInfinity - let acos = eval_unop (top ()) eval_acos - let asin = eval_unop (top ()) eval_asin - let atan = eval_unop (top ()) eval_atan - let cos = eval_unop (top ()) eval_cos - let sin = eval_unop (top ()) eval_sin - let tan = eval_unop (top ()) eval_tan + let acos = eval_unop eval_acos + let asin = eval_unop eval_asin + let atan = eval_unop eval_atan + let cos = eval_unop eval_cos + let sin = eval_unop eval_sin + let tan = eval_unop eval_tan - let inv_ceil = eval_unop (top ()) eval_inv_ceil - let inv_floor = eval_unop (top ()) eval_inv_floor - let inv_fabs = eval_unop (top ()) eval_inv_fabs + let inv_ceil = eval_unop ~warn:false eval_inv_ceil + let inv_floor = eval_unop ~warn:false eval_inv_floor + let inv_fabs op = + match op with + | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) + | Top -> Top + | Interval v -> eval_inv_fabs v + | NaN -> NaN (* so we assume, fabs(NaN) = NaN?)*) + | PlusInfinity -> Top (* +/-inf *) + | MinusInfinity -> Bot end module F64Interval = FloatIntervalImpl(CDouble) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c new file mode 100644 index 0000000000..b60fe1ef18 --- /dev/null +++ b/tests/regression/57-floats/19-library-invariant.c @@ -0,0 +1,65 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include +#include + +void main() { + double f, g; + double x; + int unk; + + // isnan, isfinite + if(__builtin_isfinite(f)) { + __goblint_check(__builtin_isfinite(f)); + __goblint_check(! __builtin_isnan(f)); + } + if(__builtin_isnan(f)) { + __goblint_check(__builtin_isnan(f)); + __goblint_check(! __builtin_isfinite(f)); + } + + // Comparison + x = (unk) ? -100. : 100.; + if(__builtin_isgreater(x, 0.)) { + __goblint_check(x > 0.); + } + if(__builtin_isgreaterequal(x, 0.)) { + __goblint_check(x >= 0.); + } + if(__builtin_isless(x, 0.)) { + __goblint_check(x < 0.); + } + if(__builtin_islessequal(x, 0.)) { + __goblint_check(x <= 0.); + } + if(__builtin_islessgreater(x, 0.)) { + __goblint_check(x < 0. || x > 0.); // UNKNOWN + } + + // fabs + if(__builtin_fabs(f) == 4.) { + __goblint_check(f >= -4.); + __goblint_check(f <= 4.); + } + g = (unk) ? (3.) : (5.); + if(__builtin_fabs(f) == g) { + __goblint_check(f >= -5.); + __goblint_check(f <= 5.); + } + if(__builtin_fabs(f) == -6.) { + // DEAD + g = 0.; + } + + // ceil, floor + if(ceil(f) == 5.) { + __goblint_check(f <= 5.); + __goblint_check(f > 4.); + __goblint_check(f >= 4.5); // UNKNOWN! + } + if(floor(f) == 5.) { + __goblint_check(f >= 5.); + __goblint_check(f < 6.); + __goblint_check(f >= 5.5); // UNKNOWN! + } +} diff --git a/tests/regression/57-floats/19-specialEq.c b/tests/regression/57-floats/19-specialEq.c deleted file mode 100644 index 05eacbfa8d..0000000000 --- a/tests/regression/57-floats/19-specialEq.c +++ /dev/null @@ -1,15 +0,0 @@ -//PARAM: --set ana.activated[+] tmpSpecial -#include -#include - - -void main() { - double f; - int unk; - - if (__builtin_isnan(f) ) { - __goblint_check( __builtin_isfinite(f)); - } else { - __goblint_check( __builtin_isnan(f)); - } -} \ No newline at end of file diff --git a/tests/regression/57-floats/20-library-invariant-invalidate.c b/tests/regression/57-floats/20-library-invariant-invalidate.c new file mode 100644 index 0000000000..7a93264c9d --- /dev/null +++ b/tests/regression/57-floats/20-library-invariant-invalidate.c @@ -0,0 +1,15 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include + +void main() { + double f, g; + int unk; + + g = __builtin_fabs(f); + f = 7.; + + if(g == 5.) { + __goblint_check(f <= 5.); // FAIL + } +} From 6ca138fe69337246709d2e902cf3ac8d0ee910dd Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Thu, 9 Mar 2023 18:36:30 +0100 Subject: [PATCH 005/238] sound but less precise inversions of ceil/floor --- src/cdomains/floatDomain.ml | 8 ++++---- tests/regression/57-floats/19-library-invariant.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index a95926b29e..ca017111c1 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -675,11 +675,11 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function (*TODO*) - | (l, h) -> Interval (Float_t.succ( Float_t.floor (Float_t.pred l)), h) + let eval_inv_ceil = function (*TODO: can probably be more precise*) + | (l, h) -> Interval (Float_t.lower_bound, h) - let eval_inv_floor = function (*TODO*) - | (l, h) -> Interval (l, Float_t.pred (Float_t.ceil (Float_t.succ h))) + let eval_inv_floor = function (*TODO: can probably be more precise*) + | (l, h) -> Interval (l, Float_t.upper_bound) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index b60fe1ef18..535a306174 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -54,12 +54,12 @@ void main() { // ceil, floor if(ceil(f) == 5.) { __goblint_check(f <= 5.); - __goblint_check(f > 4.); + __goblint_check(f > 4.); // TODO __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); - __goblint_check(f < 6.); + __goblint_check(f < 6.); // TODO __goblint_check(f >= 5.5); // UNKNOWN! } } From 7849a7aef27123f323ecaa6442f8882f9b04bc65 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 14 Mar 2023 19:13:21 +0100 Subject: [PATCH 006/238] Use lval instead of var for dependencies --- src/analyses/baseInvariant.ml | 12 ++++++------ src/analyses/tmpSpecial.ml | 25 +++++++++++++------------ src/domains/queries.ml | 8 ++++---- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 92204d9bea..b0ae6fef93 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -702,10 +702,10 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); let tv = not (ID.leq c (ID.of_bool ik false)) in - begin match ctx.ask (Queries.TmpSpecial v) with + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) @@ -735,9 +735,9 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), _) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial v)); - begin match ctx.ask (Queries.TmpSpecial v) with + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index b5bda86289..7e5d636e41 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -12,7 +12,7 @@ struct module ML = LibraryDesc.MathLifted module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) module MlLsProd = Lattice.Prod (ML) (LS) - module D = MapDomain.MapBot (Basetype.Variables) (MlLsProd) + module D = MapDomain.MapBot (Lval.CilLval) (MlLsProd) module C = Lattice.Unit let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = @@ -52,7 +52,8 @@ struct let assign ctx (lval:lval) (rval:exp) : D.t = (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) let lvalsWritten = ls_of_lv ctx lval in - D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local + if M.tracing then M.trace "tmpSpecial" "lvalsWritten %a\n" LS.pretty lvalsWritten; + D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -74,19 +75,19 @@ struct (* Just dbg prints *) (match lval with - | Some (Var v, offs) -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a; vinfo %a; attr %a \n" f.vname d_lval (Var v, offs) d_varinfo v d_attrlist v.vattr + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a\n" f.vname d_lval lv | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); let desc = LibraryFunctions.find f in - (* add new math fun dec*) + (* add new math fun desc*) let d = match lval, desc.special arglist with - | (Some (Var v, _)), (Math { fun_args; }) -> + | Some (Var v, offs), (Math { fun_args; }) -> let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in if LS.is_top lvalDep then d else - D.add v ((ML.lift fun_args, lvalDep)) d - | _ -> d + D.add (v, resolve offs) ((ML.lift fun_args, lvalDep)) d + | _ -> d in (* remove entrys, dependent on lvals that were possibly written by the special function *) @@ -107,20 +108,20 @@ struct in let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in - D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs + D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs in let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in - D.filter (fun v (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs + D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs in - (* same for lval assignment of the call (though this should always be a "tmp___n")*) + (* same for lval assignment of the call*) let d = match lval with | Some lv -> ( (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) let lvalsWritten = ls_of_lv ctx lv in - D.filter (fun v (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d + D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d ) | None -> d in @@ -129,7 +130,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | TmpSpecial v -> let ml = fst (D.find v ctx.local) in + | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in if ML.is_bot ml then Queries.Result.top q else ml | _ -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 811b3f74c3..1d85c86070 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,7 +101,7 @@ type _ t = | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t - | TmpSpecial: varinfo -> ML.t t + | TmpSpecial: Lval.CilLval.t -> ML.t t type 'a result = 'a @@ -322,7 +322,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 - | Any (TmpSpecial vi1), Any (TmpSpecial vi2) -> CilType.Varinfo.compare vi1 vi2 + | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Lval.CilLval.compare lv1 lv2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -358,7 +358,7 @@ struct | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e - | Any (TmpSpecial vi) -> CilType.Varinfo.hash vi + | Any (TmpSpecial lv) -> Lval.CilLval.hash lv (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -412,7 +412,7 @@ struct | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf - | Any (TmpSpecial vi) -> Pretty.dprintf "TmpSpecial %a" CilType.Varinfo.pretty vi + | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Lval.CilLval.pretty lv end let to_value_domain_ask (ask: ask) = From 66404c4993456d55b52e50f332185d3a377537d0 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sun, 7 May 2023 15:25:06 +0200 Subject: [PATCH 007/238] fix invalidation --- src/analyses/tmpSpecial.ml | 30 +++++++++++------- .../20-library-invariant-invalidate.c | 31 +++++++++++++++---- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 7e5d636e41..c2042cd3d4 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -75,20 +75,9 @@ struct (* Just dbg prints *) (match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with\n lval %a\n" f.vname d_lval lv + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); let desc = LibraryFunctions.find f in - (* add new math fun desc*) - let d = - match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let lvalDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in - if LS.is_top lvalDep then - d - else - D.add (v, resolve offs) ((ML.lift fun_args, lvalDep)) d - | _ -> d - in (* remove entrys, dependent on lvals that were possibly written by the special function *) let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in @@ -125,9 +114,26 @@ struct ) | None -> d in + + (* add new math fun desc*) + let d = + match lval, desc.special arglist with + | Some (Var v, offs), (Math { fun_args; }) -> + let argsDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + let lvalsWritten = ls_of_lv ctx (Var v, offs) in + (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) + (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) + if LS.is_top argsDep || not (LS.is_empty (LS.meet argsDep lvalsWritten)) then + d + else + D.add (v, resolve offs) ((ML.lift fun_args, LS.union argsDep lvalsWritten)) d + | _ -> d + in + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; d + let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in diff --git a/tests/regression/57-floats/20-library-invariant-invalidate.c b/tests/regression/57-floats/20-library-invariant-invalidate.c index 7a93264c9d..bc00279af3 100644 --- a/tests/regression/57-floats/20-library-invariant-invalidate.c +++ b/tests/regression/57-floats/20-library-invariant-invalidate.c @@ -3,13 +3,32 @@ #include void main() { - double f, g; - int unk; + double f1, g1; + double f2, g2; + double unk_double; + double f3; - g = __builtin_fabs(f); - f = 7.; + // example 1: + g1 = __builtin_fabs(f1); + f1 = 7.; - if(g == 5.) { - __goblint_check(f <= 5.); // FAIL + if(g1 == 5.) { + __goblint_check(f1 <= 5.); // FAIL + } + + // example 2: + g2 = __builtin_fabs(f2); + g2 = unk_double; + + if(g2 == 5.) { + __goblint_check(f2 <= 5.); // UNKNOWN! + } + + // example 3: + // the check is not interesting, this only exists to make sure the analyzer can handle this case and terminates + f3 = __builtin_fabs(f3); + + if(f3 == 0.) { + __goblint_check(f3 <= 5.); } } From d99608a1e8cb1dd09ae9212c592699050cf65c9f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 8 May 2023 14:50:49 +0200 Subject: [PATCH 008/238] Fix issues after merge --- src/analyses/tmpSpecial.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index c2042cd3d4..8df101d7c1 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,7 +1,7 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) -open Prelude.Ana +open GoblintCil open Analyses module Spec = @@ -26,7 +26,7 @@ struct | (Var v, offs) -> LS.of_list [(v, resolve offs)] | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) - let rec ls_of_exp ctx (exp:exp) : LS.t = + let rec ls_of_exp ctx (exp:exp) : LS.t = match exp with | Const _ -> LS.bot () | Lval lv -> ls_of_lv ctx lv @@ -95,11 +95,11 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> + let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs in - let d = List.fold_left (fun accD addr -> + let d = List.fold_left (fun accD addr -> let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs in @@ -112,14 +112,14 @@ struct let lvalsWritten = ls_of_lv ctx lv in D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d ) - | None -> d + | None -> d in (* add new math fun desc*) let d = match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let argsDep = List.fold LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in + | Some (Var v, offs), (Math { fun_args; }) -> + let argsDep = List.fold_left LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in let lvalsWritten = ls_of_lv ctx (Var v, offs) in (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) From 818787318e982d0bd5948800151130ee57aecfe2 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 20 May 2023 08:38:58 +0200 Subject: [PATCH 009/238] implement Github suggestions --- src/analyses/baseInvariant.ml | 33 ++++++++++++++++++--------------- src/analyses/libraryDesc.ml | 28 +--------------------------- src/analyses/tmpSpecial.ml | 24 ++++++++++++++---------- src/cdomains/floatDomain.ml | 10 ---------- src/cdomains/floatDomain.mli | 2 -- src/domains/queries.ml | 2 +- 6 files changed, 34 insertions(+), 65 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 8f2cc67fd7..1d0f34ef14 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -550,7 +550,7 @@ struct let unroll_fk_of_exp e = match unrollType (Cilfacade.typeOf e) with | TFloat (fk, _) -> fk - | _ -> failwith "impossible" + | _ -> failwith "value which was expected to be a float is of different type?!" in let rec inv_exp c_typed exp (st:D.t): D.t = (* trying to improve variables in an expression so it is bottom means dead code *) @@ -704,18 +704,21 @@ struct begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); - let tv = not (ID.leq c (ID.of_bool ik false)) in - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | `Lifted (Isunordered (xFloat, yFloat)) -> st (* something can probably be done here *) - | _ -> st + let tv_opt = ID.to_bool c in + begin match tv_opt with + | Some tv -> + begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> st + end + | None -> st end | _ -> st end @@ -738,8 +741,8 @@ struct | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) when FD.is_interval c -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 1b915faa01..d9afb20bee 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -150,33 +150,7 @@ module MathPrintable = struct let name () = "MathPrintable" - let relift = function - | Nan (fk, exp) -> Nan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Inf fk -> Inf (CilType.Fkind.relift fk) - | Isfinite exp -> Isfinite (CilType.Exp.relift exp) - | Isinf exp -> Isinf (CilType.Exp.relift exp) - | Isnan exp -> Isnan (CilType.Exp.relift exp) - | Isnormal exp -> Isnormal (CilType.Exp.relift exp) - | Signbit exp -> Signbit (CilType.Exp.relift exp) - | Isgreater (exp1, exp2) -> Isgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isgreaterequal (exp1, exp2) -> Isgreaterequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isless (exp1, exp2) -> Isless (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Islessequal (exp1, exp2) -> Islessequal (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Islessgreater (exp1, exp2) -> Islessgreater (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Isunordered (exp1, exp2) -> Isunordered (CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Ceil (fk, exp) -> Ceil (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Floor (fk, exp) -> Floor (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Fabs (fk, exp) -> Fabs (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Fmax (fk, exp1, exp2) -> Fmax (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Fmin (fk, exp1, exp2) -> Fmin (fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Acos (fk, exp) -> Acos (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Asin (fk, exp) -> Asin (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Atan (fk, exp) -> Atan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Atan2 (fk, exp1, exp2) -> Atan2 (CilType.Fkind.relift fk, CilType.Exp.relift exp1, CilType.Exp.relift exp2) - | Cos (fk, exp) -> Cos (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Sin (fk, exp) -> Sin (CilType.Fkind.relift fk, CilType.Exp.relift exp) - | Tan (fk, exp) -> Tan (CilType.Fkind.relift fk, CilType.Exp.relift exp) - + let relift ml = ml let order = function | Nan _ -> 1 diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 8df101d7c1..a9b65189b2 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -28,22 +28,22 @@ struct let rec ls_of_exp ctx (exp:exp) : LS.t = match exp with - | Const _ -> LS.bot () + | Const _ -> LS.empty () | Lval lv -> ls_of_lv ctx lv - | SizeOf _ -> LS.bot () + | SizeOf _ -> LS.empty () | Real e -> ls_of_exp ctx e | Imag e -> ls_of_exp ctx e | SizeOfE e -> ls_of_exp ctx e | SizeOfStr _ -> LS.empty () - | AlignOf _ -> LS.top () (* TODO: what is this*) - | AlignOfE _ -> LS.top () (* TODO: what is this*) + | AlignOf _ -> LS.empty () + | AlignOfE e -> ls_of_exp ctx e | UnOp (_,e,_) -> ls_of_exp ctx e | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) | CastE (_,e) -> ls_of_exp ctx e | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) - | AddrOfLabel _ -> LS.top () (* TODO: what is this*) - | StartOf _ -> LS.top () (* TODO: what is this*) + | AddrOfLabel _ -> LS.empty () + | StartOf _ -> ctx.ask (Queries.MayPointTo exp) let context _ _ = () @@ -65,20 +65,24 @@ struct ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) [ctx.local, D.bot ()] let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let d = ctx.local in (* Just dbg prints *) - (match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv - | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - let desc = LibraryFunctions.find f in + (if M.tracing then + match lval with + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + let desc = LibraryFunctions.find f in (* remove entrys, dependent on lvals that were possibly written by the special function *) let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index ca017111c1..e7117c9b62 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -101,7 +101,6 @@ module type FloatDomainBase = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool end module FloatIntervalImpl(Float_t : CFloatType) = struct @@ -182,10 +181,6 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | MinusInfinity -> true | _ -> false - let is_interval = function - | Interval _ -> true - | _ -> false - let norm = function | Interval (low, high) as x -> if Float_t.is_finite low && Float_t.is_finite high then @@ -802,7 +797,6 @@ module type FloatDomain = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end @@ -909,7 +903,6 @@ module FloatIntervalImplLifted = struct let minus_inf_of fkind = dispatch_fkind fkind (F1.minus_inf, F2.minus_inf) let is_inf = dispatch (F1.is_inf, F2.is_inf) let is_neg_inf = dispatch (F1.is_minus_inf, F2.is_minus_inf) - let is_interval = dispatch (F1.is_interval, F2.is_interval) let get_fkind = function | F32 _ -> FFloat @@ -1075,9 +1068,6 @@ module FloatDomTupleImpl = struct let is_exact = exists % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_exact); } - let is_interval = - for_all - % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_interval); } let is_top = for_all % mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.is_top); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 67ec9d17dd..4052f633a7 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -103,7 +103,6 @@ module type FloatDomainBase = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool end (* Only exposed for testing *) @@ -139,7 +138,6 @@ module type FloatDomain = sig val maximal: t -> float option val is_exact : t -> bool - val is_interval : t -> bool val get_fkind : t -> Cil.fkind val invariant: Cil.exp -> t -> Invariant.t end diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1d85c86070..80a9e05e09 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -279,7 +279,7 @@ struct | Any ActiveJumpBuf -> 46 | Any ValidLongJmp -> 47 | Any (MayBeModifiedSinceSetjmp _) -> 48 - | Any (TmpSpecial _) -> 42 + | Any (TmpSpecial _) -> 49 let rec compare a b = let r = Stdlib.compare (order a) (order b) in From 1fd2af385238642262df93129d83ddd76dc1281e Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Sat, 20 May 2023 12:18:21 +0200 Subject: [PATCH 010/238] derive math printable --- src/analyses/libraryDesc.ml | 138 +++++++----------------------------- 1 file changed, 27 insertions(+), 111 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index d9afb20bee..903360a603 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -13,31 +13,31 @@ struct end type math = - | Nan of (Cil.fkind * Cil.exp) - | Inf of Cil.fkind - | Isfinite of Cil.exp - | Isinf of Cil.exp - | Isnan of Cil.exp - | Isnormal of Cil.exp - | Signbit of Cil.exp - | Isgreater of (Cil.exp * Cil.exp) - | Isgreaterequal of (Cil.exp * Cil.exp) - | Isless of (Cil.exp * Cil.exp) - | Islessequal of (Cil.exp * Cil.exp) - | Islessgreater of (Cil.exp * Cil.exp) - | Isunordered of (Cil.exp * Cil.exp) - | Ceil of (Cil.fkind * Cil.exp) - | Floor of (Cil.fkind * Cil.exp) - | Fabs of (Cil.fkind * Cil.exp) - | Fmax of (Cil.fkind * Cil.exp * Cil.exp) - | Fmin of (Cil.fkind * Cil.exp * Cil.exp) - | Acos of (Cil.fkind * Cil.exp) - | Asin of (Cil.fkind * Cil.exp) - | Atan of (Cil.fkind * Cil.exp) - | Atan2 of (Cil.fkind * Cil.exp * Cil.exp) - | Cos of (Cil.fkind * Cil.exp) - | Sin of (Cil.fkind * Cil.exp) - | Tan of (Cil.fkind * Cil.exp) + | Nan of (CilType.Fkind.t * Basetype.CilExp.t) + | Inf of CilType.Fkind.t + | Isfinite of Basetype.CilExp.t + | Isinf of Basetype.CilExp.t + | Isnan of Basetype.CilExp.t + | Isnormal of Basetype.CilExp.t + | Signbit of Basetype.CilExp.t + | Isgreater of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isgreaterequal of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isless of (Basetype.CilExp.t * Basetype.CilExp.t) + | Islessequal of (Basetype.CilExp.t * Basetype.CilExp.t) + | Islessgreater of (Basetype.CilExp.t * Basetype.CilExp.t) + | Isunordered of (Basetype.CilExp.t * Basetype.CilExp.t) + | Ceil of (CilType.Fkind.t * Basetype.CilExp.t) + | Floor of (CilType.Fkind.t * Basetype.CilExp.t) + | Fabs of (CilType.Fkind.t * Basetype.CilExp.t) + | Fmax of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Fmin of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Acos of (CilType.Fkind.t * Basetype.CilExp.t) + | Asin of (CilType.Fkind.t * Basetype.CilExp.t) + | Atan of (CilType.Fkind.t * Basetype.CilExp.t) + | Atan2 of (CilType.Fkind.t * Basetype.CilExp.t * Basetype.CilExp.t) + | Cos of (CilType.Fkind.t * Basetype.CilExp.t) + | Sin of (CilType.Fkind.t * Basetype.CilExp.t) + | Tan of (CilType.Fkind.t * Basetype.CilExp.t) [@@deriving eq, ord, hash] (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) @@ -146,96 +146,12 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): module MathPrintable = struct include Printable.Std - type t = math + type t = math [@@deriving eq, ord, hash] let name () = "MathPrintable" let relift ml = ml - let order = function - | Nan _ -> 1 - | Inf _ -> 2 - | Isfinite _ -> 3 - | Isinf _ -> 4 - | Isnan _ -> 5 - | Isnormal _ -> 6 - | Signbit _ -> 7 - | Isgreater _ -> 8 - | Isgreaterequal _ -> 9 - | Isless _ -> 10 - | Islessequal _ -> 11 - | Islessgreater _ -> 12 - | Isunordered _ -> 13 - | Ceil _ -> 14 - | Floor _ -> 15 - | Fabs _ -> 16 - | Fmax _ -> 17 - | Fmin _ -> 18 - | Acos _ -> 19 - | Asin _ -> 20 - | Atan _ -> 21 - | Atan2 _ -> 22 - | Cos _ -> 23 - | Sin _ -> 24 - | Tan _ -> 25 - - let equal m1 m2 = (compare m1 m2) == 0 - let hash = order - - let cmp_fk_exp (fk1, e1) (fk2, e2) = - let r = (CilType.Fkind.compare fk1 fk2) in - if r <> 0 then - r - else - CilType.Exp.compare e1 e2 - - let cmp_exp_exp (e1, e1') (e2, e2') = - let r = (CilType.Exp.compare e1 e2) in - if r <> 0 then - r - else - CilType.Exp.compare e1' e2' - - let cmp_fk_exp_exp (fk1, e1, e1') (fk2, e2, e2') = - let r = (CilType.Fkind.compare fk1 fk2) in - if r <> 0 then - r - else - cmp_exp_exp (e1, e1') (e2, e2') - - let compare m1 m2 = - let r = Stdlib.compare (order m1) (order m2) in - if r <> 0 then - r - else - match m1, m2 with - | Nan fe1, Nan fe2 -> cmp_fk_exp fe1 fe2 - | Inf fk1, Inf fk2 -> CilType.Fkind.compare fk1 fk2 - | Isfinite e1, Isfinite e2 -> CilType.Exp.compare e1 e2 - | Isinf e1, Isinf e2 -> CilType.Exp.compare e1 e2 - | Isnan e1, Isnan e2 -> CilType.Exp.compare e1 e2 - | Isnormal e1, Isnormal e2 -> CilType.Exp.compare e1 e2 - | Signbit e1, Signbit e2 -> CilType.Exp.compare e1 e2 - | Isgreater ee1, Isgreater ee2 -> cmp_exp_exp ee1 ee2 - | Isgreaterequal ee1, Isgreaterequal ee2 -> cmp_exp_exp ee1 ee2 - | Isless ee1, Isless ee2 -> cmp_exp_exp ee1 ee2 - | Islessequal ee1, Islessequal ee2 -> cmp_exp_exp ee1 ee2 - | Islessgreater ee1, Islessgreater ee2 -> cmp_exp_exp ee1 ee2 - | Isunordered ee1, Isunordered ee2 -> cmp_exp_exp ee1 ee2 - | Ceil fe1, Ceil fe2 -> cmp_fk_exp fe1 fe2 - | Floor fe1, Floor fe2 -> cmp_fk_exp fe1 fe2 - | Fabs fe1, Fabs fe2 -> cmp_fk_exp fe1 fe2 - | Fmax fee1, Fmax fee2 -> cmp_fk_exp_exp fee1 fee2 - | Fmin fee1, Fmin fee2 -> cmp_fk_exp_exp fee1 fee2 - | Acos fe1, Acos fe2 -> cmp_fk_exp fe1 fe2 - | Asin fe1, Asin fe2 -> cmp_fk_exp fe1 fe2 - | Atan fe1, Atan fe2 -> cmp_fk_exp fe1 fe2 - | Atan2 fee1, Atan2 fee2 -> cmp_fk_exp_exp fee1 fee2 - | Cos fe1, Cos fe2 -> cmp_fk_exp fe1 fe2 - | Sin fe1, Sin fe2 -> cmp_fk_exp fe1 fe2 - | Tan fe1, Tan fe2 -> cmp_fk_exp fe1 fe2 - | _ -> failwith "impossible" - let show = function | Nan _ -> "nan" | Inf _ -> "inf" @@ -295,6 +211,6 @@ module MathPrintable = struct end module MathLifted = Lattice.Flat (MathPrintable) (struct - let top_name = "Unknown math desc" + let top_name = "Unknown or no math desc" let bot_name = "Nonexistent math desc" end) From 856dccb47d6fd05498a9e01d541da00ccdfe2301 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 18:58:53 +0300 Subject: [PATCH 011/238] Add fopencookie to libraryFunctions --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3eacf9013a..93eb75c09b 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -257,6 +257,7 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strsep", unknown [drop "stringp" [r_deep; w]; drop "delim" [r]]); ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); + ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 8753565a893c93e399fc233dd3f0234a86e0cf62 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 19:04:03 +0300 Subject: [PATCH 012/238] Remove functions already defined in libraries from invalidate_actions --- src/analyses/libraryFunctions.ml | 48 -------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 93eb75c09b..5a16c9edcf 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -671,10 +671,7 @@ let invalidate_actions = [ "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) - "_spin_unlock_irqrestore", readsAll;(*safe*) - "pthread_mutex_init", readsAll;(*safe*) "pthread_mutex_destroy", readsAll;(*safe*) - "pthread_mutexattr_settype", readsAll;(*safe*) "pthread_mutexattr_init", readsAll;(*safe*) "pthread_spin_init", readsAll;(*safe*) "pthread_spin_destroy", readsAll;(*safe*) @@ -718,7 +715,6 @@ let invalidate_actions = [ "getc", writesAll;(*unsafe*) "_IO_getc", writesAll;(*unsafe*) "closedir", writesAll;(*unsafe*) - "setrlimit", readsAll;(*safe*) "chdir", readsAll;(*safe*) "pipe", writesAll;(*unsafe*) "close", writesAll;(*unsafe*) @@ -732,9 +728,6 @@ let invalidate_actions = [ "pthread_attr_getstacksize", readsAll;(*safe*) "pthread_attr_getscope", readsAll;(*safe*) "pthread_cond_init", readsAll; (*safe*) - "pthread_cond_wait", readsAll; (*safe*) - "pthread_cond_signal", readsAll;(*safe*) - "pthread_cond_broadcast", readsAll;(*safe*) "pthread_cond_destroy", readsAll;(*safe*) "__pthread_cond_init", readsAll; (*safe*) "__pthread_cond_wait", readsAll; (*safe*) @@ -747,7 +740,6 @@ let invalidate_actions = [ "pthread_sigmask", writesAllButFirst 2 readsAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_object_size", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) @@ -827,7 +819,6 @@ let invalidate_actions = [ "usleep", readsAll; "svc_run", writesAll;(*unsafe*) "dup", readsAll; (*safe*) - "__builtin_expect", readsAll; (*safe*) "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) @@ -852,7 +843,6 @@ let invalidate_actions = [ "fgets", writes [1;3]; (*keep [3]*) "__fgets_alias", writes [1;3]; (*keep [3]*) "__fgets_chk", writes [1;3]; (*keep [3]*) - "strtoul", readsAll; (*safe*) "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) "strsignal", readsAll; @@ -882,12 +872,10 @@ let invalidate_actions = [ "sem_wait", readsAll; (*safe*) "sem_post", readsAll; (*safe*) "PL_NewHashTable", readsAll; (*safe*) - "__assert_fail", readsAll; (*safe*) "assert_failed", readsAll; (*safe*) "htonl", readsAll; (*safe*) "htons", readsAll; (*safe*) "ntohl", readsAll; (*safe*) - "htons", readsAll; (*safe*) "munmap", readsAll;(*safe*) "mmap", readsAll;(*safe*) "clock", readsAll; @@ -908,15 +896,12 @@ let invalidate_actions = [ "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) "dev_driver_string", readsAll; - "dev_driver_string", readsAll; "__spin_lock_init", writes [1]; "kmem_cache_create", readsAll; "idr_pre_get", readsAll; "zil_replay", writes [1;2;3;5]; "__VERIFIER_nondet_int", readsAll; (* no args, declare invalidate actions to prevent invalidating globals when extern in regression tests *) (* no args, declare invalidate actions to prevent invalidating globals *) - "__VERIFIER_atomic_begin", readsAll; - "__VERIFIER_atomic_end", readsAll; "isatty", readsAll; "setpriority", readsAll; "getpriority", readsAll; @@ -927,42 +912,24 @@ let invalidate_actions = [ "sema_init", readsAll; "down_trylock", readsAll; "up", readsAll; - "acos", readsAll; - "acosf", readsAll; "acosh", readsAll; "acoshf", readsAll; "acoshl", readsAll; - "acosl", readsAll; - "asin", readsAll; - "asinf", readsAll; "asinh", readsAll; "asinhf", readsAll; "asinhl", readsAll; - "asinl", readsAll; - "atan", readsAll; - "atan2", readsAll; - "atan2f", readsAll; - "atan2l", readsAll; - "atanf", readsAll; "atanh", readsAll; "atanhf", readsAll; "atanhl", readsAll; - "atanl", readsAll; "cbrt", readsAll; "cbrtf", readsAll; "cbrtl", readsAll; - "ceil", readsAll; - "ceilf", readsAll; - "ceill", readsAll; "copysign", readsAll; "copysignf", readsAll; "copysignl", readsAll; - "cos", readsAll; - "cosf", readsAll; "cosh", readsAll; "coshf", readsAll; "coshl", readsAll; - "cosl", readsAll; "erf", readsAll; "erfc", readsAll; "erfcf", readsAll; @@ -984,12 +951,6 @@ let invalidate_actions = [ "fma", readsAll; "fmaf", readsAll; "fmal", readsAll; - "fmax", readsAll; - "fmaxf", readsAll; - "fmaxl", readsAll; - "fmin", readsAll; - "fminf", readsAll; - "fminl", readsAll; "fmod", readsAll; "fmodf", readsAll; "fmodl", readsAll; @@ -1041,9 +1002,6 @@ let invalidate_actions = [ "modf", readsAll; "modff", readsAll; "modfl", readsAll; - "nan", readsAll; - "nanf", readsAll; - "nanl", readsAll; "nearbyint", readsAll; "nearbyintf", readsAll; "nearbyintl", readsAll; @@ -1074,21 +1032,15 @@ let invalidate_actions = [ "scalbn", readsAll; "scalbnf", readsAll; "scalbnl", readsAll; - "sin", readsAll; - "sinf", readsAll; "sinh", readsAll; "sinhf", readsAll; "sinhl", readsAll; - "sinl", readsAll; "sqrt", readsAll; "sqrtf", readsAll; "sqrtl", readsAll; - "tan", readsAll; - "tanf", readsAll; "tanh", readsAll; "tanhf", readsAll; "tanhl", readsAll; - "tanl", readsAll; "tgamma", readsAll; "tgammaf", readsAll; "tgammal", readsAll; From a0b390930b895640301e73d75486e9ba919983ba Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 7 Jun 2023 19:05:17 +0300 Subject: [PATCH 013/238] Add failwith to prevent goblint devs from adding functions already in libraries to invalidate_actions --- src/analyses/libraryFunctions.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5a16c9edcf..7940c03045 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1053,6 +1053,10 @@ let invalidate_actions = [ "__goblint_assume_join", readsAll; ] +let () = List.iter (fun (x, _) -> + if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then + failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); + ) invalidate_actions (* used by get_invalidate_action to make sure * that hash of invalidates is built only once From 81db59318819a89cea0757dd4082f4bb26acfb75 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 8 Jun 2023 17:54:12 +0300 Subject: [PATCH 014/238] Convert all math.h functions to new specifications --- src/analyses/libraryFunctions.ml | 276 +++++++++++++++---------------- 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7940c03045..0d432e0efa 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -398,6 +398,144 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("tan", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FDouble, x)) }); ("tanf", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FFloat, x)) }); ("tanl", special [__ "x" []] @@ fun x -> Math { fun_args = (Tan (FLongDouble, x)) }); + ("acosh", unknown [drop "x" []]); + ("acoshf", unknown [drop "x" []]); + ("acoshl", unknown [drop "x" []]); + ("asinh", unknown [drop "x" []]); + ("asinhf", unknown [drop "x" []]); + ("asinhl", unknown [drop "x" []]); + ("atanh", unknown [drop "x" []]); + ("atanhf", unknown [drop "x" []]); + ("atanhl", unknown [drop "x" []]); + ("cosh", unknown [drop "x" []]); + ("coshf", unknown [drop "x" []]); + ("coshl", unknown [drop "x" []]); + ("sinh", unknown [drop "x" []]); + ("sinhf", unknown [drop "x" []]); + ("sinhl", unknown [drop "x" []]); + ("tanh", unknown [drop "x" []]); + ("tanhf", unknown [drop "x" []]); + ("tanhl", unknown [drop "x" []]); + ("cbrt", unknown [drop "x" []]); + ("cbrtf", unknown [drop "x" []]); + ("cbrtl", unknown [drop "x" []]); + ("copysign", unknown [drop "x" []; drop "y" []]); + ("copysignf", unknown [drop "x" []; drop "y" []]); + ("copysignl", unknown [drop "x" []; drop "y" []]); + ("erf", unknown [drop "x" []]); + ("erff", unknown [drop "x" []]); + ("erfl", unknown [drop "x" []]); + ("erfc", unknown [drop "x" []]); + ("erfcf", unknown [drop "x" []]); + ("erfcl", unknown [drop "x" []]); + ("exp", unknown [drop "x" []]); + ("expf", unknown [drop "x" []]); + ("expl", unknown [drop "x" []]); + ("exp2", unknown [drop "x" []]); + ("exp2f", unknown [drop "x" []]); + ("exp2l", unknown [drop "x" []]); + ("expm1", unknown [drop "x" []]); + ("expm1f", unknown [drop "x" []]); + ("expm1l", unknown [drop "x" []]); + ("fdim", unknown [drop "x" []; drop "y" []]); + ("fdimf", unknown [drop "x" []; drop "y" []]); + ("fdiml", unknown [drop "x" []; drop "y" []]); + ("fma", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmaf", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmal", unknown [drop "x" []; drop "y" []; drop "z" []]); + ("fmod", unknown [drop "x" []; drop "y" []]); + ("fmodf", unknown [drop "x" []; drop "y" []]); + ("fmodl", unknown [drop "x" []; drop "y" []]); + ("frexp", unknown [drop "arg" []; drop "exp" [w]]); + ("frexpf", unknown [drop "arg" []; drop "exp" [w]]); + ("frexpl", unknown [drop "arg" []; drop "exp" [w]]); + ("hypot", unknown [drop "x" []; drop "y" []]); + ("hypotf", unknown [drop "x" []; drop "y" []]); + ("hypotl", unknown [drop "x" []; drop "y" []]); + ("ilogb", unknown [drop "x" []]); + ("ilogbf", unknown [drop "x" []]); + ("ilogbl", unknown [drop "x" []]); + ("ldexp", unknown [drop "arg" []; drop "exp" []]); + ("ldexpf", unknown [drop "arg" []; drop "exp" []]); + ("ldexpl", unknown [drop "arg" []; drop "exp" []]); + ("lgamma", unknown [drop "x" []]); + ("lgammaf", unknown [drop "x" []]); + ("lgammal", unknown [drop "x" []]); + ("log", unknown [drop "x" []]); + ("logf", unknown [drop "x" []]); + ("logl", unknown [drop "x" []]); + ("log10", unknown [drop "x" []]); + ("log10f", unknown [drop "x" []]); + ("log10l", unknown [drop "x" []]); + ("log1p", unknown [drop "x" []]); + ("log1pf", unknown [drop "x" []]); + ("log1pl", unknown [drop "x" []]); + ("log2", unknown [drop "x" []]); + ("log2f", unknown [drop "x" []]); + ("log2l", unknown [drop "x" []]); + ("logb", unknown [drop "x" []]); + ("logbf", unknown [drop "x" []]); + ("logbl", unknown [drop "x" []]); + ("rint", unknown [drop "x" []]); + ("rintf", unknown [drop "x" []]); + ("rintl", unknown [drop "x" []]); + ("lrint", unknown [drop "x" []]); + ("lrintf", unknown [drop "x" []]); + ("lrintl", unknown [drop "x" []]); + ("llrint", unknown [drop "x" []]); + ("llrintf", unknown [drop "x" []]); + ("llrintl", unknown [drop "x" []]); + ("round", unknown [drop "x" []]); + ("roundf", unknown [drop "x" []]); + ("roundl", unknown [drop "x" []]); + ("lround", unknown [drop "x" []]); + ("lroundf", unknown [drop "x" []]); + ("lroundl", unknown [drop "x" []]); + ("llround", unknown [drop "x" []]); + ("llroundf", unknown [drop "x" []]); + ("llroundl", unknown [drop "x" []]); + ("modf", unknown [drop "arg" []; drop "iptr" [w]]); + ("modff", unknown [drop "arg" []; drop "iptr" [w]]); + ("modfl", unknown [drop "arg" []; drop "iptr" [w]]); + ("nearbyint", unknown [drop "x" []]); + ("nearbyintf", unknown [drop "x" []]); + ("nearbyintl", unknown [drop "x" []]); + ("nextafter", unknown [drop "from" []; drop "to" []]); + ("nextafterf", unknown [drop "from" []; drop "to" []]); + ("nextafterl", unknown [drop "from" []; drop "to" []]); + ("nexttoward", unknown [drop "from" []; drop "to" []]); + ("nexttowardf", unknown [drop "from" []; drop "to" []]); + ("nexttowardl", unknown [drop "from" []; drop "to" []]); + ("pow", unknown [drop "base" []; drop "exponent" []]); + ("powf", unknown [drop "base" []; drop "exponent" []]); + ("powl", unknown [drop "base" []; drop "exponent" []]); + ("remainder", unknown [drop "x" []; drop "y" []]); + ("remainderf", unknown [drop "x" []; drop "y" []]); + ("remainderl", unknown [drop "x" []; drop "y" []]); + ("remquo", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("remquof", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("remquol", unknown [drop "x" []; drop "y" []; drop "quo" [w]]); + ("scalbn", unknown [drop "arg" []; drop "exp" []]); + ("scalbnf", unknown [drop "arg" []; drop "exp" []]); + ("scalbnl", unknown [drop "arg" []; drop "exp" []]); + ("scalbln", unknown [drop "arg" []; drop "exp" []]); + ("scalblnf", unknown [drop "arg" []; drop "exp" []]); + ("scalblnl", unknown [drop "arg" []; drop "exp" []]); + ("sqrt", unknown [drop "x" []]); + ("sqrtf", unknown [drop "x" []]); + ("sqrtl", unknown [drop "x" []]); + ("tgamma", unknown [drop "x" []]); + ("tgammaf", unknown [drop "x" []]); + ("tgammal", unknown [drop "x" []]); + ("trunc", unknown [drop "x" []]); + ("truncf", unknown [drop "x" []]); + ("truncl", unknown [drop "x" []]); + ("j0", unknown [drop "x" []]); (* GNU C Library special function *) + ("j1", unknown [drop "x" []]); (* GNU C Library special function *) + ("jn", unknown [drop "n" []; drop "x" []]); (* GNU C Library special function *) + ("y0", unknown [drop "x" []]); (* GNU C Library special function *) + ("y1", unknown [drop "x" []]); (* GNU C Library special function *) + ("yn", unknown [drop "n" []; drop "x" []]); (* GNU C Library special function *) ("fegetround", unknown []); ("fesetround", unknown [drop "round" []]); (* Our float domain is rounding agnostic *) ("__builtin_fpclassify", unknown [drop "nan" []; drop "infinite" []; drop "normal" []; drop "subnormal" []; drop "zero" []; drop "x" []]); (* TODO: We could do better here *) @@ -912,144 +1050,6 @@ let invalidate_actions = [ "sema_init", readsAll; "down_trylock", readsAll; "up", readsAll; - "acosh", readsAll; - "acoshf", readsAll; - "acoshl", readsAll; - "asinh", readsAll; - "asinhf", readsAll; - "asinhl", readsAll; - "atanh", readsAll; - "atanhf", readsAll; - "atanhl", readsAll; - "cbrt", readsAll; - "cbrtf", readsAll; - "cbrtl", readsAll; - "copysign", readsAll; - "copysignf", readsAll; - "copysignl", readsAll; - "cosh", readsAll; - "coshf", readsAll; - "coshl", readsAll; - "erf", readsAll; - "erfc", readsAll; - "erfcf", readsAll; - "erfcl", readsAll; - "erff", readsAll; - "erfl", readsAll; - "exp", readsAll; - "exp2", readsAll; - "exp2f", readsAll; - "exp2l", readsAll; - "expf", readsAll; - "expl", readsAll; - "expm1", readsAll; - "expm1f", readsAll; - "expm1l", readsAll; - "fdim", readsAll; - "fdimf", readsAll; - "fdiml", readsAll; - "fma", readsAll; - "fmaf", readsAll; - "fmal", readsAll; - "fmod", readsAll; - "fmodf", readsAll; - "fmodl", readsAll; - "frexp", readsAll; - "frexpf", readsAll; - "frexpl", readsAll; - "hypot", readsAll; - "hypotf", readsAll; - "hypotl", readsAll; - "ilogb", readsAll; - "ilogbf", readsAll; - "ilogbl", readsAll; - "j0", readsAll; - "j1", readsAll; - "jn", readsAll; - "ldexp", readsAll; - "ldexpf", readsAll; - "ldexpl", readsAll; - "lgamma", readsAll; - "lgammaf", readsAll; - "lgammal", readsAll; - "llrint", readsAll; - "llrintf", readsAll; - "llrintl", readsAll; - "llround", readsAll; - "llroundf", readsAll; - "llroundl", readsAll; - "log", readsAll; - "log10", readsAll; - "log10f", readsAll; - "log10l", readsAll; - "log1p", readsAll; - "log1pf", readsAll; - "log1pl", readsAll; - "log2", readsAll; - "log2f", readsAll; - "log2l", readsAll; - "logb", readsAll; - "logbf", readsAll; - "logbl", readsAll; - "logf", readsAll; - "logl", readsAll; - "lrint", readsAll; - "lrintf", readsAll; - "lrintl", readsAll; - "lround", readsAll; - "lroundf", readsAll; - "lroundl", readsAll; - "modf", readsAll; - "modff", readsAll; - "modfl", readsAll; - "nearbyint", readsAll; - "nearbyintf", readsAll; - "nearbyintl", readsAll; - "nextafter", readsAll; - "nextafterf", readsAll; - "nextafterl", readsAll; - "nexttoward", readsAll; - "nexttowardf", readsAll; - "nexttowardl", readsAll; - "pow", readsAll; - "powf", readsAll; - "powl", readsAll; - "remainder", readsAll; - "remainderf", readsAll; - "remainderl", readsAll; - "remquo", readsAll; - "remquof", readsAll; - "remquol", readsAll; - "rint", readsAll; - "rintf", readsAll; - "rintl", readsAll; - "round", readsAll; - "roundf", readsAll; - "roundl", readsAll; - "scalbln", readsAll; - "scalblnf", readsAll; - "scalblnl", readsAll; - "scalbn", readsAll; - "scalbnf", readsAll; - "scalbnl", readsAll; - "sinh", readsAll; - "sinhf", readsAll; - "sinhl", readsAll; - "sqrt", readsAll; - "sqrtf", readsAll; - "sqrtl", readsAll; - "tanh", readsAll; - "tanhf", readsAll; - "tanhl", readsAll; - "tgamma", readsAll; - "tgammaf", readsAll; - "tgammal", readsAll; - "trunc", readsAll; - "truncf", readsAll; - "truncl", readsAll; - "y0", readsAll; - "y1", readsAll; - "yn", readsAll; "__goblint_assume_join", readsAll; ] From e94f1c41c5f1b4b4068adc976efd6bf567a90cd6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 8 Jun 2023 18:21:10 +0300 Subject: [PATCH 015/238] Convert some __builtin functions to new specifications --- src/analyses/libraryFunctions.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0d432e0efa..5ea4fdf3fa 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -182,6 +182,14 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ (** GCC builtin functions. These are not builtin versions of functions from other lists. *) let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("__builtin_bswap16", unknown [drop "x" []]); + ("__builtin_bswap32", unknown [drop "x" []]); + ("__builtin_bswap64", unknown [drop "x" []]); + ("__builtin_bswap128", unknown [drop "x" []]); + ("__builtin_ctz", unknown [drop "x" []]); + ("__builtin_ctzl", unknown [drop "x" []]); + ("__builtin_ctzll", unknown [drop "x" []]); + ("__builtin_clz", unknown [drop "x" []]); ("__builtin_object_size", unknown [drop "ptr" [r]; drop' []]); ("__builtin_prefetch", unknown (drop "addr" [] :: VarArgs (drop' []))); ("__builtin_expect", special [__ "exp" []; drop' []] @@ fun exp -> Identity exp); (* Identity, because just compiler optimization annotation. *) @@ -765,10 +773,6 @@ open Invalidate (* WTF: why are argument numbers 1-indexed (in partition)? *) let invalidate_actions = [ "atoi", readsAll; (*safe*) - "__builtin_ctz", readsAll; - "__builtin_ctzl", readsAll; - "__builtin_ctzll", readsAll; - "__builtin_clz", readsAll; "connect", readsAll; (*safe*) "fclose", readsAll; (*safe*) "fflush", writesAll; (*unsafe*) @@ -1026,10 +1030,6 @@ let invalidate_actions = [ "pthread_rwlock_destroy", readsAll; "pthread_rwlock_init", readsAll; "pthread_rwlock_unlock", readsAll; - "__builtin_bswap16", readsAll; - "__builtin_bswap32", readsAll; - "__builtin_bswap64", readsAll; - "__builtin_bswap128", readsAll; "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) From 96a59cbaadb917ed2f5665fce6df4e6c73a84299 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 16:24:22 +0300 Subject: [PATCH 016/238] Convert most file-related functions to new specifications --- src/analyses/libraryFunctions.ml | 67 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5ea4fdf3fa..8fdc867834 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -15,6 +15,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); + ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); + ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -27,6 +29,27 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); + ("free", unknown [drop "ptr" [f]]); + ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); + ("feof", unknown [drop "stream" [r_deep; w_deep]]); + ("ferror", unknown [drop "stream" [r_deep; w_deep]]); + ("fflush", unknown [drop "stream" [r_deep; w_deep]]); + ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("fprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]]); + ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); + ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); + ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); + ("ftell", unknown [drop "stream" [r_deep]]); + ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); + (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) + (* as any future write (or flush) of the stream could result in a write to the buffer *) + ("localtime", unknown [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); @@ -85,7 +108,10 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putc_unlocked", unknown [drop "c" []; drop "stream" [w]]); ("putchar_unlocked", unknown [drop "c" []]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); - ("fseeko", unknown [drop "stream" [w]; drop "offset" []; drop "whence" []]); + ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); + ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); + ("fileno", unknown [drop "stream" [r_deep; w_deep]]); + ("getopt", unknown [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -98,6 +124,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("gettimeofday", unknown [drop "tv" [w]; drop "tz" [w]]); ("futimens", unknown [drop "fd" []; drop "times" [r]]); ("utimes", unknown [drop "filename" [r]; drop "times" [r]]); + ("utimensat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]; drop "flags" []]); ("linkat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]; drop "flags" []]); ("dirfd", unknown [drop "dirp" [r]]); ("fdopendir", unknown [drop "fd" []]); @@ -234,12 +261,16 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fputs_unlocked", unknown [drop "s" [r]; drop "stream" [w]]); - ("futimesat", unknown [drop "dirfd" [w]; drop "pathname" [r]; drop "times" [r]]); + ("futimesat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "times" [r]]); ("error", unknown ((drop "status" []):: (drop "errnum" []) :: (drop "format" [r]) :: (VarArgs (drop' [r])))); ("gettext", unknown [drop "msgid" [r]]); ("euidaccess", unknown [drop "pathname" [r]; drop "mode" []]); ("rpmatch", unknown [drop "response" [r]]); ("getpagesize", unknown []); + ("__fgets_alias", unknown [drop "__s" [r; w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_chk", unknown [drop "__s" [r; w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_alias", unknown [drop "__ptr" [w_deep]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk", unknown [drop "__ptr" [w_deep]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); @@ -276,6 +307,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); + ("__fprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]]); ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) @@ -774,24 +806,6 @@ open Invalidate let invalidate_actions = [ "atoi", readsAll; (*safe*) "connect", readsAll; (*safe*) - "fclose", readsAll; (*safe*) - "fflush", writesAll; (*unsafe*) - "fopen", readsAll; (*safe*) - "fdopen", readsAll; (*safe*) - "setvbuf", writes[1;2]; (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) - (* as any future write (or flush) of the stream could result in a write to the buffer *) - "fprintf", writes [1]; (*keep [1]*) - "__fprintf_chk", writes [1]; (*keep [1]*) - "fread", writes [1;4]; - "__fread_alias", writes [1;4]; - "__fread_chk", writes [1;4]; - "utimensat", readsAll; - "free", frees [1]; (*unsafe*) - "fwrite", readsAll;(*safe*) - "getopt", writes [2];(*keep [2]*) - "localtime", readsAll;(*safe*) - "mempcpy", writes [1];(*keep [1]*) - "__builtin___mempcpy_chk", writes [1]; "printf", readsAll;(*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) @@ -853,8 +867,6 @@ let invalidate_actions = [ "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) "getpid", readsAll;(*safe*) - "fgetc", writesAll;(*unsafe*) - "getc", writesAll;(*unsafe*) "_IO_getc", writesAll;(*unsafe*) "closedir", writesAll;(*unsafe*) "chdir", readsAll;(*safe*) @@ -904,7 +916,6 @@ let invalidate_actions = [ "open", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) - "fcntl", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "umount", readsAll;(*safe*) @@ -922,18 +933,11 @@ let invalidate_actions = [ "dcgettext", readsAll;(*safe*) "syscall", writesAllButFirst 1 readsAll;(*drop 1*) "sysconf", readsAll; - "fputs", readsAll;(*safe*) - "fputc", readsAll;(*safe*) - "fseek", writes[1]; "rewind", writesAll; - "fileno", readsAll; - "ferror", readsAll; - "ftell", readsAll; "putc", readsAll;(*safe*) "putw", readsAll;(*safe*) "putchar", readsAll;(*safe*) "getchar", readsAll;(*safe*) - "feof", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) "vsyslog", readsAll;(*safe*) "gethostbyname_r", readsAll;(*safe*) @@ -982,9 +986,6 @@ let invalidate_actions = [ "getpeername", writes [1]; (*keep [1]*) "times", writesAll; (*unsafe*) "timespec_get", writes [1]; - "fgets", writes [1;3]; (*keep [3]*) - "__fgets_alias", writes [1;3]; (*keep [3]*) - "__fgets_chk", writes [1;3]; (*keep [3]*) "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) "strsignal", readsAll; From ce50eb4eb88693f98316f5f2a2db1cfac4e2b02a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 19:11:02 +0300 Subject: [PATCH 017/238] Fix fprintf: add VarArgs --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8fdc867834..ac60ec2643 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -39,7 +39,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("fprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]]); + ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); From c272f14e6a530829666841fd53786201176bb906 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 19:11:49 +0300 Subject: [PATCH 018/238] Mark and add thread-unsafe functions in libraryFunctions, relates to #723 --- src/analyses/libraryFunctions.ml | 110 +++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index ac60ec2643..9b689b64ad 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -29,6 +29,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); + ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); ("free", unknown [drop "ptr" [f]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); @@ -49,10 +50,13 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) - ("localtime", unknown [drop "time" [r]]); + ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); + ("localeconv", unknown ~attrs:[ThreadUnsafe] []); + ("localtime", unknown ~attrs:[ThreadUnsafe] [drop "time" [r]]); ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); + ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); @@ -69,6 +73,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswprint", unknown [drop "wc" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); ("puts", unknown [drop "s" [r]]); + ("rand", unknown ~attrs:[ThreadUnsafe] []); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strtod", unknown [drop "nptr" [r]; drop "endptr" [w]]); @@ -77,15 +82,17 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtoll", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoul", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoull", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); + ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); ("mktime", unknown [drop "tm" [r;w]]); - ("ctime", unknown [drop "rm" [r]]); + ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' []))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); - ("system", unknown [drop "command" [r]]); + ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); + ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]; drop "wc" []; drop "ps" [w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -101,17 +108,74 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("explicit_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("__explicit_bzero_chk", special [__ "dest" [w]; __ "count" []; drop "os" []] @@ fun dest count -> Bzero { dest; count; }); - ("nl_langinfo", unknown [drop "item" []]); + ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r_deep]]); + ("crypt", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]; drop "salt" [r]]); + ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]]); + ("dbm_clearerr", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]]); + ("dbm_close", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep; f_deep]]); + ("dbm_delete", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []]); + ("dbm_error", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_fetch", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]; drop "key" []]); + ("dbm_firstkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_nextkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); + ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r_deep; w_deep]; drop "open_flags" []; drop "file_mode" []]); + ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); + ("dlerror", unknown ~attrs:[ThreadUnsafe] []); + ("drand48", unknown ~attrs:[ThreadUnsafe] []); + ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" []; drop "edflag" []]); + ("endgrent", unknown ~attrs:[ThreadUnsafe] []); + ("endpwent", unknown ~attrs:[ThreadUnsafe] []); + ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); + ("gcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigit" []; drop "buf" [w]]); + ("getdate", unknown ~attrs:[ThreadUnsafe] [drop "string" [r]]); + ("getenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getgrent", unknown ~attrs:[ThreadUnsafe] []); + ("getgrgid", unknown ~attrs:[ThreadUnsafe] [drop "gid" []]); + ("getgrnam", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getlogin", unknown ~attrs:[ThreadUnsafe] []); + ("getnetbyaddr", unknown ~attrs:[ThreadUnsafe] [drop "net" []; drop "type" []]); + ("getnetbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getnetent", unknown ~attrs:[ThreadUnsafe] []); + ("getprotobyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getprotobynumber", unknown ~attrs:[ThreadUnsafe] [drop "proto" []]); + ("getprotoent", unknown ~attrs:[ThreadUnsafe] []); + ("getpwent", unknown ~attrs:[ThreadUnsafe] []); + ("getpwnam", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); + ("getpwuid", unknown ~attrs:[ThreadUnsafe] [drop "uid" []]); + ("getservbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "proto" [r]]); + ("getservbyport", unknown ~attrs:[ThreadUnsafe] [drop "port" []; drop "proto" [r]]); + ("getservent", unknown ~attrs:[ThreadUnsafe] []); + ("getutxent", unknown ~attrs:[ThreadUnsafe] []); + ("getutxid", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("getutxline", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("pututxline", unknown ~attrs:[ThreadUnsafe] [drop "utmpx" [r_deep]]); + ("hcreate", unknown ~attrs:[ThreadUnsafe] [drop "nel" []]); + ("hdestroy", unknown ~attrs:[ThreadUnsafe] []); + ("hsearch", unknown ~attrs:[ThreadUnsafe] [drop "item" [r_deep]; drop "action" [r_deep]]); + ("l64a", unknown ~attrs:[ThreadUnsafe] [drop "value" []]); + ("lrand48", unknown ~attrs:[ThreadUnsafe] []); + ("mrand48", unknown ~attrs:[ThreadUnsafe] []); + ("nl_langinfo", unknown ~attrs:[ThreadUnsafe] [drop "item" []]); ("nl_langinfo_l", unknown [drop "item" []; drop "locale" [r_deep]]); - ("getc_unlocked", unknown [drop "stream" [w]]); - ("getchar_unlocked", unknown []); - ("putc_unlocked", unknown [drop "c" []; drop "stream" [w]]); - ("putchar_unlocked", unknown [drop "c" []]); + ("getc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "stream" [r_deep; w_deep]]); + ("getchar_unlocked", unknown ~attrs:[ThreadUnsafe] []); + ("ptsname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); + ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [w]]); + ("putchar_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []]); + ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); + ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); + ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); + ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); + ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); + ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("getopt", unknown [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); + ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -136,15 +200,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("alarm", unknown [drop "seconds" []]); ("pwrite", unknown [drop "fd" []; drop "buf" [r]; drop "count" []; drop "offset" []]); ("hstrerror", unknown [drop "err" []]); - ("inet_ntoa", unknown [drop "in" []]); + ("inet_ntoa", unknown ~attrs:[ThreadUnsafe] [drop "in" []]); ("getsockopt", unknown [drop "sockfd" []; drop "level" []; drop "optname" []; drop "optval" [w]; drop "optlen" [w]]); - ("gethostbyaddr", unknown [drop "addr" [r_deep]; drop "len" []; drop "type" []]); + ("gethostbyaddr", unknown ~attrs:[ThreadUnsafe] [drop "addr" [r_deep]; drop "len" []; drop "type" []]); ("gethostbyaddr_r", unknown [drop "addr" [r_deep]; drop "len" []; drop "type" []; drop "ret" [w_deep]; drop "buf" [w]; drop "buflen" []; drop "result" [w]; drop "h_errnop" [w]]); + ("gethostbyname", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("sigaction", unknown [drop "signum" []; drop "act" [r_deep; s_deep]; drop "oldact" [w_deep]]); ("tcgetattr", unknown [drop "fd" []; drop "termios_p" [w_deep]]); ("tcsetattr", unknown [drop "fd" []; drop "optional_actions" []; drop "termios_p" [r_deep]]); ("access", unknown [drop "pathname" [r]; drop "mode" []]); - ("ttyname", unknown [drop "fd" []]); + ("ttyname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); ("shm_open", unknown [drop "name" [r]; drop "oflag" []; drop "mode" []]); ("sched_get_priority_max", unknown [drop "policy" []]); ("mprotect", unknown [drop "addr" []; drop "len" []; drop "prot" []]); @@ -164,14 +229,15 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strtok_r", unknown [drop "str" [r; w]; drop "delim" [r]; drop "saveptr" [r_deep; w_deep]]); (* deep accesses through saveptr if str is NULL: https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/string/strtok_r.c#L31-L40 *) ("kill", unknown [drop "pid" []; drop "sig" []]); ("closelog", unknown []); - ("dirname", unknown [drop "path" [r]]); + ("dirname", unknown ~attrs:[ThreadUnsafe] [drop "path" [r]]); + ("basename", unknown ~attrs:[ThreadUnsafe] [drop "path" [r]]); ("setpgid", unknown [drop "pid" []; drop "pgid" []]); ("dup2", unknown [drop "oldfd" []; drop "newfd" []]); ("pclose", unknown [drop "stream" [w; f]]); ("getcwd", unknown [drop "buf" [w]; drop "size" []]); ("inet_pton", unknown [drop "af" []; drop "src" [r]; drop "dst" [w]]); ("inet_ntop", unknown [drop "af" []; drop "src" [r]; drop "dst" [w]; drop "size" []]); - ("gethostent", unknown []); + ("gethostent", unknown ~attrs:[ThreadUnsafe] []); ("poll", unknown [drop "fds" [r]; drop "nfds" []; drop "timeout" []]); ("semget", unknown [drop "key" []; drop "nsems" []; drop "semflg" []]); ("semctl", unknown (drop "semid" [] :: drop "semnum" [] :: drop "cmd" [] :: VarArgs (drop "semun" [r_deep]))); @@ -304,6 +370,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("prctl", unknown (drop "option" [] :: VarArgs (drop' []))); (* man page has 5 arguments, but header has varargs and real-world programs may call with <5 *) ("__ctype_tolower_loc", unknown []); ("__ctype_toupper_loc", unknown []); + ("endutxent", unknown ~attrs:[ThreadUnsafe] []); ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); @@ -498,9 +565,9 @@ let math_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ldexp", unknown [drop "arg" []; drop "exp" []]); ("ldexpf", unknown [drop "arg" []; drop "exp" []]); ("ldexpl", unknown [drop "arg" []; drop "exp" []]); - ("lgamma", unknown [drop "x" []]); - ("lgammaf", unknown [drop "x" []]); - ("lgammal", unknown [drop "x" []]); + ("lgamma", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); + ("lgammaf", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); + ("lgammal", unknown ~attrs:[ThreadUnsafe] [drop "x" []]); ("log", unknown [drop "x" []]); ("logf", unknown [drop "x" []]); ("logl", unknown [drop "x" []]); @@ -898,11 +965,9 @@ let invalidate_actions = [ "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) - "dlerror", readsAll;(*safe*) "stat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "lstat__extinline", writesAllButFirst 1 readsAll;(*drop 1*) "__builtin_strchr", readsAll;(*safe*) - "strtok", readsAll;(*safe*) "getpgrp", readsAll;(*safe*) "umount2", readsAll;(*safe*) "memchr", readsAll;(*safe*) @@ -944,8 +1009,6 @@ let invalidate_actions = [ "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) "getuid", readsAll;(*safe*) - "strerror", readsAll;(*safe*) - "readdir", readsAll;(*safe*) "openlog", readsAll;(*safe*) "getdtablesize", readsAll;(*safe*) "umask", readsAll;(*safe*) @@ -971,15 +1034,12 @@ let invalidate_actions = [ "syslog", readsAll; (*safe*) "strcasecmp", readsAll; (*safe*) "strchr", readsAll; (*safe*) - "getservbyname", readsAll; (*safe*) "__error", readsAll; (*safe*) "__maskrune", writesAll; (*unsafe*) "inet_addr", readsAll; (*safe*) - "gethostbyname", readsAll; (*safe*) "setsockopt", readsAll; (*safe*) "listen", readsAll; (*safe*) "getsockname", writes [1;3]; (*keep [1;3]*) - "getenv", readsAll; (*safe*) "execl", readsAll; (*safe*) "select", writes [1;5]; (*keep [1;5]*) "accept", writesAll; (*keep [1]*) @@ -988,7 +1048,6 @@ let invalidate_actions = [ "timespec_get", writes [1]; "__tolower", readsAll; (*safe*) "signal", writesAll; (*unsafe*) - "strsignal", readsAll; "popen", readsAll; (*safe*) "BF_cfb64_encrypt", writes [1;3;4;5]; (*keep [1;3;4,5]*) "BZ2_bzBuffToBuffDecompress", writes [3;4]; (*keep [3;4]*) @@ -1005,7 +1064,6 @@ let invalidate_actions = [ "sendto", writes [2;4]; (*keep [2;4]*) "recvfrom", writes [4;5]; (*keep [4;5]*) "srand", readsAll; (*safe*) - "rand", readsAll; (*safe*) "gethostname", writesAll; (*unsafe*) "fork", readsAll; (*safe*) "setrlimit", readsAll; (*safe*) From 884763609adc2e457727d089745c679a4d42a7be Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 21:38:44 +0300 Subject: [PATCH 019/238] Add (function) Call to accessKind --- src/analyses/libraryFunctions.ml | 23 ++++++++++++----------- src/analyses/mutexAnalysis.ml | 1 + src/domains/accessKind.ml | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 9b689b64ad..31ed3adb65 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -797,70 +797,70 @@ struct let writesAllButFirst n f a x = match a with - | Write | Spawn -> f a x @ drop n x + | Write | Call | Spawn -> f a x @ drop n x | Read -> f a x | Free -> [] let readsAllButFirst n f a x = match a with - | Write | Spawn -> f a x + | Write | Call | Spawn -> f a x | Read -> f a x @ drop n x | Free -> [] let reads ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> o + | Write | Call | Spawn -> o | Read -> i | Free -> [] let writes ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> i + | Write | Call | Spawn -> i | Read -> o | Free -> [] let frees ns a x = let i, o = partition ns x in match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> o | Free -> i let readsFrees rs fs a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> keep rs x | Free -> keep fs x let onlyReads ns a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> keep ns x | Free -> [] let onlyWrites ns a x = match a with - | Write | Spawn -> keep ns x + | Write | Call | Spawn -> keep ns x | Read -> [] | Free -> [] let readsWrites rs ws a x = match a with - | Write | Spawn -> keep ws x + | Write | Call | Spawn -> keep ws x | Read -> keep rs x | Free -> [] let readsAll a x = match a with - | Write | Spawn -> [] + | Write | Call | Spawn -> [] | Read -> x | Free -> [] let writesAll a x = match a with - | Write | Spawn -> x + | Write | Call | Spawn -> x | Read -> [] | Free -> [] end @@ -1158,6 +1158,7 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul | Read when GobConfig.get_bool "sem.unknown_function.read.args" -> args | Read -> [] | Free -> [] + | Call -> [] (* TODO: option *) | Spawn when get_bool "sem.unknown_function.spawn" -> args | Spawn -> [] in diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9029c1b4a1..6d39be150c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -254,6 +254,7 @@ struct let write = match kind with | Write | Free -> true | Read -> false + | Call | Spawn -> false (* TODO: nonsense? *) in let s = GProtecting.make ~write ~recovered:is_recovered_to_st locks in diff --git a/src/domains/accessKind.ml b/src/domains/accessKind.ml index 576581af02..b36e8f3eca 100644 --- a/src/domains/accessKind.ml +++ b/src/domains/accessKind.ml @@ -1,9 +1,10 @@ (** Kinds of memory accesses. *) type t = - | Write (** argument may be read or written to *) + | Write (** argument may be written to *) | Read (** argument may be read *) | Free (** argument may be freed *) + | Call (** argument may be called *) | Spawn (** argument may be spawned *) [@@deriving eq, ord, hash] (** Specifies what is known about an argument. *) @@ -12,6 +13,7 @@ let show: t -> string = function | Write -> "write" | Read -> "read" | Free -> "free" + | Call -> "call" | Spawn -> "spawn" include Printable.SimpleShow ( From d58c1ca6e0182b86daf083180d986aa52315654b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 22:17:30 +0300 Subject: [PATCH 020/238] Add tests for thread-unsafe function call --- .../04-mutex/77-thread-unsafe_fun_rc.c | 22 +++++++++++++++++++ .../04-mutex/78-thread-unsafe_fun_nr.c | 21 ++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/regression/04-mutex/77-thread-unsafe_fun_rc.c create mode 100644 tests/regression/04-mutex/78-thread-unsafe_fun_nr.c diff --git a/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c b/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c new file mode 100644 index 0000000000..8f2f01fc6d --- /dev/null +++ b/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c @@ -0,0 +1,22 @@ +#include +#include + +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + rand(); // RACE! + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex2); + rand(); // RACE! + pthread_mutex_unlock(&mutex2); + pthread_join (id, NULL); + return 0; +} diff --git a/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c b/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c new file mode 100644 index 0000000000..df02d23db9 --- /dev/null +++ b/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c @@ -0,0 +1,21 @@ +#include +#include + +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + rand(); // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&mutex1); + rand(); // NORACE + pthread_mutex_unlock(&mutex1); + pthread_join (id, NULL); + return 0; +} From 874c1864bc2f4e4cdc14138de09c9a408c94f319 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 9 Jun 2023 22:18:38 +0300 Subject: [PATCH 021/238] Handle thread-unsafe function calls in raceAnalysis #723 Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 481ccbf60b..30cf03cfa7 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -153,6 +153,19 @@ struct | _ -> ctx.local + let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = + (* perform shallow and deep invalidate according to Library descriptors *) + let desc = LibraryFunctions.find f in + if List.mem LibraryDesc.ThreadUnsafe desc.attrs then ( + let e = Lval (Var f, NoOffset) in + let conf = 110 in + let loc = Option.get !Node.current_node in + let vo = Some f in + let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in + side_access ctx (`Type f.vtype) (Some (f, `NoOffset)) (conf, Call, loc, e, a); + ); + ctx.local + let finalize () = let total = !safe + !unsafe + !vulnerable in if total > 0 then ( From a42b9645785acc38d7aa93fb9757230368c627c0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:03:04 +0300 Subject: [PATCH 022/238] Convert all pthread functions to new specifications --- src/analyses/libraryFunctions.ml | 78 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 9b689b64ad..a9b1fafa40 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -251,15 +251,47 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) + ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); ("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); ("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime}); + ("pthread_cond_destroy", unknown [drop "cond" [f]]); ("pthread_mutexattr_settype", special [__ "attr" []; __ "type" []] @@ fun attr typ -> MutexAttrSetType {attr; typ}); ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); + ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); + ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); + ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); + ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); + ("pthread_mutexattr_init", unknown [drop "attr" [w]]); + ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); + ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); + ("pthread_rwlock_destroy", unknown [drop "rwlock" [f]]); + ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); + ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); + ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_rwlock_trywrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = true; write = true; return_on_success = false}); + ("pthread_rwlock_unlock", special [__ "rwlock" []] @@ fun rwlock -> Unlock rwlock); + ("pthread_rwlockattr_init", unknown [drop "attr" [w]]); + ("pthread_rwlockattr_destroy", unknown [drop "attr" [f]]); + ("pthread_spin_init", unknown [drop "lock" []; drop "pshared" []]); + ("pthread_spin_destroy", unknown [drop "lock" [f]]); + ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = true; write = true; return_on_success = false}); + ("pthread_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("pthread_attr_init", unknown [drop "attr" [w]]); ("pthread_attr_destroy", unknown [drop "attr" [f]]); + ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [r]]); + ("pthread_attr_setdetachstate", unknown [drop "attr" [w]; drop "detachstate" []]); + ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [r]]); + ("pthread_attr_setstacksize", unknown [drop "attr" [w]; drop "stacksize" []]); + ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [r]]); + ("pthread_attr_setscope", unknown [drop "attr" [w]; drop "scope" []]); + ("pthread_self", unknown []); + ("pthread_sigmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); ("pthread_setspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []; drop "value" [w_deep]]); ("pthread_getspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []]); + ("pthread_key_create", unknown [drop "key" [w]; drop "destructor" [s]]); ("pthread_key_delete", unknown [drop "key" [f]]); ("pthread_cancel", unknown [drop "thread" []]); ("pthread_setcanceltype", unknown [drop "type" []; drop "oldtype" [w]]); @@ -753,24 +785,21 @@ let classify fn exps: categories = | "_spin_trylock" | "spin_trylock" | "mutex_trylock" | "_spin_trylock_irqsave" | "down_trylock" -> `Lock(true, true, true) - | "pthread_mutex_trylock" | "pthread_rwlock_trywrlock" | "pthread_spin_trylock" - -> `Lock (true, true, false) | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" | "down_write" | "mutex_lock" | "mutex_lock_interruptible" | "_write_lock" | "_raw_write_lock" - | "pthread_rwlock_wrlock" | "GetResource" | "_raw_spin_lock" + | "GetResource" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - | "spin_lock" | "pthread_spin_lock" + | "spin_lock" -> `Lock (get_bool "sem.lock.fail", true, true) - | "pthread_mutex_lock" | "__pthread_mutex_lock" + | "__pthread_mutex_lock" -> `Lock (get_bool "sem.lock.fail", true, false) - | "pthread_rwlock_tryrdlock" | "pthread_rwlock_rdlock" | "_read_lock" | "_raw_read_lock" - | "down_read" + | "_read_lock" | "_raw_read_lock" | "down_read" -> `Lock (get_bool "sem.lock.fail", false, true) | "__raw_read_unlock" | "__raw_write_unlock" | "raw_spin_unlock" | "_spin_unlock" | "spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" | "mutex_unlock" | "_write_unlock" | "_read_unlock" - | "pthread_mutex_unlock" | "__pthread_mutex_unlock" | "up_read" | "up_write" - | "up" | "pthread_spin_unlock" + | "__pthread_mutex_unlock" | "up_read" | "up_write" + | "up" -> `Unlock | x -> `Unknown x @@ -877,12 +906,6 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "pthread_mutex_lock", readsAll;(*safe*) - "pthread_mutex_trylock", readsAll; - "pthread_mutex_unlock", readsAll;(*safe*) - "pthread_spin_lock", readsAll;(*safe*) - "pthread_spin_trylock", readsAll; - "pthread_spin_unlock", readsAll;(*safe*) "__pthread_mutex_lock", readsAll;(*safe*) "__pthread_mutex_trylock", readsAll; "__pthread_mutex_unlock", readsAll;(*safe*) @@ -894,11 +917,6 @@ let invalidate_actions = [ "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) - "pthread_mutex_destroy", readsAll;(*safe*) - "pthread_mutexattr_init", readsAll;(*safe*) - "pthread_spin_init", readsAll;(*safe*) - "pthread_spin_destroy", readsAll;(*safe*) - "pthread_self", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) "scanf", writesAllButFirst 1 readsAll;(*drop 1*) @@ -941,24 +959,13 @@ let invalidate_actions = [ "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) "strerror_r", writesAll;(*unsafe*) - "pthread_attr_init", writesAll; (*unsafe*) - "pthread_attr_setdetachstate", writesAll;(*unsafe*) - "pthread_attr_setstacksize", writesAll;(*unsafe*) - "pthread_attr_setscope", writesAll;(*unsafe*) - "pthread_attr_getdetachstate", readsAll;(*safe*) - "pthread_attr_getstacksize", readsAll;(*safe*) - "pthread_attr_getscope", readsAll;(*safe*) - "pthread_cond_init", readsAll; (*safe*) - "pthread_cond_destroy", readsAll;(*safe*) "__pthread_cond_init", readsAll; (*safe*) "__pthread_cond_wait", readsAll; (*safe*) "__pthread_cond_signal", readsAll;(*safe*) "__pthread_cond_broadcast", readsAll;(*safe*) "__pthread_cond_destroy", readsAll;(*safe*) - "pthread_key_create", writesAll;(*unsafe*) "sigemptyset", writesAll;(*unsafe*) "sigaddset", writesAll;(*unsafe*) - "pthread_sigmask", writesAllButFirst 2 readsAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) @@ -1080,15 +1087,6 @@ let invalidate_actions = [ "munmap", readsAll;(*safe*) "mmap", readsAll;(*safe*) "clock", readsAll; - "pthread_rwlock_wrlock", readsAll; - "pthread_rwlock_trywrlock", readsAll; - "pthread_rwlock_rdlock", readsAll; - "pthread_rwlock_tryrdlock", readsAll; - "pthread_rwlockattr_destroy", writesAll; - "pthread_rwlockattr_init", writesAll; - "pthread_rwlock_destroy", readsAll; - "pthread_rwlock_init", readsAll; - "pthread_rwlock_unlock", readsAll; "__builtin_va_arg_pack_len", readsAll; "__open_too_many_args", readsAll; "usb_submit_urb", readsAll; (* first argument is written to but according to specification must not be read from anymore *) From 8c44ac7d68850372dfe8ffa9693e5a381e1fca5a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:49:40 +0300 Subject: [PATCH 023/238] Convert linux kernel functions from classify to new specifications --- src/analyses/libraryFunctions.ml | 44 +++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index a9b1fafa40..3b3af35399 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -417,8 +417,24 @@ let console_sem = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[console (** Linux kernel functions. *) let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("down_trylock", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = true; write = true; return_on_success = true }); + ("down_read", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("down_write", special [__ "sem" []] @@ fun sem -> Lock { lock = sem; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("up", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("up_read", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("up_write", special [__ "sem" []] @@ fun sem -> Unlock sem); + ("mutex_init", unknown [drop "mutex" []]); + ("mutex_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("mutex_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("mutex_lock_interruptible", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("mutex_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("spin_lock_init", unknown [drop "lock" []]); + ("spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("raw_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("_raw_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); ("spinlock_check", special [__ "lock" []] @@ fun lock -> Identity lock); (* Identity, because we don't want lock internals. *) ("_lock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Lock { lock = big_kernel_lock; try_ = false; write = true; return_on_success = true }); @@ -782,24 +798,19 @@ let classify fn exps: categories = | n::size::_ -> `Calloc (n, size) | _ -> strange_arguments () end - | "_spin_trylock" | "spin_trylock" | "mutex_trylock" | "_spin_trylock_irqsave" - | "down_trylock" + | "_spin_trylock" | "_spin_trylock_irqsave" -> `Lock(true, true, true) - | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" | "down_write" - | "mutex_lock" | "mutex_lock_interruptible" | "_write_lock" | "_raw_write_lock" - | "GetResource" | "_raw_spin_lock" + | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" + | "_write_lock" | "_raw_write_lock" | "GetResource" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - | "spin_lock" -> `Lock (get_bool "sem.lock.fail", true, true) | "__pthread_mutex_lock" -> `Lock (get_bool "sem.lock.fail", true, false) - | "_read_lock" | "_raw_read_lock" | "down_read" + | "_read_lock" | "_raw_read_lock" -> `Lock (get_bool "sem.lock.fail", false, true) - | "__raw_read_unlock" | "__raw_write_unlock" | "raw_spin_unlock" - | "_spin_unlock" | "spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" - | "mutex_unlock" | "_write_unlock" | "_read_unlock" - | "__pthread_mutex_unlock" | "up_read" | "up_write" - | "up" + | "__raw_read_unlock" | "__raw_write_unlock" + | "_spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" + | "_write_unlock" | "_read_unlock" | "__pthread_mutex_unlock" -> `Unlock | x -> `Unknown x @@ -910,10 +921,6 @@ let invalidate_actions = [ "__pthread_mutex_trylock", readsAll; "__pthread_mutex_unlock", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "mutex_init", readsAll;(*safe*) - "mutex_lock", readsAll;(*safe*) - "mutex_lock_interruptible", readsAll;(*safe*) - "mutex_unlock", readsAll;(*safe*) "_spin_lock", readsAll;(*safe*) "_spin_unlock", readsAll;(*safe*) "_spin_lock_irqsave", readsAll;(*safe*) @@ -1101,12 +1108,7 @@ let invalidate_actions = [ "setpriority", readsAll; "getpriority", readsAll; (* ddverify *) - "spin_lock_init", readsAll; - "spin_lock", readsAll; - "spin_unlock", readsAll; "sema_init", readsAll; - "down_trylock", readsAll; - "up", readsAll; "__goblint_assume_join", readsAll; ] From 2ef2e7f4cb2311c76e135435cccacfb61c9a00de Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:51:27 +0300 Subject: [PATCH 024/238] Remove GetResource from libraryFunctions --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3b3af35399..d0bb1edf53 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -801,7 +801,7 @@ let classify fn exps: categories = | "_spin_trylock" | "_spin_trylock_irqsave" -> `Lock(true, true, true) | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" - | "_write_lock" | "_raw_write_lock" | "GetResource" | "_raw_spin_lock" + | "_write_lock" | "_raw_write_lock" | "_raw_spin_lock" | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" -> `Lock (get_bool "sem.lock.fail", true, true) | "__pthread_mutex_lock" From 30d5aea6ed99832d8b6cafac19ac9e924561adb5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 15:31:24 +0300 Subject: [PATCH 025/238] Convert most functions from classify to new specifications --- src/analyses/libraryFunctions.ml | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d0bb1edf53..31ee8bc497 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -261,8 +261,10 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); + ("__pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); + ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); @@ -430,12 +432,34 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("mutex_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_init", unknown [drop "lock" []]); ("spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock_bh", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); ("spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); + ("_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = true; write = true; return_on_success = true }); ("spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_spin_unlock_bh", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spin_lock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_lock_irqsave", special [__ "lock" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_spin_trylock_irqsave", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock; try_ = true; write = true; return_on_success = true }); ("spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); ("raw_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("_raw_spin_unlock_irqrestore", special [__ "lock" []; drop "flags" []] @@ fun lock -> Unlock lock); + ("_raw_spin_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_flags", special [__ "lock" []; drop "flags" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_irqsave", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_irq", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_lock_bh", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_raw_spin_unlock_bh", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_read_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("_read_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_raw_read_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true }); + ("__raw_read_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_write_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("_write_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); + ("_raw_write_lock", special [__ "lock" []] @@ fun lock -> Lock { lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true }); + ("__raw_write_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("spinlock_check", special [__ "lock" []] @@ fun lock -> Identity lock); (* Identity, because we don't want lock internals. *) ("_lock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Lock { lock = big_kernel_lock; try_ = false; write = true; return_on_success = true }); ("_unlock_kernel", special [drop "func" [r]; drop "file" [r]; drop "line" []] @@ Unlock big_kernel_lock); @@ -798,20 +822,6 @@ let classify fn exps: categories = | n::size::_ -> `Calloc (n, size) | _ -> strange_arguments () end - | "_spin_trylock" | "_spin_trylock_irqsave" - -> `Lock(true, true, true) - | "_spin_lock" | "_spin_lock_irqsave" | "_spin_lock_bh" - | "_write_lock" | "_raw_write_lock" | "_raw_spin_lock" - | "_raw_spin_lock_flags" | "_raw_spin_lock_irqsave" | "_raw_spin_lock_irq" | "_raw_spin_lock_bh" - -> `Lock (get_bool "sem.lock.fail", true, true) - | "__pthread_mutex_lock" - -> `Lock (get_bool "sem.lock.fail", true, false) - | "_read_lock" | "_raw_read_lock" - -> `Lock (get_bool "sem.lock.fail", false, true) - | "__raw_read_unlock" | "__raw_write_unlock" - | "_spin_unlock" | "_spin_unlock_irqrestore" | "_spin_unlock_bh" | "_raw_spin_unlock_bh" - | "_write_unlock" | "_read_unlock" | "__pthread_mutex_unlock" - -> `Unlock | x -> `Unknown x @@ -917,13 +927,8 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "__pthread_mutex_lock", readsAll;(*safe*) "__pthread_mutex_trylock", readsAll; - "__pthread_mutex_unlock", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "_spin_lock", readsAll;(*safe*) - "_spin_unlock", readsAll;(*safe*) - "_spin_lock_irqsave", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) "scanf", writesAllButFirst 1 readsAll;(*drop 1*) From 01ce50483be81bf863e5284280a73bcebe49e389 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 16:10:44 +0300 Subject: [PATCH 026/238] Convert all __pthread functions to new specifications --- src/analyses/libraryFunctions.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 31ee8bc497..f2fd7e0d41 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -252,17 +252,23 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); + ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); + ("__pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); + ("__pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); ("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); + ("__pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex}); ("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime}); ("pthread_cond_destroy", unknown [drop "cond" [f]]); + ("__pthread_cond_destroy", unknown [drop "cond" [f]]); ("pthread_mutexattr_settype", special [__ "attr" []; __ "type" []] @@ fun attr typ -> MutexAttrSetType {attr; typ}); ("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr}); ("pthread_mutex_destroy", unknown [drop "mutex" [f]]); ("pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("__pthread_mutex_lock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); + ("__pthread_mutex_trylock", special [__ "mutex" []] @@ fun mutex -> Lock {lock = mutex; try_ = true; write = true; return_on_success = false}); ("pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); @@ -927,7 +933,6 @@ let invalidate_actions = [ "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) "perror", readsAll;(*safe*) - "__pthread_mutex_trylock", readsAll; "__mutex_init", readsAll;(*safe*) "read", writes [2];(*keep [2]*) "recv", writes [2];(*keep [2]*) @@ -971,11 +976,6 @@ let invalidate_actions = [ "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) "strerror_r", writesAll;(*unsafe*) - "__pthread_cond_init", readsAll; (*safe*) - "__pthread_cond_wait", readsAll; (*safe*) - "__pthread_cond_signal", readsAll;(*safe*) - "__pthread_cond_broadcast", readsAll;(*safe*) - "__pthread_cond_destroy", readsAll;(*safe*) "sigemptyset", writesAll;(*unsafe*) "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) From 6a1445ac56ddf7235ab74768067cd08a47e163a2 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 16 Jun 2023 19:06:43 +0300 Subject: [PATCH 027/238] Convert some more lib-funs to new specifications --- src/analyses/libraryFunctions.ml | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index f2fd7e0d41..e3fce8718a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -40,13 +40,18 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep];]); - ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep];]); + ("sprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("snprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); + ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); + ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("rewind", unknown [drop "stream" [r_deep; w_deep]]); ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) @@ -64,7 +69,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("abort", special [] Abort); ("exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); - ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); + ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); + ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); (* TODO: why stream not r_deep; w_deep? *) + ("sscanf", unknown ((drop "buffer" [r]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("__freading", unknown [drop "stream" [r]]); ("mbsinit", unknown [drop "ps" [r]]); ("mbrtowc", unknown [drop "pwc" [w]; drop "s" [r]; drop "n" []; drop "ps" [r; w]]); @@ -72,17 +79,27 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("iswalnum", unknown [drop "wc" []]); ("iswprint", unknown [drop "wc" []]); ("rename" , unknown [drop "oldpath" [r]; drop "newpath" [r];]); + ("perror", unknown [drop "s" [r]]); + ("getchar", unknown []); + ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); + ("strftime", unknown [drop "str" [w]; drop "count" []; drop "format" [r]; drop "tp" [r]]); ("strtod", unknown [drop "nptr" [r]; drop "endptr" [w]]); ("strtol", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("__strtol_internal", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []; drop "group" []]); ("strtoll", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoul", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); ("strtoull", unknown [drop "nptr" [r]; drop "endptr" [w]; drop "base" []]); + ("tolower", unknown [drop "ch" []]); + ("toupper", unknown [drop "ch" []]); + ("time", unknown [drop "arg" [w]]); ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); + ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -220,7 +237,22 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("timer_getoverrun", unknown [drop "timerid" []]); ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("getpwnam", unknown [drop "name" [r]]); + ("chdir", unknown [drop "path" [r]]); + ("closedir", unknown [drop "dirp" [w]]); + ("mkdir", unknown [drop "pathname" [r]; drop "mode" []]); + ("opendir", unknown [drop "name" [r]]); + ("rmdir", unknown [drop "path" [r]]); + ("open", unknown (drop "pathname" [r] :: drop "flags" [] :: VarArgs (drop "mode" []))); + ("read", unknown [drop "fd" []; drop "buf" [w]; drop "count" []]); + ("write", unknown [drop "fd" []; drop "buf" [r]; drop "count" []]); + ("recv", unknown [drop "sockfd" []; drop "buf" [w]; drop "len" []; drop "flags" []]); + ("send", unknown [drop "sockfd" []; drop "buf" [r]; drop "len" []; drop "flags" []]); + ("strdup", unknown [drop "s" [r]]); ("strndup", unknown [drop "s" [r]; drop "n" []]); + ("syscall", unknown (drop "number" [] :: VarArgs (drop' [r; w]))); + ("sysconf", unknown [drop "name" []]); + ("syslog", unknown (drop "priority" [] :: drop "format" [r] :: VarArgs (drop' [r]))); (* TODO: is the VarArgs correct here? *) + ("vsyslog", unknown [drop "priority" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("freeaddrinfo", unknown [drop "res" [f_deep]]); ("getgid", unknown []); ("pselect", unknown [drop "nfds" []; drop "readdfs" [r]; drop "writedfs" [r]; drop "exceptfds" [r]; drop "timeout" [r]; drop "sigmask" [r]]); @@ -929,29 +961,11 @@ open Invalidate let invalidate_actions = [ "atoi", readsAll; (*safe*) "connect", readsAll; (*safe*) - "printf", readsAll;(*safe*) "__printf_chk", readsAll;(*safe*) "printk", readsAll;(*safe*) - "perror", readsAll;(*safe*) "__mutex_init", readsAll;(*safe*) - "read", writes [2];(*keep [2]*) - "recv", writes [2];(*keep [2]*) - "scanf", writesAllButFirst 1 readsAll;(*drop 1*) - "send", readsAll;(*safe*) - "snprintf", writes [1];(*keep [1]*) "__builtin___snprintf_chk", writes [1];(*keep [1]*) - "sprintf", writes [1];(*keep [1]*) - "sscanf", writesAllButFirst 2 readsAll;(*drop 2*) - "strftime", writes [1];(*keep [1]*) - "strdup", readsAll;(*safe*) - "toupper", readsAll;(*safe*) - "tolower", readsAll;(*safe*) - "time", writesAll;(*unsafe*) - "vfprintf", writes [1];(*keep [1]*) "__vfprintf_chk", writes [1];(*keep [1]*) - "vprintf", readsAll;(*safe*) - "vsprintf", writes [1];(*keep [1]*) - "write", readsAll;(*safe*) "__builtin_va_arg", readsAll;(*safe*) "__builtin_va_end", readsAll;(*safe*) "__builtin_va_start", readsAll;(*safe*) @@ -965,13 +979,10 @@ let invalidate_actions = [ "__strdup", readsAll;(*safe*) "strtoul__extinline", readsAll;(*safe*) "geteuid", readsAll;(*safe*) - "opendir", readsAll; (*safe*) "readdir_r", writesAll;(*unsafe*) "atoi__extinline", readsAll;(*safe*) "getpid", readsAll;(*safe*) "_IO_getc", writesAll;(*unsafe*) - "closedir", writesAll;(*unsafe*) - "chdir", readsAll;(*safe*) "pipe", writesAll;(*unsafe*) "close", writesAll;(*unsafe*) "setsid", readsAll;(*safe*) @@ -995,15 +1006,12 @@ let invalidate_actions = [ "__builtin___memmove_chk", writes [2;3];(*keep [2;3]*) "waitpid", readsAll;(*safe*) "statfs", writes [1;3;4];(*keep [1;3;4]*) - "mkdir", readsAll;(*safe*) "mount", readsAll;(*safe*) - "open", readsAll;(*safe*) "__open_alias", readsAll;(*safe*) "__open_2", readsAll;(*safe*) "ioctl", writesAll;(*unsafe*) "fstat__extinline", writesAll;(*unsafe*) "umount", readsAll;(*safe*) - "rmdir", readsAll;(*safe*) "strrchr", readsAll;(*safe*) "scandir", writes [1;3;4];(*keep [1;3;4]*) "unlink", readsAll;(*safe*) @@ -1015,15 +1023,8 @@ let invalidate_actions = [ "bindtextdomain", readsAll;(*safe*) "textdomain", readsAll;(*safe*) "dcgettext", readsAll;(*safe*) - "syscall", writesAllButFirst 1 readsAll;(*drop 1*) - "sysconf", readsAll; - "rewind", writesAll; - "putc", readsAll;(*safe*) "putw", readsAll;(*safe*) - "putchar", readsAll;(*safe*) - "getchar", readsAll;(*safe*) "__getdelim", writes [3];(*keep [3]*) - "vsyslog", readsAll;(*safe*) "gethostbyname_r", readsAll;(*safe*) "__h_errno_location", readsAll;(*safe*) "__fxstat", readsAll;(*safe*) @@ -1050,7 +1051,6 @@ let invalidate_actions = [ "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) - "syslog", readsAll; (*safe*) "strcasecmp", readsAll; (*safe*) "strchr", readsAll; (*safe*) "__error", readsAll; (*safe*) From 3525e494835566906a38c493c03ce5a57c2539dc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 15:14:09 +0300 Subject: [PATCH 028/238] Add more tests for covering different cases of access outer distribution Co-authored-by: Simmo Saan --- .../04-mutex/90-distribute-fields-type-1.c | 42 +++++++++++++++++ .../04-mutex/90-distribute-fields-type-1.t | 31 ++++++++++++ .../04-mutex/91-distribute-fields-type-2.c | 43 +++++++++++++++++ .../04-mutex/91-distribute-fields-type-2.t | 31 ++++++++++++ .../04-mutex/92-distribute-fields-type-deep.c | 47 +++++++++++++++++++ .../04-mutex/92-distribute-fields-type-deep.t | 33 +++++++++++++ .../93-distribute-fields-type-global.c | 26 ++++++++++ .../93-distribute-fields-type-global.t | 5 ++ 8 files changed, 258 insertions(+) create mode 100644 tests/regression/04-mutex/90-distribute-fields-type-1.c create mode 100644 tests/regression/04-mutex/90-distribute-fields-type-1.t create mode 100644 tests/regression/04-mutex/91-distribute-fields-type-2.c create mode 100644 tests/regression/04-mutex/91-distribute-fields-type-2.t create mode 100644 tests/regression/04-mutex/92-distribute-fields-type-deep.c create mode 100644 tests/regression/04-mutex/92-distribute-fields-type-deep.t create mode 100644 tests/regression/04-mutex/93-distribute-fields-type-global.c create mode 100644 tests/regression/04-mutex/93-distribute-fields-type-global.t diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.c b/tests/regression/04-mutex/90-distribute-fields-type-1.c new file mode 100644 index 0000000000..51f0e52426 --- /dev/null +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.c @@ -0,0 +1,42 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< >s< t +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct T).s.field in addition to (struct S).field + // but easier to implement the other way around? + getS()->field = 1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct S s1; + getT()->s = s1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t new file mode 100644 index 0000000000..dd862fa65a --- /dev/null +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 90-distribute-fields-type-1.c + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.c b/tests/regression/04-mutex/91-distribute-fields-type-2.c new file mode 100644 index 0000000000..12866105f6 --- /dev/null +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.c @@ -0,0 +1,43 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) >(S)< >(T)< (U) +// \ / \ / \ / +// f s t +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct T).s.field in addition to (struct S).field + // but easier to implement the other way around? + struct S s1; + *(getS()) = s1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct T t1; + *(getT()) = t1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t new file mode 100644 index 0000000000..4cfb965e25 --- /dev/null +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 91-distribute-fields-type-2.c + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s (race with conf. 100): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Success][Race] Memory location (struct S) (safe): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.c b/tests/regression/04-mutex/92-distribute-fields-type-deep.c new file mode 100644 index 0000000000..891f5fb51f --- /dev/null +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.c @@ -0,0 +1,47 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s >t< +// \ / \ / +// f s +// \ / +// f + +struct S { + int field; +}; + +struct T { + struct S s; +}; + +struct U { + struct T t; +}; + +// struct S s; +// struct T t; + +extern struct S* getS(); +extern struct T* getT(); +extern struct U* getU(); + +// getS could return the same struct as is contained in getT + +void *t_fun(void *arg) { + // should write to (struct U).t.s.field in addition to (struct T).s.field + // but easier to implement the other way around? + getS()->field = 1; // RACE! + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct T t1; + getU()->t = t1; // RACE! + return 0; +} diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t new file mode 100644 index 0000000000..12dc5e8f52 --- /dev/null +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -0,0 +1,33 @@ + $ goblint --enable allglobs 92-distribute-fields-type-deep.c + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 1 + total memory locations: 4 diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c new file mode 100644 index 0000000000..e0065b7870 --- /dev/null +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -0,0 +1,26 @@ +//PARAM: --enable ana.race.direct-arithmetic +#include +#include + +struct S { + int field; +}; + +struct S s; + +void *t_fun(void *arg) { + printf("%d",getS()->field); // RACE! + + return NULL; +} + +extern struct S* getS(); + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + struct S s1; + s = s1; // RACE! + return 0; +} + diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t new file mode 100644 index 0000000000..90baf61492 --- /dev/null +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -0,0 +1,5 @@ + $ goblint --enable allglobs 93-distribute-fields-type-global.c + 93-distribute-fields-type-global.c:12: Error: expecting a pointer to a struct + Error: There were parsing errors in .goblint/preprocessed/93-distribute-fields-type-global.i + Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error") + [2] From 2a003ab733b53c3e89eef205c6a152a9d71aadb7 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 15:16:24 +0300 Subject: [PATCH 029/238] Add cram tests and ASCII art to existing tests that cover access outer distribution --- .../04-mutex/77-type-nested-fields.c | 8 +++++ .../04-mutex/77-type-nested-fields.t | 29 +++++++++++++++++ .../04-mutex/79-type-nested-fields-deep1.c | 8 +++++ .../04-mutex/79-type-nested-fields-deep1.t | 31 +++++++++++++++++++ .../04-mutex/80-type-nested-fields-deep2.c | 8 +++++ .../04-mutex/80-type-nested-fields-deep2.t | 29 +++++++++++++++++ tests/regression/06-symbeq/16-type_rc.c | 8 +++++ tests/regression/06-symbeq/16-type_rc.t | 30 +++++++++--------- 8 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 tests/regression/04-mutex/77-type-nested-fields.t create mode 100644 tests/regression/04-mutex/79-type-nested-fields-deep1.t create mode 100644 tests/regression/04-mutex/80-type-nested-fields-deep2.t diff --git a/tests/regression/04-mutex/77-type-nested-fields.c b/tests/regression/04-mutex/77-type-nested-fields.c index 6f173d6fec..a526defb06 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.c +++ b/tests/regression/04-mutex/77-type-nested-fields.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// >f< s +// \ / +// f + struct S { int field; }; diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t new file mode 100644 index 0000000000..2cbd339dfa --- /dev/null +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -0,0 +1,29 @@ + $ goblint --enable allglobs 77-type-nested-fields.c + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location (struct T).s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 1 + total memory locations: 2 diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.c b/tests/regression/04-mutex/79-type-nested-fields-deep1.c index ee99c40973..c38e700829 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.c +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// f s +// \ / +// >f< + struct S { int field; }; diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t new file mode 100644 index 0000000000..6bb9b040fd --- /dev/null +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -0,0 +1,31 @@ + $ goblint --enable allglobs 79-type-nested-fields-deep1.c + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.c b/tests/regression/04-mutex/80-type-nested-fields-deep2.c index 646acd9147..9a1e3028a3 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.c +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.c @@ -2,6 +2,14 @@ #include #include +// (int) (S) (T) (U) +// \ / \ / \ / +// f s t +// \ / \ / +// >f< s +// \ / +// >f< + struct S { int field; }; diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t new file mode 100644 index 0000000000..f14a315de2 --- /dev/null +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -0,0 +1,29 @@ + $ goblint --enable allglobs 80-type-nested-fields-deep2.c + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 1 + total memory locations: 2 diff --git a/tests/regression/06-symbeq/16-type_rc.c b/tests/regression/06-symbeq/16-type_rc.c index efeb6c768b..e9e7c7972b 100644 --- a/tests/regression/06-symbeq/16-type_rc.c +++ b/tests/regression/06-symbeq/16-type_rc.c @@ -1,6 +1,14 @@ // PARAM: --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" #include +//>(int)< (S) (T) (U) +// \ / \ / \ / +// >f< s t +// \ / \ / +// f s +// \ / +// f + struct s { int datum; pthread_mutex_t mutex; diff --git a/tests/regression/06-symbeq/16-type_rc.t b/tests/regression/06-symbeq/16-type_rc.t index 78c293b7ef..06a3b314a4 100644 --- a/tests/regression/06-symbeq/16-type_rc.t +++ b/tests/regression/06-symbeq/16-type_rc.t @@ -1,22 +1,22 @@ Disable info messages because race summary contains (safe) memory location count, which is different on Linux and OSX. $ goblint --enable warn.deterministic --disable warn.info --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" 16-type_rc.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:13:3-13:15) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:24:3-24:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:25:3-25:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:28:3-28:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:21:3-21:15) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:32:3-32:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:33:3-33:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:36:3-36:9) [Warning][Race] Memory location (struct s).datum (race with conf. 100): - write with [mhp:{tid=[main, t_fun@16-type_rc.c:27:3-27:37#top]}, thread:[main, t_fun@16-type_rc.c:27:3-27:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:13:3-13:15) - write with [mhp:{tid=[main]; created={[main, t_fun@16-type_rc.c:27:3-27:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (16-type_rc.c:28:3-28:9) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:12:12-12:24) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:23:3-23:14) + write with [mhp:{tid=[main, t_fun@16-type_rc.c:35:3-35:37#top]}, thread:[main, t_fun@16-type_rc.c:35:3-35:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:21:3-21:15) + write with [mhp:{tid=[main]; created={[main, t_fun@16-type_rc.c:35:3-35:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (16-type_rc.c:36:3-36:9) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 16-type_rc.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:13:3-13:15) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:24:3-24:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:25:3-25:16) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:28:3-28:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:21:3-21:15) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:32:3-32:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:33:3-33:16) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (16-type_rc.c:36:3-36:9) [Success][Race] Memory location (struct s).datum (safe): - write with [mhp:{tid=[main, t_fun@16-type_rc.c:27:3-27:37#top]}, thread:[main, t_fun@16-type_rc.c:27:3-27:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:13:3-13:15) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:12:12-12:24) - [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:23:3-23:14) + write with [mhp:{tid=[main, t_fun@16-type_rc.c:35:3-35:37#top]}, thread:[main, t_fun@16-type_rc.c:35:3-35:37#top]] (conf. 100) (exp: & s->datum) (16-type_rc.c:21:3-21:15) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:20:12-20:24) + [Error][Imprecise][Unsound] Function definition missing for get_s (16-type_rc.c:31:3-31:14) From 91b64e827dbaeacac52497482c72cbf8a7ceadd6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 16:24:48 +0300 Subject: [PATCH 030/238] Fix test 04 93 --- .../93-distribute-fields-type-global.c | 4 +-- .../93-distribute-fields-type-global.t | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c index e0065b7870..ad7839d95f 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.c +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -8,14 +8,14 @@ struct S { struct S s; +extern struct S* getS(); + void *t_fun(void *arg) { printf("%d",getS()->field); // RACE! return NULL; } -extern struct S* getS(); - int main(void) { pthread_t id; pthread_create(&id, NULL, t_fun, NULL); diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 90baf61492..30f61cb3cc 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,5 +1,25 @@ $ goblint --enable allglobs 93-distribute-fields-type-global.c - 93-distribute-fields-type-global.c:12: Error: expecting a pointer to a struct - Error: There were parsing errors in .goblint/preprocessed/93-distribute-fields-type-global.i - Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error") - [2] + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Warning][Race] Memory location s.field@93-distribute-fields-type-global.c:9:10-9:11 (race with conf. 110): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Success][Race] Memory location (struct S).field (safe): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 From 4ac338dbfa8813197663a250b742d71ed5b9bf51 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 19 Jun 2023 16:47:09 +0300 Subject: [PATCH 031/238] Move access outer distribute to raceAnalysis Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 41 ++++++++++++++++--- src/domains/access.ml | 23 +---------- .../04-mutex/79-type-nested-fields-deep1.t | 6 +-- .../04-mutex/84-distribute-fields-1.t | 4 +- .../04-mutex/85-distribute-fields-2.t | 4 +- .../04-mutex/86-distribute-fields-3.t | 4 +- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 802c83dadc..f2d2554abc 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -54,6 +54,12 @@ struct struct include TrieDomain.Make (OneOffset) (Access.AS) + let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = + match offset with + | `NoOffset -> accs + | `Field (f, offset') -> find offset' (ChildMap.find (Field f) children) + | `Index ((), offset') -> find offset' (ChildMap.find Index children) + let rec singleton (offset : Offset.Unit.t) (value : value) : t = match offset with | `NoOffset -> (value, ChildMap.empty ()) @@ -99,6 +105,22 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (Access.AS.singleton (conf, w, loc, e, a)))); side_vars ctx memo + let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + match offset with + | `NoOffset -> None + | `Field (f, offset') -> Some (`Type f.ftype, offset') + | `Index ((), offset') -> None (* TODO *) + + let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = + let trie = G.access (ctx.global (V.access root)) in + let accs = OffsetTrie.find offset trie in + let outer_accs = + match outer_memo (root, offset) with + | Some outer_memo -> distribute_outer ctx outer_memo + | None -> Access.AS.empty () + in + Access.AS.union accs outer_accs + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> @@ -109,15 +131,22 @@ struct let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ancestor_accs = - let ancestor_accs' = Access.AS.union ancestor_accs accs in - OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs' - ) children; + let outer_accs = + match outer_memo (g', offset) with + | Some outer_memo -> distribute_outer ctx outer_memo + | None -> Access.AS.empty () + in + M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; + let ancestor_accs' = Access.AS.union ancestor_accs outer_accs in if not (Access.AS.is_empty accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs memo) accs - ) + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs:ancestor_accs' memo) accs + ); + let ancestor_accs'' = Access.AS.union ancestor_accs' accs in + OffsetTrie.ChildMap.iter (fun child_key child_trie -> + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs'' + ) children; in distribute_inner `NoOffset trie (Access.AS.empty ()) | `Right _ -> (* vars *) diff --git a/src/domains/access.ml b/src/domains/access.ml index 798a35ee9c..0e029ff128 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -211,27 +211,6 @@ let add_one side memo: unit = if not ignorable then side memo -(** Distribute type-based access to variables and containing fields. *) -let rec add_distribute_outer side (t: typ) (o: Offset.Unit.t) = - let memo = (`Type t, o) in - if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; - add_one side memo; - - (* distribute to variables of the type *) - let ts = typeSig t in - let vars = TSH.find_all typeVar ts in - List.iter (fun v -> - add_one side (`Var v, o) (* same offset, but on variable *) - ) vars; - - (* recursively distribute to fields containing the type *) - let fields = TSH.find_all typeIncl ts in - List.iter (fun f -> - (* prepend field and distribute to outer struct *) - add_distribute_outer side (TComp (f.fcomp, [])) (`Field (f, o)) - ) fields; - - if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) let add side e voffs = @@ -249,7 +228,7 @@ let add side e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer side t o (* distribute to variables and outer offsets *) + | _ -> add_one side (`Type t, o) (* add_distribute_outer side t o (* distribute to variables and outer offsets *)*) end; if M.tracing then M.traceu "access" "add\n" diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 6bb9b040fd..4075dab33b 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -17,15 +17,13 @@ live: 7 dead: 0 total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 + total memory locations: 2 diff --git a/tests/regression/04-mutex/84-distribute-fields-1.t b/tests/regression/04-mutex/84-distribute-fields-1.t index eb2c43623f..27653d4759 100644 --- a/tests/regression/04-mutex/84-distribute-fields-1.t +++ b/tests/regression/04-mutex/84-distribute-fields-1.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location s@84-distribute-fields-1.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) [Warning][Race] Memory location s.data@84-distribute-fields-1.c:9:10-9:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}, thread:[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]] (conf. 110) (exp: & s.data) (84-distribute-fields-1.c:12:3-12:13) write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) - [Success][Race] Memory location s@84-distribute-fields-1.c:9:10-9:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@84-distribute-fields-1.c:18:3-18:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (84-distribute-fields-1.c:20:3-20:9) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 diff --git a/tests/regression/04-mutex/85-distribute-fields-2.t b/tests/regression/04-mutex/85-distribute-fields-2.t index 7039fc399c..19355f7bc9 100644 --- a/tests/regression/04-mutex/85-distribute-fields-2.t +++ b/tests/regression/04-mutex/85-distribute-fields-2.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location t.s@85-distribute-fields-2.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) [Warning][Race] Memory location t.s.data@85-distribute-fields-2.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}, thread:[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (85-distribute-fields-2.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) - [Success][Race] Memory location t.s@85-distribute-fields-2.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@85-distribute-fields-2.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t.s) (85-distribute-fields-2.c:26:3-26:11) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index 5557f3400a..9651a91923 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -3,11 +3,11 @@ live: 8 dead: 0 total lines: 8 + [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) - [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 From bdf116d466db245f7021eaf312df9392e8629adb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 20 Jun 2023 15:56:12 +0300 Subject: [PATCH 032/238] Handle global variables in outer_memo Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f2d2554abc..18426ebc4f 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -106,10 +106,11 @@ struct side_vars ctx memo let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = - match offset with - | `NoOffset -> None - | `Field (f, offset') -> Some (`Type f.ftype, offset') - | `Index ((), offset') -> None (* TODO *) + match root, offset with + | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) + | _, `NoOffset -> None + | _, `Field (f, offset') -> Some (`Type f.ftype, offset') + | _, `Index ((), offset') -> None (* TODO *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in From 31ec579e2d7b3b51ee293db8b42d96290cfaccfd Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 20 Jun 2023 16:43:01 +0300 Subject: [PATCH 033/238] Split ancestor accs and outer ancestor accs Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 12 ++++++------ src/domains/access.ml | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 18426ebc4f..2e9a3d5b4b 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -131,25 +131,25 @@ struct (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) - let rec distribute_inner offset (accs, children) ancestor_accs = + let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = let outer_accs = match outer_memo (g', offset) with | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - let ancestor_accs' = Access.AS.union ancestor_accs outer_accs in if not (Access.AS.is_empty accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs:ancestor_accs' memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs ); - let ancestor_accs'' = Access.AS.union ancestor_accs' accs in + let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in + let ancestor_accs' = Access.AS.union ancestor_accs accs in OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ancestor_accs'' + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ) children; in - distribute_inner `NoOffset trie (Access.AS.empty ()) + distribute_inner `NoOffset trie ~ancestor_accs:(Access.AS.empty ()) ~ancestor_outer_accs:(Access.AS.empty ()) | `Right _ -> (* vars *) () end diff --git a/src/domains/access.ml b/src/domains/access.ml index 0e029ff128..1dc6cbafce 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -357,9 +357,9 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -let group_may_race ~ancestor_accs accs = +let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = (* BFS to traverse one component with may_race edges *) - let rec bfs' ~ancestor_accs ~accs ~todo ~visited = + let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = let may_race_accs ~accs ~todo = AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> @@ -372,16 +372,21 @@ let group_may_race ~ancestor_accs accs = in let accs' = AS.diff accs todo in let ancestor_accs' = AS.diff ancestor_accs todo in + let ancestor_outer_accs' = AS.diff ancestor_outer_accs todo in + let outer_accs' = AS.diff outer_accs todo in let todo_accs = may_race_accs ~accs:accs' ~todo in - let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:(AS.diff todo ancestor_accs') in - let todo' = AS.union todo_accs todo_ancestor_accs in + let accs_todo = AS.inter todo accs in + let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in + let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in + let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in + let todo' = AS.union (AS.union todo_accs todo_ancestor_accs) (AS.union todo_ancestor_outer_accs todo_outer_accs) in let visited' = AS.union visited todo in if AS.is_empty todo' then (accs', visited') else - (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~accs:accs' ~todo:todo' ~visited:visited' + (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' in - let bfs accs acc = bfs' ~ancestor_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) let rec components comps accs = if AS.is_empty accs then @@ -457,7 +462,7 @@ let print_accesses memo grouped_accs = M.msg_group Success ~category:Race "Memory location %a (safe)" Memo.pretty memo (msgs safe_accs) ) -let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs memo accs = - let grouped_accs = group_may_race ~ancestor_accs accs in (* do expensive component finding only once *) +let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo accs = + let grouped_accs = group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs in (* do expensive component finding only once *) incr_summary ~safe ~vulnerable ~unsafe memo grouped_accs; print_accesses memo grouped_accs From 936d7b04331ebeee920bc699a7dc64dbc217ead8 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 22 Jun 2023 12:22:45 +0300 Subject: [PATCH 034/238] Fix races between ancestor and outer accs Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 29 +++++++--- src/domains/access.ml | 58 +++++++++++++++---- .../04-mutex/79-type-nested-fields-deep1.t | 6 +- .../04-mutex/86-distribute-fields-3.t | 8 ++- .../04-mutex/90-distribute-fields-type-1.t | 4 +- .../04-mutex/91-distribute-fields-type-2.t | 4 +- .../04-mutex/92-distribute-fields-type-deep.t | 14 +++-- .../93-distribute-fields-type-global.t | 4 +- 8 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 2e9a3d5b4b..e0fbf96c2f 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -52,7 +52,7 @@ struct module OffsetTrie = struct - include TrieDomain.Make (OneOffset) (Access.AS) + include TrieDomain.Make (OneOffset) (Lattice.LiftBot (Access.AS)) let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = match offset with @@ -63,8 +63,8 @@ struct let rec singleton (offset : Offset.Unit.t) (value : value) : t = match offset with | `NoOffset -> (value, ChildMap.empty ()) - | `Field (f, offset') -> (Access.AS.empty (), ChildMap.singleton (Field f) (singleton offset' value)) - | `Index ((), offset') -> (Access.AS.empty (), ChildMap.singleton Index (singleton offset' value)) + | `Field (f, offset') -> (`Bot, ChildMap.singleton (Field f) (singleton offset' value)) + | `Index ((), offset') -> (`Bot, ChildMap.singleton Index (singleton offset' value)) end module G = @@ -102,7 +102,13 @@ struct let side_access ctx (conf, w, loc, e, a) ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (Access.AS.singleton (conf, w, loc, e, a)))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); + side_vars ctx memo + + let side_access0 ctx ((memoroot, offset) as memo) = + (* ignore (Pretty.printf "memo: %a\n" Access.Memo.pretty memo); *) + if !AnalysisState.should_warn then + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = @@ -114,7 +120,11 @@ struct let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in - let accs = OffsetTrie.find offset trie in + let accs = + match OffsetTrie.find offset trie with + | `Lifted accs -> accs + | `Bot -> Access.AS.empty () + in let outer_accs = match outer_memo (root, offset) with | Some outer_memo -> distribute_outer ctx outer_memo @@ -132,13 +142,18 @@ struct let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = + let accs = + match accs with + | `Lifted accs -> accs + | `Bot -> Access.AS.empty () + in let outer_accs = match outer_memo (g', offset) with | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - if not (Access.AS.is_empty accs) then ( + if not (Access.AS.is_empty accs && Access.AS.is_empty ancestor_accs && Access.AS.is_empty outer_accs) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs @@ -172,7 +187,7 @@ struct let loc = Option.get !Node.current_node in let add_access conf voffs = let a = part_access (Option.map fst voffs) in - Access.add (side_access octx (conf, kind, loc, e, a)) e voffs; + Access.add (side_access octx (conf, kind, loc, e, a)) (side_access0 octx) e voffs; in let add_access_struct conf ci = let a = part_access None in diff --git a/src/domains/access.ml b/src/domains/access.ml index 1dc6cbafce..40ebb289db 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -211,9 +211,30 @@ let add_one side memo: unit = if not ignorable then side memo +(** Distribute type-based access to variables and containing fields. *) +let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = + let memo = (`Type t, o) in + if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; + add_one side memo; + + (* distribute to variables of the type *) + let ts = typeSig t in + let vars = TSH.find_all typeVar ts in + List.iter (fun v -> + add_one side0 (`Var v, o) (* same offset, but on variable *) + ) vars; + + (* recursively distribute to fields containing the type *) + let fields = TSH.find_all typeIncl ts in + List.iter (fun f -> + (* prepend field and distribute to outer struct *) + add_distribute_outer side0 side0 (TComp (f.fcomp, [])) (`Field (f, o)) + ) fields; + + if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) -let add side e voffs = +let add side side0 e voffs = begin match voffs with | Some (v, o) -> (* known variable *) if M.tracing then M.traceli "access" "add var %a%a\n" CilType.Varinfo.pretty v CilType.Offset.pretty o; @@ -228,7 +249,7 @@ let add side e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_one side (`Type t, o) (* add_distribute_outer side t o (* distribute to variables and outer offsets *)*) + | _ -> add_distribute_outer side side0 t o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" @@ -379,26 +400,43 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in - let todo' = AS.union (AS.union todo_accs todo_ancestor_accs) (AS.union todo_ancestor_outer_accs todo_outer_accs) in + let todo_ancestor_accs_cross = may_race_accs ~accs:ancestor_accs' ~todo:(AS.inter todo outer_accs) in + let todo_outer_accs_cross = may_race_accs ~accs:outer_accs' ~todo:(AS.inter todo ancestor_accs) in + let todos = [todo_accs; todo_ancestor_accs; todo_ancestor_outer_accs; todo_outer_accs; todo_ancestor_accs_cross; todo_outer_accs_cross] in + let todo' = List.reduce AS.union todos in let visited' = AS.union visited todo in if AS.is_empty todo' then - (accs', visited') + (accs', ancestor_accs', ancestor_outer_accs', outer_accs', visited') else (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' in - let bfs accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps accs = + let rec components comps ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs = if AS.is_empty accs then - comps + (comps, ancestor_accs, outer_accs) else ( let acc = AS.choose accs in - let (accs', comp) = bfs accs acc in + let (accs', ancestor_accs', ancestor_outer_accs', outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc in + let comps' = comp :: comps in + components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' + ) + in + (* ignore (Pretty.printf "ancestors0: %a outer0: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in + (* ignore (Pretty.printf "ancestors: %a outer: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + let rec components_cross comps ~ancestor_accs ~outer_accs = + if AS.is_empty ancestor_accs then + comps + else ( + let ancestor_acc = AS.choose ancestor_accs in + let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in + (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) let comps' = comp :: comps in - components comps' accs' + components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in - components [] accs + components_cross comps ~ancestor_accs ~outer_accs let race_conf accs = assert (not (AS.is_empty accs)); (* group_may_race should only construct non-empty components *) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 4075dab33b..33bc8eede4 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -23,7 +23,9 @@ write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 1 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 2 + total memory locations: 3 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index 9651a91923..af17297db9 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -5,11 +5,15 @@ total lines: 8 [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) + [Success][Race] Memory location t.s@86-distribute-fields-3.c:15:10-15:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: - safe: 1 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 2 + total memory locations: 3 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index dd862fa65a..65689ff4d4 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -17,11 +17,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) - [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) [Info][Race] Memory locations race summary: diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index 4cfb965e25..be365577f2 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -17,11 +17,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) - [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Success][Race] Memory location (struct S) (safe): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) [Info][Race] Memory locations race summary: diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index 12dc5e8f52..f605c4c4cf 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -17,17 +17,19 @@ live: 7 dead: 0 total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) [Success][Race] Memory location (struct S).field (safe): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Success][Race] Memory location (struct U).t.s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) - [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Info][Race] Memory locations race summary: - safe: 3 + safe: 4 vulnerable: 0 unsafe: 1 - total memory locations: 4 + total memory locations: 5 + +TODO: fix memory location numbers diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 30f61cb3cc..5a00f03dce 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -11,11 +11,11 @@ live: 7 dead: 0 total lines: 7 + [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Warning][Race] Memory location s.field@93-distribute-fields-type-global.c:9:10-9:11 (race with conf. 110): read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) - [Success][Race] Memory location s@93-distribute-fields-type-global.c:9:10-9:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Success][Race] Memory location (struct S).field (safe): read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) [Info][Race] Memory locations race summary: From edff1d3b94fdb84e26f3e98be6d0e3cf47548e2c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 22 Jun 2023 13:35:51 +0300 Subject: [PATCH 035/238] Fix memory location counts for cross races --- src/analyses/raceAnalysis.ml | 4 ++-- src/domains/access.ml | 7 ++++++- tests/regression/04-mutex/79-type-nested-fields-deep1.t | 6 ++---- tests/regression/04-mutex/86-distribute-fields-3.t | 8 ++------ .../regression/04-mutex/92-distribute-fields-type-deep.t | 8 ++------ 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index e0fbf96c2f..76b93892f6 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -153,10 +153,10 @@ struct | None -> Access.AS.empty () in M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; - if not (Access.AS.is_empty accs && Access.AS.is_empty ancestor_accs && Access.AS.is_empty outer_accs) then ( + if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs ); let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in let ancestor_accs' = Access.AS.union ancestor_accs accs in diff --git a/src/domains/access.ml b/src/domains/access.ml index 40ebb289db..9d5147f2b0 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -432,7 +432,12 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = let ancestor_acc = AS.choose ancestor_accs in let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) - let comps' = comp :: comps in + let comps' = + if AS.cardinal comp > 1 then + comp :: comps + else + comps (* ignore self-race ancestor_acc component, self-race checked at ancestor's level *) + in components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 33bc8eede4..4075dab33b 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -23,9 +23,7 @@ write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 - -TODO: fix memory location numbers + total memory locations: 2 diff --git a/tests/regression/04-mutex/86-distribute-fields-3.t b/tests/regression/04-mutex/86-distribute-fields-3.t index af17297db9..9651a91923 100644 --- a/tests/regression/04-mutex/86-distribute-fields-3.t +++ b/tests/regression/04-mutex/86-distribute-fields-3.t @@ -5,15 +5,11 @@ total lines: 8 [Success][Race] Memory location t@86-distribute-fields-3.c:15:10-15:11 (safe): write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) - [Success][Race] Memory location t.s@86-distribute-fields-3.c:15:10-15:11 (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Warning][Race] Memory location t.s.data@86-distribute-fields-3.c:15:10-15:11 (race with conf. 110): write with [mhp:{tid=[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}, thread:[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]] (conf. 110) (exp: & t.s.data) (86-distribute-fields-3.c:18:3-18:15) write with [mhp:{tid=[main]; created={[main, t_fun@86-distribute-fields-3.c:24:3-24:40#top]}}, thread:[main]] (conf. 110) (exp: & t) (86-distribute-fields-3.c:26:3-26:9) [Info][Race] Memory locations race summary: - safe: 2 + safe: 1 vulnerable: 0 unsafe: 1 - total memory locations: 3 - -TODO: fix memory location numbers + total memory locations: 2 diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index f605c4c4cf..c0f3beae2c 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -21,15 +21,11 @@ write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) [Success][Race] Memory location (struct U).t (safe): write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) - [Success][Race] Memory location (struct U).t.s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Info][Race] Memory locations race summary: - safe: 4 + safe: 2 vulnerable: 0 unsafe: 1 - total memory locations: 5 - -TODO: fix memory location numbers + total memory locations: 3 From f419f50c0c26d35835235d6fb76084a7d8546fdb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 22 Jun 2023 17:28:06 +0300 Subject: [PATCH 036/238] Replace print comments with tracing in group_may_race --- src/analyses/raceAnalysis.ml | 1 - src/domains/access.ml | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 76b93892f6..c004800db6 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -152,7 +152,6 @@ struct | Some outer_memo -> distribute_outer ctx outer_memo | None -> Access.AS.empty () in - M.trace "access" "outer accs = %a" Access.AS.pretty outer_accs; if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in diff --git a/src/domains/access.ml b/src/domains/access.ml index 9d5147f2b0..b31d69baf6 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -379,6 +379,7 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo true let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = + if M.tracing then M.tracei "access" "group_may_race\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = let may_race_accs ~accs ~todo = @@ -422,16 +423,15 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ) in - (* ignore (Pretty.printf "ancestors0: %a outer0: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in - (* ignore (Pretty.printf "ancestors: %a outer: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs); *) + if M.tracing then M.trace "access" "components\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; let rec components_cross comps ~ancestor_accs ~outer_accs = if AS.is_empty ancestor_accs then comps else ( let ancestor_acc = AS.choose ancestor_accs in let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in - (* ignore (Pretty.printf "ancestor: %a comp: %a\n" A.pretty ancestor_acc AS.pretty comp); *) + if M.tracing then M.trace "access" "components_cross\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs' AS.pretty outer_accs'; let comps' = if AS.cardinal comp > 1 then comp :: comps @@ -441,7 +441,9 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' ) in - components_cross comps ~ancestor_accs ~outer_accs + let components_cross = components_cross comps ~ancestor_accs ~outer_accs in + if M.tracing then M.traceu "access" "group_may_race\n"; + components_cross let race_conf accs = assert (not (AS.is_empty accs)); (* group_may_race should only construct non-empty components *) From 511fd366c0181999023f57e0e8a592d5f745f319 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 16:32:03 +0200 Subject: [PATCH 037/238] changed the invalidation of equivalences to use VarEq.may_change --- src/analyses/tmpSpecial.ml | 92 ++++++++++---------------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index a9b65189b2..9b88ab0317 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,5 +1,7 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) + For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) + +module VarEq = VarEq.Spec open GoblintCil open Analyses @@ -10,9 +12,9 @@ struct let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted - module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) - module MlLsProd = Lattice.Prod (ML) (LS) - module D = MapDomain.MapBot (Lval.CilLval) (MlLsProd) + module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) + module MLDeps = Lattice.Prod (ML) (Deps) + module D = MapDomain.MapBot (Lval.CilLval) (MLDeps) module C = Lattice.Unit let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = @@ -20,40 +22,17 @@ struct | NoOffset -> `NoOffset | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) - - let ls_of_lv ctx (lval:lval) : LS.t = - match lval with - | (Var v, offs) -> LS.of_list [(v, resolve offs)] - | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) - - let rec ls_of_exp ctx (exp:exp) : LS.t = - match exp with - | Const _ -> LS.empty () - | Lval lv -> ls_of_lv ctx lv - | SizeOf _ -> LS.empty () - | Real e -> ls_of_exp ctx e - | Imag e -> ls_of_exp ctx e - | SizeOfE e -> ls_of_exp ctx e - | SizeOfStr _ -> LS.empty () - | AlignOf _ -> LS.empty () - | AlignOfE e -> ls_of_exp ctx e - | UnOp (_,e,_) -> ls_of_exp ctx e - | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) - | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) - | CastE (_,e) -> ls_of_exp ctx e - | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) - | AddrOfLabel _ -> LS.empty () - | StartOf _ -> ctx.ask (Queries.MayPointTo exp) - + + let invalidate ask exp_w st = + D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st let context _ _ = () (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lval in - if M.tracing then M.trace "tmpSpecial" "lvalsWritten %a\n" LS.pretty lvalsWritten; - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local + if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + (* Invalidate all entrys from the map that are possibly written by the assignment *) + invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -74,6 +53,7 @@ struct let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let d = ctx.local in + let ask = Analyses.ask_of_ctx ctx in (* Just dbg prints *) (if M.tracing then @@ -83,55 +63,31 @@ struct let desc = LibraryFunctions.find f in + (* remove entrys, dependent on lvals that were possibly written by the special function *) - let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in - let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in - let deep_addrs = - if List.mem LibraryDesc.InvalidateGlobals desc.attrs then ( - foldGlobals !Cilfacade.current_file (fun acc global -> - match global with - | GVar (vi, _, _) when not (BaseUtil.is_static vi) -> - mkAddrOf (Var vi, NoOffset) :: acc - (* TODO: what about GVarDecl? (see "base.ml -> special_unknown_invalidate")*) - | _ -> acc - ) deep_addrs - ) - else - deep_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs - in + let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in + (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) + let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in (* same for lval assignment of the call*) let d = match lval with - | Some lv -> ( - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lv in - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d - ) + | Some lv -> invalidate ask (mkAddrOf lv) ctx.local | None -> d in (* add new math fun desc*) let d = match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let argsDep = List.fold_left LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in - let lvalsWritten = ls_of_lv ctx (Var v, offs) in - (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) + | Some ((Var v, offs) as lv), (Math { fun_args; }) -> + (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) - if LS.is_top argsDep || not (LS.is_empty (LS.meet argsDep lvalsWritten)) then + if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d - else - D.add (v, resolve offs) ((ML.lift fun_args, LS.union argsDep lvalsWritten)) d + else + D.add (v, resolve offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d + in if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; From 1bff8fb0222ffe6ac77616bfe1a713aa7ec70f6d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:05:40 +0200 Subject: [PATCH 038/238] Add InvalidMemoryDeallocation message category --- src/util/messageCategory.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index ddf91dba0b..5452225c26 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -12,6 +12,7 @@ type undefined_behavior = | NullPointerDereference | UseAfterFree | DoubleFree + | InvalidMemoryDeallocation | Uninitialized | DoubleLocking | Other @@ -65,6 +66,7 @@ struct let nullpointer_dereference: category = create @@ NullPointerDereference let use_after_free: category = create @@ UseAfterFree let double_free: category = create @@ DoubleFree + let invalid_memory_deallocation: category = create @@ InvalidMemoryDeallocation let uninitialized: category = create @@ Uninitialized let double_locking: category = create @@ DoubleLocking let other: category = create @@ Other @@ -102,6 +104,7 @@ struct | "nullpointer_dereference" -> nullpointer_dereference | "use_after_free" -> use_after_free | "double_free" -> double_free + | "invalid_memory_deallocation" -> invalid_memory_deallocation | "uninitialized" -> uninitialized | "double_locking" -> double_locking | "other" -> other @@ -113,6 +116,7 @@ struct | NullPointerDereference -> ["NullPointerDereference"] | UseAfterFree -> ["UseAfterFree"] | DoubleFree -> ["DoubleFree"] + | InvalidMemoryDeallocation -> ["InvalidMemoryDeallocation"] | Uninitialized -> ["Uninitialized"] | DoubleLocking -> ["DoubleLocking"] | Other -> ["Other"] @@ -223,6 +227,7 @@ let behaviorName = function |NullPointerDereference -> "NullPointerDereference" |UseAfterFree -> "UseAfterFree" |DoubleFree -> "DoubleFree" + |InvalidMemoryDeallocation -> "InvalidMemoryDeallocation" |Uninitialized -> "Uninitialized" |DoubleLocking -> "DoubleLocking" |Other -> "Other" From f8e8116460f9d8d5b8e91c2aa4b2390d6591477f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:06:02 +0200 Subject: [PATCH 039/238] Add checks for deallocation of non-dynamically allocated memory --- src/analyses/base.ml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 72cc6a614f..cdc8e5a7d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1997,6 +1997,20 @@ struct let st' = invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) gs st shallow_addrs in invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs + let check_free_of_non_heap_mem ctx special_fn ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) -> + let warn_if_not_heap_var special_fn var = + if not (ctx.ask (Queries.IsHeapVar var)) then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + in + let pointed_to_vars = + Queries.LS.elements a + |> List.map fst + in + List.iter (warn_if_not_heap_var special_fn) pointed_to_vars + | _ -> () + let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> @@ -2277,6 +2291,8 @@ struct | _ -> st end | Realloc { ptr = p; size }, _ -> + (* Realloc shouldn't be passed non-dynamically allocated memory *) + check_free_of_non_heap_mem ctx f p; begin match lv with | Some lv -> let ask = Analyses.ask_of_ctx ctx in @@ -2308,6 +2324,10 @@ struct | None -> st end + | Free ptr, _ -> + (* Free shouldn't be passed non-dynamically allocated memory *) + check_free_of_non_heap_mem ctx f ptr; + st | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine | Setjmp { env }, _ -> let ask = Analyses.ask_of_ctx ctx in From 25462b84432580f0a703a2d0a898eeb60f9a205c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:06:26 +0200 Subject: [PATCH 040/238] Add regression tests for invalid memory deallocation --- .../01-invalid-dealloc-simple.c | 14 +++++++++++ .../02-invalid-dealloc-struct.c | 14 +++++++++++ .../03-invalid-dealloc-array.c | 25 +++++++++++++++++++ .../75-invalid_dealloc/04-invalid-realloc.c | 25 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c create mode 100644 tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c create mode 100644 tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c create mode 100644 tests/regression/75-invalid_dealloc/04-invalid-realloc.c diff --git a/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c b/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c new file mode 100644 index 0000000000..16fbd593f4 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/01-invalid-dealloc-simple.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char const *argv[]) +{ + int a; + int *p = &a; + free(p); //WARN + + char b = 'b'; + char *p2 = &b; + free(p2); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c b/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c new file mode 100644 index 0000000000..6768103976 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/02-invalid-dealloc-struct.c @@ -0,0 +1,14 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) +{ + custom_t *var; + free(var); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c b/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c new file mode 100644 index 0000000000..c023b5fc53 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/03-invalid-dealloc-array.c @@ -0,0 +1,25 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) +{ + custom_t custom_arr[MAX_SIZE]; + free(custom_arr); //WARN + + int int_arr[MAX_SIZE]; + free(int_arr); //WARN + + char char_arr[MAX_SIZE]; + free(char_arr); //WARN + + char char_arr2[1]; + free(char_arr2); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/04-invalid-realloc.c b/tests/regression/75-invalid_dealloc/04-invalid-realloc.c new file mode 100644 index 0000000000..94cbf031c2 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/04-invalid-realloc.c @@ -0,0 +1,25 @@ +#include + +typedef struct custom_t { + int x; + int y; +} custom_t; + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) +{ + custom_t custom_arr[10]; + realloc(custom_arr, MAX_SIZE); //WARN + + int int_arr[100]; + realloc(int_arr, MAX_SIZE); //WARN + + char char_arr[1000]; + realloc(char_arr, MAX_SIZE); //WARN + + char char_arr2[1]; + realloc(char_arr2, MAX_SIZE); //WARN + + return 0; +} From acb1ca83d00ae5f09167e1b30b775a465736ecd1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:59:19 +0200 Subject: [PATCH 041/238] Warn once for invalid de-/realloc for entire points-to set This includes the case where the points-to-set is top --- src/analyses/base.ml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cdc8e5a7d3..556aa4440e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1998,18 +1998,14 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) -> - let warn_if_not_heap_var special_fn var = - if not (ctx.ask (Queries.IsHeapVar var)) then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - in - let pointed_to_vars = - Queries.LS.elements a - |> List.map fst - in - List.iter (warn_if_not_heap_var special_fn) pointed_to_vars - | _ -> () + let points_to_set = ctx.ask (Queries.MayPointTo ptr) in + let exists_non_heap_var = + Queries.LS.elements points_to_set + |> List.map fst + |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) + in + if exists_non_heap_var then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From ee80fd1761a7a780e46c2e9af25389bd268af65e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 11:30:04 +0200 Subject: [PATCH 042/238] Handle case when points-to set is top --- src/analyses/base.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 556aa4440e..a4a674cf2d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1999,13 +1999,17 @@ struct let check_free_of_non_heap_mem ctx special_fn ptr = let points_to_set = ctx.ask (Queries.MayPointTo ptr) in - let exists_non_heap_var = - Queries.LS.elements points_to_set - |> List.map fst - |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) - in - if exists_non_heap_var then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + begin try + let exists_non_heap_var = + (* elements throws Unsupported if the points-to set is top *) + Queries.LS.elements points_to_set + |> List.map fst + |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) + in + if exists_non_heap_var then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + with _ -> M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + end let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From 91388efe40f0ea7f432068bfa34c91cce00a82b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jul 2023 12:42:59 +0300 Subject: [PATCH 043/238] Fix UnionDomain crash on chrony --- src/analyses/base.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0e766401d9..1808014654 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1411,9 +1411,13 @@ struct let new_value = VD.update_offset (Queries.to_value_domain_ask a) old_value offs projected_value lval_raw ((Var x), cil_offset) t in if WeakUpdates.mem x st.weak then VD.join old_value new_value - else if invariant then + else if invariant then ( (* without this, invariant for ambiguous pointer might worsen precision for each individual address to their join *) - VD.meet old_value new_value + try + VD.meet old_value new_value + with Lattice.Uncomparable -> + new_value + ) else new_value in From 99fb013038d3b6c333f236537668e3513d8faceb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 28 Jul 2023 13:59:17 +0200 Subject: [PATCH 044/238] Refactor and avoid unnecessary queries and conversions --- src/analyses/base.ml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a4a674cf2d..3a2f4b52bd 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1998,18 +1998,14 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - let points_to_set = ctx.ask (Queries.MayPointTo ptr) in - begin try - let exists_non_heap_var = - (* elements throws Unsupported if the points-to set is top *) - Queries.LS.elements points_to_set - |> List.map fst - |> List.exists (fun var -> not (ctx.ask (Queries.IsHeapVar var))) - in - if exists_non_heap_var then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - with _ -> M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - end + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with + | Address a -> + let points_to_set = addrToLvalSet a in + if Q.LS.is_top points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + else if Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with From 2d5604657c5d3e48a3827ae56fca3c5c0d02b10f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 28 Jul 2023 15:57:25 +0300 Subject: [PATCH 045/238] Convert memmove library functions. fixes #1121 --- src/analyses/libraryFunctions.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index e3fce8718a..3cd3b308cd 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -17,6 +17,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); + ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -1001,9 +1004,6 @@ let invalidate_actions = [ "getpgrp", readsAll;(*safe*) "umount2", readsAll;(*safe*) "memchr", readsAll;(*safe*) - "memmove", writes [2;3];(*keep [2;3]*) - "__builtin_memmove", writes [2;3];(*keep [2;3]*) - "__builtin___memmove_chk", writes [2;3];(*keep [2;3]*) "waitpid", readsAll;(*safe*) "statfs", writes [1;3;4];(*keep [1;3;4]*) "mount", readsAll;(*safe*) From 89dde4c8314b9aa80c79ce00088157ec84d34bc5 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 17:58:22 +0200 Subject: [PATCH 046/238] implement suggestions from Github --- src/analyses/baseInvariant.ml | 8 ++-- src/analyses/libraryDesc.ml | 39 ++++--------------- src/analyses/tmpSpecial.ml | 8 +--- .../57-floats/19-library-invariant.c | 3 +- 4 files changed, 13 insertions(+), 45 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 1d0f34ef14..35e9bdc5b0 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -703,11 +703,11 @@ struct | TInt (ik, _) -> begin match x with | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs))); let tv_opt = ID.to_bool c in begin match tv_opt with | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + begin match ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs)) with | `Lifted (Isfinite xFloat) when tv -> inv_exp (`Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st | `Lifted (Isnan xFloat) when tv -> inv_exp (`Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st (* should be correct according to C99 standard*) @@ -739,8 +739,8 @@ struct | TFloat (fk, _) -> begin match x with | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs))); - begin match ctx.ask (Queries.TmpSpecial (v, TmpSpecial.Spec.resolve offs)) with + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs))); + begin match ctx.ask (Queries.TmpSpecial (v, Lval.CilLval.of_ciloffs offs)) with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (`Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st | `Lifted (Fabs (ret_fk, xFloat)) -> diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 903360a603..5268e7e1f1 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -145,40 +145,11 @@ let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): } module MathPrintable = struct - include Printable.Std + include Printable.StdLeaf type t = math [@@deriving eq, ord, hash] let name () = "MathPrintable" - let relift ml = ml - - let show = function - | Nan _ -> "nan" - | Inf _ -> "inf" - | Isfinite _ -> "isFinite" - | Isinf _ -> "isInf" - | Isnan _ -> "isNan" - | Isnormal _ -> "isNormal" - | Signbit _ -> "signbit" - | Isgreater _ -> "isGreater" - | Isgreaterequal _ -> "isGreaterEqual" - | Isless _ -> "isLess" - | Islessequal _ -> "isLessEqual" - | Islessgreater _ -> "isLessGreater" - | Isunordered _ -> "isUnordered" - | Ceil _ -> "ceil" - | Floor _ -> "floor" - | Fabs _ -> "fabs" - | Fmax _ -> "fmax" - | Fmin _ -> "fmin" - | Acos _ -> "acos" - | Asin _ -> "asin" - | Atan _ -> "atan" - | Atan2 _ -> "atan2" - | Cos _ -> "cos" - | Sin _ -> "sin" - | Tan _ -> "tan" - let pretty () = function | Nan (fk, exp) -> Pretty.dprintf "(%a )nan(%a)" d_fkind fk d_exp exp | Inf fk -> Pretty.dprintf "(%a )inf()" d_fkind fk @@ -206,8 +177,12 @@ module MathPrintable = struct | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp - let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) - let to_yojson _ = failwith "ToDo Implement in future" + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) end module MathLifted = Lattice.Flat (MathPrintable) (struct diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 9b88ab0317..4e00b58c92 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -17,12 +17,6 @@ struct module D = MapDomain.MapBot (Lval.CilLval) (MLDeps) module C = Lattice.Unit - let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = - match offs with - | NoOffset -> `NoOffset - | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) - | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) - let invalidate ask exp_w st = D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st @@ -85,7 +79,7 @@ struct if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d else - D.add (v, resolve offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d + D.add (v, Lval.CilLval.of_ciloffs offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d in diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 535a306174..1bf332aba9 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -46,8 +46,7 @@ void main() { __goblint_check(f >= -5.); __goblint_check(f <= 5.); } - if(__builtin_fabs(f) == -6.) { - // DEAD + if(__builtin_fabs(f) == -6.) { //WARN (dead branch) g = 0.; } From ff511297dc353ab7c0db09bdcf01f27617802eb1 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 19:11:43 +0200 Subject: [PATCH 047/238] removed unnecessary update_lval --- src/analyses/baseInvariant.ml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 35e9bdc5b0..25731f7127 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -697,7 +697,6 @@ struct | TFloat (fk, _) -> `Float (FD.of_int fk c) | _ -> `Int c in - let st = update_lval c x c' ID.pretty in (* handle special calls *) begin match t with | TInt (ik, _) -> @@ -716,13 +715,13 @@ struct | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (`Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | _ -> st + | _ -> update_lval c x c' ID.pretty end - | None -> st + | None -> update_lval c x c' ID.pretty end - | _ -> st + | _ -> update_lval c x c' ID.pretty end - | _ -> st + | _ -> update_lval c x c' ID.pretty end | `Float c -> let c' = match t with @@ -733,7 +732,6 @@ struct | TFloat (fk, _) -> `Float (FD.cast_to fk c) | _ -> `Float c in - let st = update_lval c x c' FD.pretty in (* handle special calls *) begin match t with | TFloat (fk, _) -> @@ -749,11 +747,11 @@ struct raise Analyses.Deadcode else inv_exp (`Float inv) xFloat st - | _ -> st + | _ -> update_lval c x c' FD.pretty end - | _ -> st + | _ -> update_lval c x c' FD.pretty end - | _ -> st + | _ -> update_lval c x c' FD.pretty end | `Address c -> let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) From e23c42d570ee2539a0f04c10ebd175254eab5065 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 11:43:18 +0200 Subject: [PATCH 048/238] Add check for UnknownPtr --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a2f4b52bd..d4ba662e97 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2003,7 +2003,7 @@ struct let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - else if Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set then + else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 49fa590a1b481b90de416fc93cc4f13b8f86b489 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 20:02:28 +0200 Subject: [PATCH 049/238] better inversion of floor/ceil --- src/cdomains/floatDomain.ml | 26 +++++++++++++++---- .../57-floats/19-library-invariant.c | 4 ++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index e7117c9b62..1b674a667f 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -670,11 +670,27 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function (*TODO: can probably be more precise*) - | (l, h) -> Interval (Float_t.lower_bound, h) - - let eval_inv_floor = function (*TODO: can probably be more precise*) - | (l, h) -> Interval (l, Float_t.upper_bound) + let eval_inv_ceil = function + | (l, h) -> + if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then + (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. + With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) + Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) + (* [succ(ceil(l) - 1.0), h] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + else + (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) + Interval (Float_t.pred l, h) + + let eval_inv_floor = function + | (l, h) -> + if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then + (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. + With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) + Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) + (* [l, pred(floor(h) + 1.0)] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + else + (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [h, h+1.0] could exist such that floor(x) = h appart from h itself *) + Interval (l, Float_t.succ h) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 1bf332aba9..35ccbc8dcf 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -46,18 +46,20 @@ void main() { __goblint_check(f >= -5.); __goblint_check(f <= 5.); } - if(__builtin_fabs(f) == -6.) { //WARN (dead branch) + if(__builtin_fabs(f) == -6.) { // WARN (dead branch) g = 0.; } // ceil, floor if(ceil(f) == 5.) { __goblint_check(f <= 5.); + __goblint_check(f >= 4.); __goblint_check(f > 4.); // TODO __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); + __goblint_check(f <= 6.); __goblint_check(f < 6.); // TODO __goblint_check(f >= 5.5); // UNKNOWN! } From fe2cdffb4849be983ebce91a81d48bbd224868c0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 1 Aug 2023 14:47:19 +0300 Subject: [PATCH 050/238] Add missing argument to __builtin___memmove_chk --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3cd3b308cd..8d809ba46a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -19,7 +19,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); + ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []; drop "os" []]); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); From 2efaf16f5f7b2ba36008b50cec0bed405ef8bfd5 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 1 Aug 2023 11:21:47 +0200 Subject: [PATCH 051/238] more sophisticated floor/ceil --- src/cdomains/floatDomain.ml | 65 +++++++--- src/cdomains/floatDomain.mli | 4 +- .../57-floats/19-library-invariant.c | 6 +- .../21-library-invariant-ceil-floor.c | 122 ++++++++++++++++++ 4 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 tests/regression/57-floats/21-library-invariant-ceil-floor.c diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 1b674a667f..baae801585 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -42,9 +42,9 @@ module type FloatArith = sig (** tan(x) *) (** {inversions of unary functions}*) - val inv_ceil : t -> t + val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) - val inv_floor : t -> t + val inv_floor : ?asPreciseAsConcrete:bool -> t -> t (** (inv_floor z -> x) if (z = floor(x)) *) val inv_fabs : t -> t (** (inv_fabs z -> x) if (z = fabs(x)) *) @@ -670,27 +670,39 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | (l, h) when l = h && l = Float_t.zero -> of_const 0. (*tan(0) = 0*) | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) - let eval_inv_ceil = function + let eval_inv_ceil ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) - Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) - (* [succ(ceil(l) - 1.0), h] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + if asPreciseAsConcrete then + (* in case abstract and concrete precision are the same, [succ(l - 1.0), h] is more precise *) + Interval (Float_t.succ (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)), h) + else + Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) else - (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) - Interval (Float_t.pred l, h) + (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) + if asPreciseAsConcrete then + Interval (l, h) + else + Interval (Float_t.pred l, h) - let eval_inv_floor = function + let eval_inv_floor ?(asPreciseAsConcrete=false) = function | (l, h) -> if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) - Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) - (* [l, pred(floor(h) + 1.0)] would be even more precise, in case abstract and concrete have same precision (float/double). Does not work for more precise type though (e.g. long double) *) + if asPreciseAsConcrete then + (* in case abstract and concrete precision are the same, [l, pred(floor(h) + 1.0)] is more precise than [l, floor(h) + 1.0] *) + Interval (l, Float_t.pred (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0))) + else + Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) else - (* if we knew the abstract and concrete precision are the same, we could return [l, h] as an interval, since no x in [h, h+1.0] could exist such that floor(x) = h appart from h itself *) - Interval (l, Float_t.succ h) + (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [h, h + 1.0] could exist such that floor(x) = h appart from h itself *) + if asPreciseAsConcrete then + Interval (l, h) + else + Interval (l, Float_t.succ h) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) @@ -769,8 +781,8 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let sin = eval_unop eval_sin let tan = eval_unop eval_tan - let inv_ceil = eval_unop ~warn:false eval_inv_ceil - let inv_floor = eval_unop ~warn:false eval_inv_floor + let inv_ceil ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_ceil ~asPreciseAsConcrete:asPreciseAsConcrete) + let inv_floor ?(asPreciseAsConcrete=false) = eval_unop ~warn:false (eval_inv_floor ~asPreciseAsConcrete:asPreciseAsConcrete) let inv_fabs op = match op with | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "unop %s" (show op))) @@ -883,8 +895,19 @@ module FloatIntervalImplLifted = struct let cos = lift (F1.cos, F2.cos) let sin = lift (F1.sin, F2.sin) let tan = lift (F1.tan, F2.tan) - let inv_ceil = lift (F1.inv_ceil, F2.inv_ceil) - let inv_floor = lift (F1.inv_floor, F2.inv_floor) + + let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function + | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_ceil a) + | FFloat128 a -> FFloat128 (F2.inv_ceil a) + + let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function + | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_floor a) + | FFloat128 a -> FFloat128 (F2.inv_floor a) + let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) let sub = lift2 (F1.sub, F2.sub) @@ -1133,10 +1156,12 @@ module FloatDomTupleImpl = struct let tan = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.tan); } - let inv_ceil = - map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil); } - let inv_floor = - map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor); } + (*"asPreciseAsConcrete" has no meaning here*) + let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_ceil ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } + (*"asPreciseAsConcrete" has no meaning here*) + let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = + map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } let inv_fabs = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } diff --git a/src/cdomains/floatDomain.mli b/src/cdomains/floatDomain.mli index 4052f633a7..32b850a7b3 100644 --- a/src/cdomains/floatDomain.mli +++ b/src/cdomains/floatDomain.mli @@ -43,9 +43,9 @@ module type FloatArith = sig (** tan(x) *) (** {inversions of unary functions}*) - val inv_ceil : t -> t + val inv_ceil : ?asPreciseAsConcrete:bool -> t -> t (** (inv_ceil z -> x) if (z = ceil(x)) *) - val inv_floor : t -> t + val inv_floor : ?asPreciseAsConcrete:bool -> t -> t (** (inv_floor z -> x) if (z = floor(x)) *) val inv_fabs : t -> t (** (inv_fabs z -> x) if (z = fabs(x)) *) diff --git a/tests/regression/57-floats/19-library-invariant.c b/tests/regression/57-floats/19-library-invariant.c index 35ccbc8dcf..93c133ce19 100644 --- a/tests/regression/57-floats/19-library-invariant.c +++ b/tests/regression/57-floats/19-library-invariant.c @@ -54,13 +54,13 @@ void main() { if(ceil(f) == 5.) { __goblint_check(f <= 5.); __goblint_check(f >= 4.); - __goblint_check(f > 4.); // TODO + __goblint_check(f > 4.); __goblint_check(f >= 4.5); // UNKNOWN! } if(floor(f) == 5.) { __goblint_check(f >= 5.); __goblint_check(f <= 6.); - __goblint_check(f < 6.); // TODO - __goblint_check(f >= 5.5); // UNKNOWN! + __goblint_check(f < 6.); + __goblint_check(f <= 5.5); // UNKNOWN! } } diff --git a/tests/regression/57-floats/21-library-invariant-ceil-floor.c b/tests/regression/57-floats/21-library-invariant-ceil-floor.c new file mode 100644 index 0000000000..040f8c5566 --- /dev/null +++ b/tests/regression/57-floats/21-library-invariant-ceil-floor.c @@ -0,0 +1,122 @@ +//PARAM: --enable ana.float.interval --set ana.activated[+] tmpSpecial +#include +#include +#include + +void main() { + float f; + double d; + long double ld; + + if(ceilf(f) == 5.f) { + __goblint_check(f >= 4.f); + __goblint_check(f > 4.f); + __goblint_check(f >= 4.5f); // UNKNOWN! + } + if(floorf(f) == 5.f) { + __goblint_check(f <= 6.f); + __goblint_check(f < 6.f); + __goblint_check(f <= 5.5f); // UNKNOWN! + } + + if(ceil(d) == 5.) { + __goblint_check(d >= 4.); + __goblint_check(d > 4.); + __goblint_check(d <= 4.5); // UNKNOWN! + } + if(floor(d) == 5.) { + __goblint_check(d <= 6.); + __goblint_check(d < 6.); + __goblint_check(d <= 5.5); // UNKNOWN! + } + + if(ceill(ld) == 5.l) { + __goblint_check(ld >= 4.l); + __goblint_check(ld > 4.l); // UNKNOWN + __goblint_check(ld >= 4.5l); // UNKNOWN! + } + if(floorl(ld) == 5.l) { + __goblint_check(ld <= 6.l); + __goblint_check(ld < 6.l); // UNKNOWN + __goblint_check(ld <= 5.5l); // UNKNOWN! + } + + // Edge cases: + // 9007199254740992.0 = 2^53; up to here all integer values are representable in double. + // 2^53+1 is the first that is not representable as double, only as a long double + long double max_int_l = 9007199254740992.0l; + + if(floorl(ld) == max_int_l) { + //floorl(ld) == 2^53 => ld in [2^53, 2^53 + 1.0]. This is not representable in double, so Goblint computes with ld in [2^53, 2^53 + 2.0] + __goblint_check(ld <= (max_int_l + 2.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld <= (max_int_l + 1.0l)); // UNKNOWN + } + if(ceill(ld) == - max_int_l) { + // analogous to explanation above but with negative signbit + __goblint_check(ld >= (- max_int_l - 2.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN + __goblint_check(ld >= (- max_int_l - 1.0l)); // UNKNOWN + } + + // 4503599627370496.0 = 2^52; from here up to 2^53 double is not able to represent any fractional part, i.e., only integers + // 2^52 + 0.5 is not representable as double, only as long double + long double no_fractional_l = 4503599627370496.0l; + + if(floorl(ld) == no_fractional_l) { + // floorl(ld) == 2^52 => ld < 2^52 + 1.0. + // If ld were a double, Goblint could compute with ld < pred(2^52 + 1.0), since we know no double can exist between pred(2^52 + 1.0) and 2^52 + 1.0. + // However for long double this does not hold, ase e.g. (2^52 + 0.5) is representable. + __goblint_check(ld <= (no_fractional_l + 1.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld < (no_fractional_l + 1.0l)); // UNKNOWN + } + if(ceill(ld) == - no_fractional_l) { + // analogous to explanation above but with negative signbit + __goblint_check(ld >= (- no_fractional_l - 1.0l)); + // as long as we abstract long doubles with intervals of doubles, the next should be UNKNOWN. + __goblint_check(ld > (- no_fractional_l - 1.0l)); // UNKNOWN + } + + // same tests, but this time with doubles. Here we can use the knowledge, which values are not representable + double max_int = (double)max_int_l; + if(floor(d) == max_int) { + __goblint_check(d <= (max_int + 2.0)); + __goblint_check(d <= (max_int + 1.0)); + } + if(ceil(d) == - max_int) { + __goblint_check(d >= (- max_int - 2.0)); + __goblint_check(d >= (- max_int - 1.0)); + } + + double no_fractional = (double)no_fractional_l; + if(floor(d) == no_fractional) { + __goblint_check(d <= (no_fractional + 1.0)); + __goblint_check(d < (no_fractional + 1.0)); + } + if(ceil(d) == - no_fractional) { + __goblint_check(d >= (- no_fractional - 1.0)); + __goblint_check(d > (- no_fractional - 1.0)); + } + + // same for float + float max_int_f = 16777216.0f; // 2^24 + if(floorf(f) == max_int_f) { + __goblint_check(f <= (max_int_f + 2.0f)); + __goblint_check(f <= (max_int_f + 1.0f)); + } + if(ceilf(f) == - max_int_f) { + __goblint_check(f >= (- max_int_f - 2.0f)); + __goblint_check(f >= (- max_int_f - 1.0f)); + } + + float no_fractional_f = 8388608.0f; // 2^23 + if(floorf(f) == no_fractional_f) { + __goblint_check(f <= (no_fractional_f + 1.0f)); + __goblint_check(f < (no_fractional_f + 1.0f)); + } + if(ceilf(f) == - no_fractional_f) { + __goblint_check(f >= (- no_fractional_f - 1.0f)); + __goblint_check(f > (- no_fractional_f - 1.0f)); + } +} From 07a7645df3dbdc696c31f902ae36e70d508db258 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 1 Aug 2023 22:10:55 +0200 Subject: [PATCH 052/238] fix indentation --- src/analyses/baseInvariant.ml | 352 +++++++++++++++++----------------- src/analyses/tmpSpecial.ml | 209 ++++++++++---------- 2 files changed, 280 insertions(+), 281 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 9bcd011f97..096d50b89b 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -588,202 +588,202 @@ struct Some (s1 @ s2) | _ -> None - in - (* find common exp from all equality pairs and list of other sides, if possible *) - let find_common = function - | [] -> assert false - | (e1, e2) :: eqs -> - let eqs_for_all_mem e = List.for_all (fun (e1, e2) -> CilType.Exp.(equal e1 e || equal e2 e)) eqs in - let eqs_map_remove e = List.map (fun (e1, e2) -> if CilType.Exp.equal e1 e then e2 else e1) eqs in - if eqs_for_all_mem e1 then - Some (e1, e2 :: eqs_map_remove e1) - else if eqs_for_all_mem e2 then - Some (e2, e1 :: eqs_map_remove e2) - else - None - in - let eqs_st = - let* eqs = split exp in - let* (e, es) = find_common eqs in - let v = eval e st in (* value of common exp *) - let vs = List.map (fun e -> eval e st) es in (* values of other sides *) - match v with - | Address _ -> - (* get definite addrs from vs *) - let rec to_definite_ad = function - | [] -> AD.empty () - | VD.Address a :: vs when AD.is_definite a -> - AD.union a (to_definite_ad vs) - | _ :: vs -> - AD.top () - in - let definite_ad = to_definite_ad vs in - let c' = VD.Address definite_ad in - Some (inv_exp c' e st) - | Int i -> - let ik = ID.ikind i in - let module BISet = IntDomain.BISet in - (* get definite ints from vs *) - let rec to_int_id = function - | [] -> ID.bot_of ik - | VD.Int i :: vs -> - begin match ID.to_int i with - | Some i' -> ID.join i (to_int_id vs) - | None -> ID.top_of ik - end - | _ :: vs -> - ID.top_of ik - in - let int_id = to_int_id vs in - let c' = VD.Int int_id in - Some (inv_exp c' e st) - | _ -> + in + (* find common exp from all equality pairs and list of other sides, if possible *) + let find_common = function + | [] -> assert false + | (e1, e2) :: eqs -> + let eqs_for_all_mem e = List.for_all (fun (e1, e2) -> CilType.Exp.(equal e1 e || equal e2 e)) eqs in + let eqs_map_remove e = List.map (fun (e1, e2) -> if CilType.Exp.equal e1 e then e2 else e1) eqs in + if eqs_for_all_mem e1 then + Some (e1, e2 :: eqs_map_remove e1) + else if eqs_for_all_mem e2 then + Some (e2, e1 :: eqs_map_remove e2) + else None - in - begin match eqs_st with - | Some st -> st - | None when ID.to_bool c = Some true -> - begin match inv_exp (Int c) arg1 st with - | st1 -> - begin match inv_exp (Int c) arg2 st with - | st2 -> D.join st1 st2 - | exception Analyses.Deadcode -> st1 - end - | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) - end - | None -> - st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) - end - | (BinOp (op, e1, e2, _) as e, Float _) - | (BinOp (op, e1, e2, _) as e, Int _) -> - let invert_binary_op c pretty c_int c_float = - if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; - (match eval e1 st, eval e2 st with - | Int a, Int b -> - let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) - let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) - let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp (Int a') e1 st in - let st'' = inv_exp (Int b') e2 st' in - st'' - | Float a, Float b -> - let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) - let a', b' = inv_bin_float (a, b) (c_float fkind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp (Float a') e1 st in - let st'' = inv_exp (Float b') e2 st' in - st'' - (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) - | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; - (* | Address a, Address b -> ... *) - | a1, a2 -> fallback (GobPretty.sprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) - (* use closures to avoid unused casts *) - in (match c_typed with - | Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) - | Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) - | _ -> failwith "unreachable") - | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) - let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in - let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) - if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; - begin match c_typed with - | Int c -> - let c' = match t with - | TPtr _ -> VD.Address (AD.of_int c) - | TInt (ik, _) - | TEnum ({ekind = ik; _}, _) -> Int (ID.cast_to ik c) - | TFloat (fk, _) -> Float (FD.of_int fk c) - | _ -> Int c - in - (* handle special calls *) - begin match t with + in + let eqs_st = + let* eqs = split exp in + let* (e, es) = find_common eqs in + let v = eval e st in (* value of common exp *) + let vs = List.map (fun e -> eval e st) es in (* values of other sides *) + match v with + | Address _ -> + (* get definite addrs from vs *) + let rec to_definite_ad = function + | [] -> AD.empty () + | VD.Address a :: vs when AD.is_definite a -> + AD.union a (to_definite_ad vs) + | _ :: vs -> + AD.top () + in + let definite_ad = to_definite_ad vs in + let c' = VD.Address definite_ad in + Some (inv_exp c' e st) + | Int i -> + let ik = ID.ikind i in + let module BISet = IntDomain.BISet in + (* get definite ints from vs *) + let rec to_int_id = function + | [] -> ID.bot_of ik + | VD.Int i :: vs -> + begin match ID.to_int i with + | Some i' -> ID.join i (to_int_id vs) + | None -> ID.top_of ik + end + | _ :: vs -> + ID.top_of ik + in + let int_id = to_int_id vs in + let c' = VD.Int int_id in + Some (inv_exp c' e st) + | _ -> + None + in + begin match eqs_st with + | Some st -> st + | None when ID.to_bool c = Some true -> + begin match inv_exp (Int c) arg1 st with + | st1 -> + begin match inv_exp (Int c) arg2 st with + | st2 -> D.join st1 st2 + | exception Analyses.Deadcode -> st1 + end + | exception Analyses.Deadcode -> inv_exp (Int c) arg2 st (* Deadcode falls through *) + end + | None -> + st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) + end + | (BinOp (op, e1, e2, _) as e, Float _) + | (BinOp (op, e1, e2, _) as e, Int _) -> + let invert_binary_op c pretty c_int c_float = + if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; + (match eval e1 st, eval e2 st with + | Int a, Int b -> + let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) + let ikres = Cilfacade.get_ikind_exp e in (* might be different from argument types, e.g. for LT, GT, EQ, ... *) + let a', b' = inv_bin_int (a, b) ikind (c_int ikres) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; + let st' = inv_exp (Int a') e1 st in + let st'' = inv_exp (Int b') e2 st' in + st'' + | Float a, Float b -> + let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) + let a', b' = inv_bin_float (a, b) (c_float fkind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; + let st' = inv_exp (Float a') e1 st in + let st'' = inv_exp (Float b') e2 st' in + st'' + (* Mixed Float and Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) + | Int _, Float _ | Float _, Int _ -> failwith "ill-typed program"; + (* | Address a, Address b -> ... *) + | a1, a2 -> fallback (GobPretty.sprintf "binop: got abstract values that are not Int: %a and %a" VD.pretty a1 VD.pretty a2) st) + (* use closures to avoid unused casts *) + in (match c_typed with + | Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) + | Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) + | _ -> failwith "unreachable") + | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) + let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in + let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) + if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a\n" d_lval x VD.pretty c_typed d_type t; + begin match c_typed with + | Int c -> + let c' = match t with + | TPtr _ -> VD.Address (AD.of_int c) + | TInt (ik, _) + | TEnum ({ekind = ik; _}, _) -> Int (ID.cast_to ik c) + | TFloat (fk, _) -> Float (FD.of_int fk c) + | _ -> Int c + in + (* handle special calls *) + begin match t with | TInt (ik, _) -> begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with - | Some tv -> - begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st - | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st - (* should be correct according to C99 standard*) - | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st - | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st - | _ -> update_lval c x c' ID.pretty - end - | None -> update_lval c x c' ID.pretty + | Some tv -> + begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with + | `Lifted (Isfinite xFloat) when tv -> inv_exp (Float (FD.finite (unroll_fk_of_exp xFloat))) xFloat st + | `Lifted (Isnan xFloat) when tv -> inv_exp (Float (FD.nan_of (unroll_fk_of_exp xFloat))) xFloat st + (* should be correct according to C99 standard*) + | `Lifted (Isgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isgreaterequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Ge, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Isless (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessequal (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (Le, xFloat, yFloat, (typeOf xFloat))) st + | `Lifted (Islessgreater (xFloat, yFloat)) -> inv_exp (Int (ID.of_bool ik tv)) (BinOp (LOr, (BinOp (Lt, xFloat, yFloat, (typeOf xFloat))), (BinOp (Gt, xFloat, yFloat, (typeOf xFloat))), (TInt (IBool, [])))) st + | _ -> update_lval c x c' ID.pretty + end + | None -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty end | _ -> update_lval c x c' ID.pretty - end - | Float c -> - let c' = match t with - (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) - | TInt (ik, _) -> VD.Int (FD.to_int ik c) - (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) - | TEnum ({ekind = ik; _}, _) -> Int (FD.to_int ik c) - | TFloat (fk, _) -> Float (FD.cast_to fk c) - | _ -> Float c - in - (* handle special calls *) - begin match t with + end + | Float c -> + let c' = match t with + (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) + | TInt (ik, _) -> VD.Int (FD.to_int ik c) + (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) + | TEnum ({ekind = ik; _}, _) -> Int (FD.to_int ik c) + | TFloat (fk, _) -> Float (FD.cast_to fk c) + | _ -> Float c + in + (* handle special calls *) + begin match t with | TFloat (fk, _) -> begin match x with | ((Var v), offs) -> if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with - | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Fabs (ret_fk, xFloat)) -> - let inv = FD.inv_fabs (FD.cast_to ret_fk c) in - if FD.is_bot inv then - raise Analyses.Deadcode - else - inv_exp (Float inv) xFloat st - | _ -> update_lval c x c' FD.pretty + | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st + | `Lifted (Fabs (ret_fk, xFloat)) -> + let inv = FD.inv_fabs (FD.cast_to ret_fk c) in + if FD.is_bot inv then + raise Analyses.Deadcode + else + inv_exp (Float inv) xFloat st + | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty end | _ -> update_lval c x c' FD.pretty - end - | Address c -> - let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) - update_lval c x c' AD.pretty - | _ -> assert false - end - | Const _ , _ -> st (* nothing to do *) - | CastE ((TFloat (_, _)), e), Float c -> - (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with - | TFloat (FLongDouble as fk, _), FFloat - | TFloat (FDouble as fk, _), FFloat - | TFloat (FLongDouble as fk, _), FDouble - | TFloat (fk, _), FLongDouble - | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | _ -> fallback ("CastE: incompatible types") st) - | CastE ((TInt (ik, _)) as t, e), Int c - | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) - (match eval e st with - | Int i -> - if ID.leq i (ID.cast_to ik i) then - match unrollType (Cilfacade.typeOf e) with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - (* let c' = ID.cast_to ik_e c in *) - let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp (Int c') e st - | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st - else - fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st - | v -> fallback (GobPretty.sprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) - | e, _ -> fallback (GobPretty.sprintf "%a not implemented" d_plainexp e) st + end + | Address c -> + let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) + update_lval c x c' AD.pretty + | _ -> assert false + end + | Const _ , _ -> st (* nothing to do *) + | CastE ((TFloat (_, _)), e), Float c -> + (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with + | TFloat (FLongDouble as fk, _), FFloat + | TFloat (FDouble as fk, _), FFloat + | TFloat (FLongDouble as fk, _), FDouble + | TFloat (fk, _), FLongDouble + | TFloat (FDouble as fk, _), FDouble + | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st + | _ -> fallback ("CastE: incompatible types") st) + | CastE ((TInt (ik, _)) as t, e), Int c + | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) + (match eval e st with + | Int i -> + if ID.leq i (ID.cast_to ik i) then + match unrollType (Cilfacade.typeOf e) with + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + (* let c' = ID.cast_to ik_e c in *) + let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + inv_exp (Int c') e st + | x -> fallback (GobPretty.sprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st + else + fallback (GobPretty.sprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st + | v -> fallback (GobPretty.sprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) + | e, _ -> fallback (GobPretty.sprintf "%a not implemented" d_plainexp e) st in if eval_bool exp st = Some (not tv) then contra st (* we already know that the branch is dead *) else diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 0798c30ad1..5d43f69add 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,106 +1,105 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) - - module VarEq = VarEq.Spec - - open GoblintCil - open Analyses - - module Spec = - struct - include Analyses.IdentitySpec - - let name () = "tmpSpecial" - module ML = LibraryDesc.MathLifted - module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) - module MLDeps = Lattice.Prod (ML) (Deps) - module D = MapDomain.MapBot (Mval.Exp) (MLDeps) - module C = Lattice.Unit - - let invalidate ask exp_w st = - D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st - - let context _ _ = () - - (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; - (* Invalidate all entrys from the map that are possibly written by the assignment *) - invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - ctx.local - - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) - [ctx.local, D.bot ()] - - let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) - D.bot () - - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let d = ctx.local in - let ask = Analyses.ask_of_ctx ctx in - - (* Just dbg prints *) - (if M.tracing then - match lval with - | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv - | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); - - - let desc = LibraryFunctions.find f in - - (* remove entrys, dependent on lvals that were possibly written by the special function *) - let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in - (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) - let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in - - (* same for lval assignment of the call*) - let d = - match lval with - | Some lv -> invalidate ask (mkAddrOf lv) ctx.local - | None -> d - in - - (* add new math fun desc*) - let d = - match lval, desc.special arglist with - | Some ((Var v, offs) as lv), (Math { fun_args; }) -> - (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) - (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) - if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then - d - else - D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d - | _ -> d - - in - - if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; - d - - - let query ctx (type a) (q: a Queries.t) : a Queries.result = - match q with - | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in - if ML.is_bot ml then Queries.Result.top q - else ml - | _ -> Queries.Result.top q - - let startstate v = D.bot () - let threadenter ctx lval f args = [D.bot ()] - let threadspawn ctx lval f args fctx = ctx.local - let exitstate v = D.bot () - end - - let _ = - MCP.register_analysis (module Spec : MCPSpec) - \ No newline at end of file + For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) + +module VarEq = VarEq.Spec + +open GoblintCil +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + let name () = "tmpSpecial" + module ML = LibraryDesc.MathLifted + module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) + module MLDeps = Lattice.Prod (ML) (Deps) + module D = MapDomain.MapBot (Mval.Exp) (MLDeps) + module C = Lattice.Unit + + let invalidate ask exp_w st = + D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st + + let context _ _ = () + + (* transfer functions *) + let assign ctx (lval:lval) (rval:exp) : D.t = + if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + (* Invalidate all entrys from the map that are possibly written by the assignment *) + invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + ctx.local + + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + [ctx.local, D.bot ()] + + let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + D.bot () + + let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let d = ctx.local in + let ask = Analyses.ask_of_ctx ctx in + + (* Just dbg prints *) + (if M.tracing then + match lval with + | Some lv -> if M.tracing then M.tracel "tmpSpecial" "Special: %s with lval %a\n" f.vname d_lval lv + | _ -> if M.tracing then M.tracel "tmpSpecial" "Special: %s\n" f.vname); + + + let desc = LibraryFunctions.find f in + + (* remove entrys, dependent on lvals that were possibly written by the special function *) + let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in + (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) + let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in + + (* same for lval assignment of the call*) + let d = + match lval with + | Some lv -> invalidate ask (mkAddrOf lv) ctx.local + | None -> d + in + + (* add new math fun desc*) + let d = + match lval, desc.special arglist with + | Some ((Var v, offs) as lv), (Math { fun_args; }) -> + (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) + (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) + if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then + d + else + D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d + | _ -> d + + in + + if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; + d + + + let query ctx (type a) (q: a Queries.t) : a Queries.result = + match q with + | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in + if ML.is_bot ml then Queries.Result.top q + else ml + | _ -> Queries.Result.top q + + let startstate v = D.bot () + let threadenter ctx lval f args = [D.bot ()] + let threadspawn ctx lval f args fctx = ctx.local + let exitstate v = D.bot () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) From 027b9cda653b95e63f904b417256da1b476546bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:41:35 +0300 Subject: [PATCH 053/238] Improve solside tracing --- src/framework/analyses.ml | 1 + src/solvers/td3.ml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index dd57f40c70..df3346af93 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -75,6 +75,7 @@ end module GVarF (V: SpecSysVar) = struct include Printable.Either (V) (CilType.Fundec) + let name () = "FromSpec" let spec x = `Left x let contexts x = `Right x diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index f0a728f73b..9621f69db8 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -429,7 +429,7 @@ module Base = if tracing then trace "sol2" "stable add %a\n" S.Var.pretty_trace y; HM.replace stable y (); if not (S.Dom.leq tmp old) then ( - if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x; + if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; let sided = match x with | Some x -> let sided = VS.mem x old_sides in From 152f34eebcc29471f43f4f7f49597b4b64fa5354 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:43:07 +0300 Subject: [PATCH 054/238] Handle all_index in evalbinop_base --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1808014654..5b7a16e002 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -397,6 +397,8 @@ struct Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.zero else match eq p1 p2 with Some x when x -> ID.of_int ik BI.one | _ -> bool_top ik) | Ne -> Int (if AD.is_bot (AD.meet p1 p2) then ID.of_int ik BI.one else match eq p1 p2 with Some x when x -> ID.of_int ik BI.zero | _ -> bool_top ik) + | IndexPI when AD.to_string p2 = ["all_index"] -> + addToAddrOp p1 (ID.top_of (Cilfacade.ptrdiff_ikind ())) | _ -> VD.top () end (* For other values, we just give up! *) From 6d0498326c395d35d51b23df9d754b866ff0fe7f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:43:35 +0300 Subject: [PATCH 055/238] Improve widen tracing --- src/cdomains/intDomain.ml | 2 +- src/solvers/td3.ml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index b1db3796a8..5417598360 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -760,7 +760,7 @@ struct 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; + if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a\n" pretty x pretty y pretty r; assert (leq x y); (* TODO: remove for performance reasons? *) r diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 9621f69db8..26df6aac95 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -334,6 +334,8 @@ module Base = ); if not (Timing.wrap "S.Dom.equal" (fun () -> S.Dom.equal old wpd) ()) then ( (* value changed *) if tracing then trace "sol" "Changed\n"; + (* if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a -> %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty old S.Dom.pretty wpd; *) + if tracing && not (S.Dom.is_bot old) && HM.mem wpoint x then trace "solchange" "%a (wpx: %b): %a\n" S.Var.pretty_trace x (HM.mem wpoint x) S.Dom.pretty_diff (wpd, old); update_var_event x old wpd; HM.replace rho x wpd; destabilize x; From f0a05668ca991baa67ad5eadf4b722357bca263b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 May 2023 17:18:38 +0300 Subject: [PATCH 056/238] Fix var_eq may_change for other argument being constant --- src/analyses/varEq.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 99307d5d37..3054f2e0da 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -291,7 +291,7 @@ struct | Question (b, t, f, _) -> lval_may_change_pt b bl || lval_may_change_pt t bl || lval_may_change_pt f bl in let r = - if Cil.isConstant b then false + if Cil.isConstant b || Cil.isConstant a then false else if Queries.LS.is_top bls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) bls then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.LS.exists (lval_may_change_pt a) bls From 5372a76b0b2dad8a89193cbcc14e0c2ff33e0945 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 May 2023 13:46:38 +0300 Subject: [PATCH 057/238] Disable CIL check in unassume because variables of typedef types fail --- src/analyses/unassumeAnalysis.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 6b719c57b9..43707acd1e 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -111,7 +111,7 @@ struct Locator.ES.iter (fun n -> let fundec = Node.find_fundec n in - match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; NH.add invs n {exp = inv_exp; uuid} @@ -157,12 +157,12 @@ struct Locator.ES.iter (fun n -> let fundec = Node.find_fundec n in - match InvariantParser.parse_cil inv_parser ~fundec ~loc pre_cabs with + match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc pre_cabs with | Ok pre_exp -> M.debug ~category:Witness ~loc:msgLoc "located precondition to %a: %a" CilType.Fundec.pretty fundec Cil.d_exp pre_exp; FH.add fun_pres fundec pre_exp; - begin match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + begin match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; if not (NH.mem pre_invs n) then From 10881f05c6318ed501249986858b80a5c49f0c70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 May 2023 13:46:50 +0300 Subject: [PATCH 058/238] Add solchange tracing to TD3 side effects --- src/solvers/td3.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 26df6aac95..29ad301292 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -432,6 +432,7 @@ module Base = HM.replace stable y (); if not (S.Dom.leq tmp old) then ( if tracing && not (S.Dom.is_bot old) then trace "solside" "side to %a (wpx: %b) from %a: %a -> %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty old S.Dom.pretty tmp; + if tracing && not (S.Dom.is_bot old) then trace "solchange" "side to %a (wpx: %b) from %a: %a\n" S.Var.pretty_trace y (HM.mem wpoint y) (Pretty.docOpt (S.Var.pretty_trace ())) x S.Dom.pretty_diff (tmp, old); let sided = match x with | Some x -> let sided = VS.mem x old_sides in From 90f155cb23bffb2ca55d1d26dc2df5dd1c8a4035 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 20 May 2023 11:04:56 +0300 Subject: [PATCH 059/238] Exclude more pthread types from base --- src/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 20c4f3bf21..9d8fcc5012 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -115,7 +115,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_mutexattr_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false From 5e19b23189b80012c6571b669b4233945036ae0b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 12:00:01 +0300 Subject: [PATCH 060/238] Fix relational witness literature examples to also validate --- tests/regression/56-witness/37-hh-ex3.c | 2 +- tests/regression/56-witness/37-hh-ex3.yml | 4 ++-- tests/regression/56-witness/40-bh-ex1-poly.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/regression/56-witness/37-hh-ex3.c b/tests/regression/56-witness/37-hh-ex3.c index c3f26b5cf1..e59fd53108 100644 --- a/tests/regression/56-witness/37-hh-ex3.c +++ b/tests/regression/56-witness/37-hh-ex3.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 37-hh-ex3.yml +// SKIP PARAM: --set ana.activated[+] apron --enable ana.apron.strengthening --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 37-hh-ex3.yml #include int main() { int i = 0; diff --git a/tests/regression/56-witness/37-hh-ex3.yml b/tests/regression/56-witness/37-hh-ex3.yml index 9a4562d6d2..d6cd5150a4 100644 --- a/tests/regression/56-witness/37-hh-ex3.yml +++ b/tests/regression/56-witness/37-hh-ex3.yml @@ -20,10 +20,10 @@ location: file_name: 37-hh-ex3.c file_hash: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f - line: 7 + line: 6 column: 4 function: main location_invariant: - string: 0 <= i && i <= 3 && j == 0 + string: 0 <= i && i <= 3 type: assertion format: C diff --git a/tests/regression/56-witness/40-bh-ex1-poly.yml b/tests/regression/56-witness/40-bh-ex1-poly.yml index e219e1f877..cdbd8d666b 100644 --- a/tests/regression/56-witness/40-bh-ex1-poly.yml +++ b/tests/regression/56-witness/40-bh-ex1-poly.yml @@ -20,10 +20,10 @@ location: file_name: 40-bh-ex1-poly.c file_hash: 34f781dcae089ecb6b7b2811027395fcb501b8477b7e5016f7b38081724bea28 - line: 8 + line: 7 column: 4 function: main location_invariant: - string: 0 <= i && i <= 3 && j == 0 + string: 0 <= i && i <= 3 type: assertion format: C From e018cb756ab36ac6f4a27d6056fd08305216435a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 14:57:25 +0300 Subject: [PATCH 061/238] Add non-terminating hh-ex3 --- src/cdomains/apron/apronDomain.apron.ml | 6 ++-- src/goblint.ml | 2 +- tests/regression/56-witness/63-hh-ex3-term.c | 33 +++++++++++++++++++ .../regression/56-witness/63-hh-ex3-term.yml | 25 ++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/regression/56-witness/63-hh-ex3-term.c create mode 100644 tests/regression/56-witness/63-hh-ex3-term.yml diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d9928df597..7dffafe967 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -693,16 +693,16 @@ struct let join x y = (* just to optimize joining folds, which start with bot *) - if is_bot x then + if is_bot x then (* TODO: also for non-empty env *) y - else if is_bot y then + else if is_bot y then (* TODO: also for non-empty env *) x else ( if M.tracing then M.traceli "apron" "join %a %a\n" pretty x pretty y; let j = join x y in if M.tracing then M.trace "apron" "j = %a\n" pretty j; let j = - if strengthening_enabled then + if strengthening_enabled then (* TODO: skip if same envs? *) strengthening j x y else j diff --git a/src/goblint.ml b/src/goblint.ml index a73d0a9fad..4ea3a3d242 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -73,7 +73,7 @@ let main () = exit 1 | Sys.Break -> (* raised on Ctrl-C if `Sys.catch_break true` *) do_stats (); - (* Printexc.print_backtrace BatInnerIO.stderr *) + Printexc.print_backtrace stderr; eprintf "%s\n" (MessageUtil.colorize ~fd:Unix.stderr ("{RED}Analysis was aborted by SIGINT (Ctrl-C)!")); Goblint_timing.teardown_tef (); exit 131 (* same exit code as without `Sys.catch_break true`, otherwise 0 *) diff --git a/tests/regression/56-witness/63-hh-ex3-term.c b/tests/regression/56-witness/63-hh-ex3-term.c new file mode 100644 index 0000000000..0d90e8753f --- /dev/null +++ b/tests/regression/56-witness/63-hh-ex3-term.c @@ -0,0 +1,33 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron --set ana.apron.domain polyhedra --enable ana.apron.strengthening --set ana.activated[+] unassume --set witness.yaml.unassume 63-hh-ex3-term.yml --enable ana.widen.tokens --disable witness.invariant.other --enable exp.arg +extern void __assert_fail (const char *__assertion, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert_perror_fail (int __errnum, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert (const char *__assertion, const char *__file, int __line) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); + +extern void abort(void); +void reach_error() { ((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "hh-ex3.c", 3, __extension__ __PRETTY_FUNCTION__); })); } +void __VERIFIER_assert(int cond) { if(!(cond)) { ERROR: {reach_error();abort();} } } +int main() { + int i = 0; + while (i < 4) { + int j = 0; + while (j < 4) { + i++; + j++; + __VERIFIER_assert(0 <= j); + __VERIFIER_assert(j <= i); + __VERIFIER_assert(i <= j + 3); + __VERIFIER_assert(j <= 4); + } + __VERIFIER_assert(0 <= j); + __VERIFIER_assert(j <= i); + __VERIFIER_assert(i <= j + 3); + __VERIFIER_assert(j <= 4); + i = i - j + 1; + } + return 0; +} diff --git a/tests/regression/56-witness/63-hh-ex3-term.yml b/tests/regression/56-witness/63-hh-ex3-term.yml new file mode 100644 index 0000000000..e635e24014 --- /dev/null +++ b/tests/regression/56-witness/63-hh-ex3-term.yml @@ -0,0 +1,25 @@ +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: d834761a-d0d7-4fea-bf42-2ff2b9a19143 + creation_time: 2022-10-12T10:59:25Z + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - /home/vagrant/eval-prec/prec/hh-ex3.i + input_file_hashes: + /home/vagrant/eval-prec/prec/hh-ex3.i: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + data_model: LP64 + language: C + location: + file_name: 63-hh-ex3-term.c + file_hash: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + line: 17 + column: 4 + function: main + location_invariant: + string: 0 <= i && i <= 3 + type: assertion + format: C From 0715a0451a6069368370aac1a6deeb29f2b3da36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 15:45:12 +0300 Subject: [PATCH 062/238] Simplify hh-ex3-term --- tests/regression/56-witness/63-hh-ex3-term.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/regression/56-witness/63-hh-ex3-term.c b/tests/regression/56-witness/63-hh-ex3-term.c index 0d90e8753f..80913c3b9d 100644 --- a/tests/regression/56-witness/63-hh-ex3-term.c +++ b/tests/regression/56-witness/63-hh-ex3-term.c @@ -19,14 +19,8 @@ int main() { i++; j++; __VERIFIER_assert(0 <= j); - __VERIFIER_assert(j <= i); - __VERIFIER_assert(i <= j + 3); - __VERIFIER_assert(j <= 4); } __VERIFIER_assert(0 <= j); - __VERIFIER_assert(j <= i); - __VERIFIER_assert(i <= j + 3); - __VERIFIER_assert(j <= 4); i = i - j + 1; } return 0; From ac8baaa8b551c7e6324d7e37c3d94d6256c9f355 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 16:26:01 +0300 Subject: [PATCH 063/238] Disable witness lifter if GraphML witness generation disabled --- src/framework/control.ml | 2 +- src/witness/witness.ml | 54 ++++++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 5ceddf2870..cd3a8b5f74 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = get_bool "ana.sv-comp.enabled" || get_bool "exp.arg" in + let arg_enabled = get_bool "witness.enabled" || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in diff --git a/src/witness/witness.ml b/src/witness/witness.ml index aff9a01383..f73a2755c8 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -297,12 +297,45 @@ struct module ArgTool = ArgTools.Make (R) module NHT = ArgTool.NHT + module type BiArgInvariant = + sig + include ArgTools.BiArg + val find_invariant: Node.t -> Invariant.t + end + let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = - let module Arg = (val ArgTool.create entrystates) in + let module Arg: BiArgInvariant = + (val if GobConfig.get_bool "witness.enabled" then ( + let module Arg = (val ArgTool.create entrystates) in + let module Arg = + struct + include Arg - let find_invariant (n, c, i) = - let context = {Invariant.default_context with path = Some i} in - ask_local (n, c) (Invariant context) + let find_invariant (n, c, i) = + let context = {Invariant.default_context with path = Some i} in + ask_local (n, c) (Invariant context) + end + in + (module Arg: BiArgInvariant) + ) + else ( + let module Arg = + struct + module Node = ArgTool.Node + module Edge = MyARG.InlineEdge + let next _ = [] + let prev _ = [] + let find_invariant _ = Invariant.none + let main_entry = + let lvar = WitnessUtil.find_main_entry entrystates in + (fst lvar, snd lvar, -1) + let iter_nodes f = f main_entry + let query _ q = Queries.Result.top q + end + in + (module Arg: BiArgInvariant) + ) + ) in match Task.specification with @@ -324,7 +357,7 @@ struct struct module Arg = Arg let result = Result.True - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation _ = false let is_sink _ = false end @@ -332,13 +365,13 @@ struct (module TaskResult:WitnessTaskResult) ) else ( let is_violation = function - | FunctionEntry f, _, _ when Svcomp.is_error_function f.svar -> true - | _, _, _ -> false + | FunctionEntry f when Svcomp.is_error_function f.svar -> true + | _ -> false in (* redefine is_violation to shift violations back by one, so enterFunction __VERIFIER_error is never used *) let is_violation n = Arg.next n - |> List.exists (fun (_, to_n) -> is_violation to_n) + |> List.exists (fun (_, to_n) -> is_violation (Arg.Node.cfgnode to_n)) in let violations = (* TODO: fold_nodes?s *) @@ -363,7 +396,7 @@ struct struct module Arg = Arg let result = Result.Unknown - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation = is_violation let is_sink = is_sink end @@ -454,7 +487,7 @@ struct struct module Arg = Arg let result = Result.True - let invariant = find_invariant + let invariant = Arg.find_invariant let is_violation _ = false let is_sink _ = false end @@ -480,7 +513,6 @@ struct print_task_result (module TaskResult); - (* TODO: use witness.enabled elsewhere as well *) if get_bool "witness.enabled" && (TaskResult.result <> Result.Unknown || get_bool "witness.unknown") then ( let witness_path = get_string "witness.path" in Timing.wrap "write" (write_file witness_path (module Task)) (module TaskResult) From 33eb6748eaa0f0b36e8fbc24744caf9f4cb3be86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 1 Aug 2023 17:01:46 +0300 Subject: [PATCH 064/238] Enable witness.invariant.other in svcomp-yaml-validate conf --- conf/svcomp-yaml-validate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index 05bb1ebcc2..fc2ada7143 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -79,7 +79,7 @@ "invariant": { "loop-head": true, "after-lock": false, - "other": false + "other": true } }, "solver": "td3", From 9127dadece4657c60ed81e3e9b78cd6ba98c986f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 10:22:39 +0300 Subject: [PATCH 065/238] Fix ARG enabled without ana.sv-comp.enabled --- src/framework/control.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index cd3a8b5f74..5cefc1a7de 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -14,7 +14,7 @@ module type S2S = functor (X : Spec) -> Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( GobConfig.building_spec := true; - let arg_enabled = get_bool "witness.enabled" || get_bool "exp.arg" in + let arg_enabled = (get_bool "ana.sv-comp.enabled" && get_bool "witness.enabled") || get_bool "exp.arg" in let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in From d512cb5e46e7c6e19e6ff80c1097c5ec183af2fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 10:25:48 +0300 Subject: [PATCH 066/238] Remove pthread_mutexattr_t from is_mutex_type --- src/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 9d8fcc5012..a239be7c83 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -115,7 +115,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_mutexattr_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false From 66204e417e23b40281c0b9973a94b62e653f2baf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 2 Feb 2023 14:05:38 +0200 Subject: [PATCH 067/238] Special case calloc with count 1 in base --- src/analyses/base.ml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 5b7a16e002..86c7cc5c2c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2277,10 +2277,18 @@ struct then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) else addr in let ik = Cilfacade.ptrdiff_ikind () in - let blobsize = ID.mul (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st size) (ID.cast_to ik @@ eval_int (Analyses.ask_of_ctx ctx) gs st n) in - (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + let sizeval = eval_int (Analyses.ask_of_ctx ctx) gs st size in + let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in + if ID.to_int countval = Some Z.one then ( + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)))] + ) + else ( + let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in + (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + ) | _ -> st end | Realloc { ptr = p; size }, _ -> From 22f6061df7d2c7bd3584c000389b9f0a7abf010e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 12:10:29 +0300 Subject: [PATCH 068/238] Fix trace-not-in-tracing semgrep rule for conjunctions --- .semgrep/tracing.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.semgrep/tracing.yml b/.semgrep/tracing.yml index 4892066c76..061b3efa0d 100644 --- a/.semgrep/tracing.yml +++ b/.semgrep/tracing.yml @@ -9,6 +9,7 @@ rules: - pattern: Messages.traceu - pattern: Messages.traceli - pattern-not-inside: if Messages.tracing then ... + - pattern-not-inside: if Messages.tracing && ... then ... message: trace functions should only be called if tracing is enabled at compile time languages: [ocaml] severity: WARNING From 1f53318999d3d1d425ed68fdf680dd68151e2d9c Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:03:26 +0300 Subject: [PATCH 069/238] Apply suggestions from code review Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 8d809ba46a..242c3f37fd 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -40,22 +40,22 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); ("getc", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [r; w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("sprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("snprintf", unknown (drop "buffer" [r_deep; w_deep] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); - ("fread", unknown [drop "buffer" [w_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fread", unknown [drop "buffer" [w]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("fseek", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "origin" []]); ("ftell", unknown [drop "stream" [r_deep]]); - ("fwrite", unknown [drop "buffer" [r_deep]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); - ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r_deep; w_deep]; drop "mode" []; drop "size" []]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); @@ -64,7 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strlen", special [__ "s" [r]] @@ fun s -> Strlen s); ("strstr", special [__ "haystack" [r]; __ "needle" [r]] @@ fun haystack needle -> Strstr { haystack; needle; }); ("strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); - ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r]; drop "delim" [r]]); + ("strtok", unknown ~attrs:[ThreadUnsafe] [drop "str" [r; w]; drop "delim" [r]]); ("__builtin_strcmp", special [__ "s1" [r]; __ "s2" [r]] @@ fun s1 s2 -> Strcmp { s1; s2; n = None; }); ("strncmp", special [__ "s1" [r]; __ "s2" [r]; __ "n" []] @@ fun s1 s2 n -> Strcmp { s1; s2; n = Some n; }); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); @@ -73,7 +73,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); - ("fscanf", unknown ((drop "stream" [r; w]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); (* TODO: why stream not r_deep; w_deep? *) + ("fscanf", unknown ((drop "stream" [r_deep; w_deep]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("sscanf", unknown ((drop "buffer" [r]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); ("__freading", unknown [drop "stream" [r]]); ("mbsinit", unknown [drop "ps" [r]]); @@ -99,7 +99,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("tolower", unknown [drop "ch" []]); ("toupper", unknown [drop "ch" []]); ("time", unknown [drop "arg" [w]]); - ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [r]]); + ("tmpnam", unknown ~attrs:[ThreadUnsafe] [drop "filename" [w]]); ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) @@ -112,7 +112,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); - ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]; drop "wc" []; drop "ps" [w_deep]]); + ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []; drop "ps" [r_deep; w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -128,9 +128,9 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("explicit_bzero", special [__ "dest" [w]; __ "count" []] @@ fun dest count -> Bzero { dest; count; }); ("__explicit_bzero_chk", special [__ "dest" [w]; __ "count" []; drop "os" []] @@ fun dest count -> Bzero { dest; count; }); - ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r_deep]]); + ("catgets", unknown ~attrs:[ThreadUnsafe] [drop "catalog" [r_deep]; drop "set_number" []; drop "message_number" []; drop "message" [r]]); ("crypt", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]; drop "salt" [r]]); - ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [r]]); + ("ctermid", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]]); ("dbm_clearerr", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]]); ("dbm_close", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep; f_deep]]); ("dbm_delete", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []]); @@ -138,7 +138,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dbm_fetch", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]; drop "key" []]); ("dbm_firstkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); ("dbm_nextkey", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep]]); - ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r_deep; w_deep]; drop "open_flags" []; drop "file_mode" []]); + ("dbm_open", unknown ~attrs:[ThreadUnsafe] [drop "file" [r; w]; drop "open_flags" []; drop "file_mode" []]); ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); @@ -180,7 +180,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "stream" [r_deep; w_deep]]); ("getchar_unlocked", unknown ~attrs:[ThreadUnsafe] []); ("ptsname", unknown ~attrs:[ThreadUnsafe] [drop "fd" []]); - ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [w]]); + ("putc_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []; drop "stream" [r_deep; w_deep]]); ("putchar_unlocked", unknown ~attrs:[ThreadUnsafe] [drop "c" []]); ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); @@ -195,7 +195,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r]; drop "optstring" [r]]); + ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); ("iconv_close", unknown [drop "cd" [f]]); @@ -241,7 +241,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); - ("closedir", unknown [drop "dirp" [w]]); + ("closedir", unknown [drop "dirp" [r]]); ("mkdir", unknown [drop "pathname" [r]; drop "mode" []]); ("opendir", unknown [drop "name" [r]]); ("rmdir", unknown [drop "path" [r]]); @@ -286,8 +286,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *) ("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *) - ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); - ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [w]]); + ("pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); + ("__pthread_cond_init", unknown [drop "cond" [w]; drop "attr" [r]]); ("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("__pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond); ("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond); @@ -308,7 +308,7 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__pthread_mutex_unlock", special [__ "mutex" []] @@ fun mutex -> Unlock mutex); ("pthread_mutexattr_init", unknown [drop "attr" [w]]); ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); - ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [w]]); + ("pthread_rwlock_init", unknown [drop "rwlock" [w]; drop "attr" [r]]); ("pthread_rwlock_destroy", unknown [drop "rwlock" [f]]); ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = true}); @@ -317,18 +317,18 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_rwlock_unlock", special [__ "rwlock" []] @@ fun rwlock -> Unlock rwlock); ("pthread_rwlockattr_init", unknown [drop "attr" [w]]); ("pthread_rwlockattr_destroy", unknown [drop "attr" [f]]); - ("pthread_spin_init", unknown [drop "lock" []; drop "pshared" []]); + ("pthread_spin_init", unknown [drop "lock" [w]; drop "pshared" []]); ("pthread_spin_destroy", unknown [drop "lock" [f]]); ("pthread_spin_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); ("pthread_spin_trylock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = true; write = true; return_on_success = false}); ("pthread_spin_unlock", special [__ "lock" []] @@ fun lock -> Unlock lock); ("pthread_attr_init", unknown [drop "attr" [w]]); ("pthread_attr_destroy", unknown [drop "attr" [f]]); - ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [r]]); + ("pthread_attr_getdetachstate", unknown [drop "attr" [r]; drop "detachstate" [w]]); ("pthread_attr_setdetachstate", unknown [drop "attr" [w]; drop "detachstate" []]); - ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [r]]); + ("pthread_attr_getstacksize", unknown [drop "attr" [r]; drop "stacksize" [w]]); ("pthread_attr_setstacksize", unknown [drop "attr" [w]; drop "stacksize" []]); - ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [r]]); + ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [w]]); ("pthread_attr_setscope", unknown [drop "attr" [w]; drop "scope" []]); ("pthread_self", unknown []); ("pthread_sigmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); @@ -408,10 +408,10 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("euidaccess", unknown [drop "pathname" [r]; drop "mode" []]); ("rpmatch", unknown [drop "response" [r]]); ("getpagesize", unknown []); - ("__fgets_alias", unknown [drop "__s" [r; w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fgets_chk", unknown [drop "__s" [r; w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fread_alias", unknown [drop "__ptr" [w_deep]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); - ("__fread_chk", unknown [drop "__ptr" [w_deep]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_alias", unknown [drop "__s" [w]; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fgets_chk", unknown [drop "__s" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_alias", unknown [drop "__ptr" [w]; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); + ("__fread_chk", unknown [drop "__ptr" [w]; drop "__ptrlen" []; drop "__size" []; drop "__n" []; drop "__stream" [r_deep; w_deep]]); ("__read_chk", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []; drop "__buflen" []]); ("__read_alias", unknown [drop "__fd" []; drop "__buf" [w]; drop "__nbytes" []]); ("__readlink_chk", unknown [drop "path" [r]; drop "buf" [w]; drop "len" []; drop "buflen" []]); From 5c8bcfbf2b270acca278db4cd8b2094b6a3693d3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 2 Aug 2023 15:20:46 +0300 Subject: [PATCH 070/238] Implement comments from code review --- src/analyses/libraryFunctions.ml | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 242c3f37fd..2de54f9660 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -15,11 +15,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); - ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); - ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []; drop "os" []]); + ("memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; drop "count" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -33,20 +31,15 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); - ("free", unknown [drop "ptr" [f]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); ("ferror", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); - ("getc", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); - ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); + ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); @@ -87,6 +80,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); + ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strftime", unknown [drop "str" [w]; drop "count" []; drop "format" [r]; drop "tp" [r]]); @@ -107,7 +104,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); - ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' []))); + ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); @@ -185,16 +182,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); - ("setgrent", unknown ~attrs:[ThreadUnsafe] []); - ("setpwent", unknown ~attrs:[ThreadUnsafe] []); - ("setutxent", unknown ~attrs:[ThreadUnsafe] []); - ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); @@ -438,6 +435,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) + ("mempcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -449,7 +448,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); - ("__fprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]]); + ("__fprintf_chk", unknown (drop "stream" [r_deep; w_deep] :: drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) From f2fd9b5e5bdebc92631088c22e6b43401903b85f Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 2 Aug 2023 22:32:05 +0300 Subject: [PATCH 071/238] Update src/analyses/libraryFunctions.ml Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 3accc6d2e8..d49800f33a 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -141,7 +141,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dbm_store", unknown ~attrs:[ThreadUnsafe] [drop "db" [r_deep; w_deep]; drop "key" []; drop "content" []; drop "store_mode" []]); ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); - ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" []; drop "edflag" []]); + ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" [r; w]; drop "edflag" []]); ("endgrent", unknown ~attrs:[ThreadUnsafe] []); ("endpwent", unknown ~attrs:[ThreadUnsafe] []); ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); From 870a9b847fc4d786752a49353eeb322d7babd319 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:20:37 +0300 Subject: [PATCH 072/238] Fix PartitionDomain.SetSet.pretty_diff crash --- src/domains/partitionDomain.ml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/domains/partitionDomain.ml b/src/domains/partitionDomain.ml index eab15e1b05..9675e9bfce 100644 --- a/src/domains/partitionDomain.ml +++ b/src/domains/partitionDomain.ml @@ -115,18 +115,23 @@ struct for_all (fun p -> exists (B.leq p) y) x let pretty_diff () (y, x) = - (* based on DisjointDomain.PairwiseSet *) - let x_not_leq = filter (fun p -> - not (exists (fun q -> B.leq p q) y) - ) x - in - let p_not_leq = choose x_not_leq in - GoblintCil.Pretty.( - dprintf "%a:\n" B.pretty p_not_leq - ++ - fold (fun q acc -> - dprintf "not leq %a because %a\n" B.pretty q B.pretty_diff (p_not_leq, q) ++ acc - ) y nil + if E.is_top x then ( + GoblintCil.Pretty.(dprintf "%a not leq bot" pretty y) + ) + else ( + (* based on DisjointDomain.PairwiseSet *) + let x_not_leq = filter (fun p -> + not (exists (fun q -> B.leq p q) y) + ) x + in + let p_not_leq = choose x_not_leq in + GoblintCil.Pretty.( + dprintf "%a:\n" B.pretty p_not_leq + ++ + fold (fun q acc -> + dprintf "not leq %a because %a\n" B.pretty q B.pretty_diff (p_not_leq, q) ++ acc + ) y nil + ) ) let meet xs ys = if is_bot xs || is_bot ys then bot () else From ab339c5e5fdaf68cd4e7a836adfc1ff720e79bd6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:26:40 +0300 Subject: [PATCH 073/238] Move back accidentally moved library functions --- src/analyses/libraryFunctions.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d49800f33a..bb3517a755 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -36,6 +36,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ferror", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); @@ -81,9 +84,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); - ("setgrent", unknown ~attrs:[ThreadUnsafe] []); - ("setpwent", unknown ~attrs:[ThreadUnsafe] []); - ("setutxent", unknown ~attrs:[ThreadUnsafe] []); ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); @@ -184,16 +184,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); From 2267095562e397f4ee9400e5f45dd639cfae8933 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:27:12 +0300 Subject: [PATCH 074/238] Remove LibraryFunctions trailing whitespace, fix one VarArgs --- src/analyses/libraryFunctions.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index bb3517a755..447263b3ef 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -51,7 +51,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ftell", unknown [drop "stream" [r_deep]]); ("fwrite", unknown [drop "buffer" [r]; drop "size" []; drop "count" []; drop "stream" [r_deep; w_deep]]); ("rewind", unknown [drop "stream" [r_deep; w_deep]]); - ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); + ("setvbuf", unknown [drop "stream" [r_deep; w_deep]; drop "buffer" [r; w]; drop "mode" []; drop "size" []]); (* TODO: if this is used to set an input buffer, the buffer (second argument) would need to remain TOP, *) (* as any future write (or flush) of the stream could result in a write to the buffer *) ("gmtime", unknown ~attrs:[ThreadUnsafe] [drop "timer" [r_deep]]); @@ -364,7 +364,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__assert_rtn", special [drop "func" [r]; drop "file" [r]; drop "line" []; drop "exp" [r]] @@ Abort); (* MacOS's built-in assert *) ("__assert_fail", special [drop "assertion" [r]; drop "file" [r]; drop "line" []; drop "function" [r]] @@ Abort); (* gcc's built-in assert *) ("__builtin_return_address", unknown [drop "level" []]); - ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' []))); + ("__builtin___sprintf_chk", unknown (drop "s" [w] :: drop "flag" [] :: drop "os" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("__builtin_add_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); ("__builtin_sadd_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); ("__builtin_saddl_overflow", unknown [drop "a" []; drop "b" []; drop "c" [w]]); @@ -1119,7 +1119,7 @@ let invalidate_actions = [ ] let () = List.iter (fun (x, _) -> - if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then + if Hashtbl.exists (fun _ b -> List.mem_assoc x b) libraries then failwith ("You have added a function to invalidate_actions that already exists in libraries. Please undo this for function: " ^ x); ) invalidate_actions From ed231fa346bb8c4abbfd17c4c31cdbb77b4e316a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:46:31 +0300 Subject: [PATCH 075/238] Fix access analysis to not dereference non-pointer types --- src/analyses/accessAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 5245e4adfe..bc5330726c 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -44,7 +44,7 @@ struct let access_one_top ?(force=false) ?(deref=false) ctx (kind: AccessKind.t) reach exp = if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)\n" CilType.Exp.pretty exp AccessKind.pretty kind reach deref; if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) then ( - if deref then + if deref && Cil.isPointerType (Cilfacade.typeOf exp) then (* avoid dereferencing integers to unknown pointers, which cause many spurious type-based accesses *) do_access ctx kind reach exp; if M.tracing then M.tracei "access" "distribute_access_exp\n"; Access.distribute_access_exp (do_access ctx Read false) exp; From 346246954086c6be78c716873bb316c0b240ecbb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 10:53:00 +0200 Subject: [PATCH 076/238] Modify get in base in attempt to fix BlobSize --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7bc589df20..6ec15f0b31 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -462,7 +462,7 @@ struct let var = get_var a gs st x in let v = VD.eval_offset (Queries.to_value_domain_ask a) (fun x -> get a gs st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v; - if full then v else match v with + if full then var else match v with | Blob (c,s,_) -> c | x -> x in From 4fcfc715135d4721a156b0f9432ac0c1105142ae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 14:56:51 +0300 Subject: [PATCH 077/238] Add failing fixpoint test case for #1126 --- .../73-strings/04-smtprc_strlen_fp.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/regression/73-strings/04-smtprc_strlen_fp.c diff --git a/tests/regression/73-strings/04-smtprc_strlen_fp.c b/tests/regression/73-strings/04-smtprc_strlen_fp.c new file mode 100644 index 0000000000..a046eac238 --- /dev/null +++ b/tests/regression/73-strings/04-smtprc_strlen_fp.c @@ -0,0 +1,21 @@ +// FIXPOINT extracted from smtprc_comb +#include // for optarg + +typedef unsigned int size_t; // size_t from 32bit cilly +extern size_t strlen(char const *__s ); + +void *s_malloc(unsigned long size) +{ + void *mymem; + mymem = malloc((unsigned int) size); + return mymem; +} + +int main() { + char const *p; + size_t s; + p = optarg; + s = strlen(optarg); + s_malloc((unsigned long) ((s + 1U) * sizeof(char))); + return 0; +} From 409cbd1f622a387f558b95f54db6cc57909f9f42 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 14:57:43 +0300 Subject: [PATCH 078/238] Fix fixpoint issue from #1126 --- src/analyses/base.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 86c7cc5c2c..0c5805c72c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1055,7 +1055,6 @@ struct else if AD.may_be_null adr then M.warn ~category:M.Category.Behavior.Undefined.nullpointer_dereference ~tags:[CWE 476] "May dereference NULL pointer"); AD.map (add_offset_varinfo (convert_offset a gs st ofs)) adr - | Bot -> AD.bot () | _ -> M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; AD.unknown_ptr From ddecc8524347a0769ae425bb8738518f5b76025b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 15:49:17 +0300 Subject: [PATCH 079/238] Move options from unassume bench to yaml confs --- conf/bench-yaml-validate.json | 8 -------- conf/bench-yaml.json | 14 -------------- conf/svcomp-yaml-validate.json | 13 +++++-------- conf/svcomp-yaml.json | 10 +++++++++- 4 files changed, 14 insertions(+), 31 deletions(-) diff --git a/conf/bench-yaml-validate.json b/conf/bench-yaml-validate.json index ca830be08a..7b18371bd1 100644 --- a/conf/bench-yaml-validate.json +++ b/conf/bench-yaml-validate.json @@ -52,14 +52,6 @@ "tokens": true } }, - "witness": { - "enabled": false, - "invariant": { - "loop-head": true, - "after-lock": true, - "other": false - } - }, "sem": { "unknown_function": { "invalidate": { diff --git a/conf/bench-yaml.json b/conf/bench-yaml.json index a24035fc9b..fd97b2c08c 100644 --- a/conf/bench-yaml.json +++ b/conf/bench-yaml.json @@ -48,20 +48,6 @@ ] } }, - "witness": { - "enabled": false, - "yaml": { - "enabled": true - }, - "invariant": { - "exact": false, - "exclude-vars": [ - "tmp\\(___[0-9]+\\)?", - "cond", - "RETURN" - ] - } - }, "sem": { "unknown_function": { "invalidate": { diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index fc2ada7143..1934a56932 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -12,6 +12,10 @@ "float": { "interval": true }, + "apron": { + "domain": "polyhedra", + "strengthening": true + }, "activated": [ "base", "threadid", @@ -31,6 +35,7 @@ "region", "thread", "threadJoins", + "apron", "unassume" ], "context": { @@ -74,14 +79,6 @@ "exp": { "region-offsets": true }, - "witness": { - "enabled": false, - "invariant": { - "loop-head": true, - "after-lock": false, - "other": true - } - }, "solver": "td3", "sem": { "unknown_function": { diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index 6e3d0e4767..e09d1c80d7 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -12,6 +12,10 @@ "float": { "interval": true }, + "apron": { + "domain": "polyhedra", + "strengthening": true + }, "activated": [ "base", "threadid", @@ -30,7 +34,8 @@ "symb_locks", "region", "thread", - "threadJoins" + "threadJoins", + "apron" ], "context": { "widen": false @@ -76,6 +81,9 @@ "enabled": true }, "invariant": { + "loop-head": true, + "other": false, + "accessed": false, "exact": false, "exclude-vars": [ "tmp\\(___[0-9]+\\)?", From c72fdedf8bc2281375c6c3c287e7423ef7699b1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 16:12:39 +0200 Subject: [PATCH 080/238] Add MemoryLeak undefined behavior category --- src/util/messageCategory.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index 5452225c26..d1c8f0db3c 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -13,6 +13,7 @@ type undefined_behavior = | UseAfterFree | DoubleFree | InvalidMemoryDeallocation + | MemoryLeak | Uninitialized | DoubleLocking | Other @@ -67,6 +68,7 @@ struct let use_after_free: category = create @@ UseAfterFree let double_free: category = create @@ DoubleFree let invalid_memory_deallocation: category = create @@ InvalidMemoryDeallocation + let memory_leak: category = create @@ MemoryLeak let uninitialized: category = create @@ Uninitialized let double_locking: category = create @@ DoubleLocking let other: category = create @@ Other @@ -117,6 +119,7 @@ struct | UseAfterFree -> ["UseAfterFree"] | DoubleFree -> ["DoubleFree"] | InvalidMemoryDeallocation -> ["InvalidMemoryDeallocation"] + | MemoryLeak -> ["MemoryLeak"] | Uninitialized -> ["Uninitialized"] | DoubleLocking -> ["DoubleLocking"] | Other -> ["Other"] @@ -228,6 +231,7 @@ let behaviorName = function |UseAfterFree -> "UseAfterFree" |DoubleFree -> "DoubleFree" |InvalidMemoryDeallocation -> "InvalidMemoryDeallocation" + |MemoryLeak -> "MemoryLeak" |Uninitialized -> "Uninitialized" |DoubleLocking -> "DoubleLocking" |Other -> "Other" From 9cea9fb688b9b7aad13c68f455d6e0167689e93c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 3 Aug 2023 16:15:25 +0200 Subject: [PATCH 081/238] Add first version of a simple memory leak analysis --- src/analyses/memLeak.ml | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/analyses/memLeak.ml diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml new file mode 100644 index 0000000000..d4a1bc967d --- /dev/null +++ b/src/analyses/memLeak.ml @@ -0,0 +1,87 @@ +(** An analysis for the detection of memory leaks ([memLeak]). *) + +open GoblintCil +open Analyses +open MessageCategory + +module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) + +module Spec : Analyses.MCPSpec = +struct + include Analyses.DefaultSpec + + let name () = "memLeak" + + module D = ToppedVarInfoSet + module C = Lattice.Unit + + let context _ _ = () + + (* HELPER FUNCTIONS *) + let warn_for_multi_threaded ctx = + if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + + (* TRANSFER FUNCTIONS *) + let assign ctx (lval:lval) (rval:exp) : D.t = + ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + ctx.local + + let body ctx (f:fundec) : D.t = + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + let state = ctx.local in + (* TODO: Is this too hacky of a solution? *) + if f.svar.vname = "main" && not @@ D.is_empty state then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a\n" f.svar.vname D.pretty state; + state + + let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [ctx.local, ctx.local] + + let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + callee_local + + let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = + ctx.local + + let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let state = ctx.local in + let desc = LibraryFunctions.find f in + match desc.special arglist with + | Malloc _ + | Calloc _ + | Realloc _ -> + (* Warn about multi-threaded programs as soon as we encounter a dynamic memory allocation function *) + warn_for_multi_threaded ctx; + begin match ctx.ask Queries.HeapVar with + | `Lifted var -> D.add var state + | _ -> state + end + | Free ptr -> + begin match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) + let unique_pointed_to_heap_vars = + Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a + |> Queries.LS.elements + |> List.map fst + |> D.of_list + in + D.diff state unique_pointed_to_heap_vars + | _ -> state + end + | _ -> state + + let threadenter ctx lval f args = [ctx.local] + let threadspawn ctx lval f args fctx = ctx.local + + let startstate v = D.bot () + let exitstate v = D.top () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file From 933b4e3482175aab6336b0308dab2ccb1a9f7960 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Thu, 3 Aug 2023 17:27:16 +0200 Subject: [PATCH 082/238] indentation and removing unnecessary implementations --- src/analyses/libraryDesc.ml | 18 +++++++++--------- src/analyses/tmpSpecial.ml | 13 ++----------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index c009c3c2fb..94de4fbf82 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -186,15 +186,15 @@ module MathPrintable = struct | Sin (fk, exp) -> Pretty.dprintf "(%a )sin(%a)" d_fkind fk d_exp exp | Tan (fk, exp) -> Pretty.dprintf "(%a )tan(%a)" d_fkind fk d_exp exp - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) end module MathLifted = Lattice.Flat (MathPrintable) (struct - let top_name = "Unknown or no math desc" - let bot_name = "Nonexistent math desc" -end) + let top_name = "Unknown or no math desc" + let bot_name = "Nonexistent math desc" + end) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 5d43f69add..c4af80906e 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -28,21 +28,12 @@ struct (* Invalidate all entrys from the map that are possibly written by the assignment *) invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - - let return ctx (exp:exp option) (f:fundec) : D.t = - ctx.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + (* For now we only track relationships intraprocedurally. *) [ctx.local, D.bot ()] let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = - (* For now we only track relationships intraprocedurally. TODO: handle interprocedural tracking *) + (* For now we only track relationships intraprocedurally. *) D.bot () let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = From 3573182fa4e22ce8f448f6337b007f279c892e18 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 4 Aug 2023 10:24:36 +0200 Subject: [PATCH 083/238] Add two simple regression tests --- tests/regression/76-memleak/01-simple-no-mem-leak.c | 9 +++++++++ tests/regression/76-memleak/02-simple-mem-leak.c | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/regression/76-memleak/01-simple-no-mem-leak.c create mode 100644 tests/regression/76-memleak/02-simple-mem-leak.c diff --git a/tests/regression/76-memleak/01-simple-no-mem-leak.c b/tests/regression/76-memleak/01-simple-no-mem-leak.c new file mode 100644 index 0000000000..da6cdacddb --- /dev/null +++ b/tests/regression/76-memleak/01-simple-no-mem-leak.c @@ -0,0 +1,9 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + free(p); + + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/02-simple-mem-leak.c b/tests/regression/76-memleak/02-simple-mem-leak.c new file mode 100644 index 0000000000..c3086433dd --- /dev/null +++ b/tests/regression/76-memleak/02-simple-mem-leak.c @@ -0,0 +1,8 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + // No free => memory is leaked + return 0; //TODO: `make test` detects OTHER and not WARN here +} From f2b7d1c0f3ac1dcec8107a67eb6bb19869943e9a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 4 Aug 2023 17:24:29 +0300 Subject: [PATCH 084/238] Fix FloatDomain whitespace (PR #1041) --- src/cdomains/floatDomain.ml | 40 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index baae801585..f52c849111 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -671,38 +671,42 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct | _ -> top () (**could be exact for intervals where l=h, or even for some intervals *) let eval_inv_ceil ?(asPreciseAsConcrete=false) = function - | (l, h) -> - if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then + | (l, h) -> + if (Float_t.sub Up (Float_t.ceil l) (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)) = (Float_t.of_float Nearest 1.0)) then ( (* if [ceil(l) - (ceil(l) - 1.0) = 1.0], then we are in a range, where each int is expressable as float. - With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) + With that we can say, that [(ceil(x) >= l) => (x > (ceil(l) - 1.0)] *) if asPreciseAsConcrete then (* in case abstract and concrete precision are the same, [succ(l - 1.0), h] is more precise *) Interval (Float_t.succ (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0)), h) else Interval (Float_t.sub Down (Float_t.ceil l) (Float_t.of_float Nearest 1.0), h) - else + ) + else ( (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [l - 1.0, l] could exist such that ceil(x) = l appart from l itself *) if asPreciseAsConcrete then Interval (l, h) else Interval (Float_t.pred l, h) + ) let eval_inv_floor ?(asPreciseAsConcrete=false) = function - | (l, h) -> - if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then + | (l, h) -> + if (Float_t.sub Up (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) (Float_t.floor h) = (Float_t.of_float Nearest 1.0)) then ( (* if [(floor(h) + 1.0) - floor(h) = 1.0], then we are in a range, where each int is expressable as float. - With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) + With that we can say, that [(floor(x) <= h) => (x < (floor(h) + 1.0)] *) if asPreciseAsConcrete then (* in case abstract and concrete precision are the same, [l, pred(floor(h) + 1.0)] is more precise than [l, floor(h) + 1.0] *) Interval (l, Float_t.pred (Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0))) else Interval (l, Float_t.add Up (Float_t.floor h) (Float_t.of_float Nearest 1.0)) - else + ) + else ( (* if we know the abstract and concrete precision are the same, we return [l, h] as an interval, since no x in [h, h + 1.0] could exist such that floor(x) = h appart from h itself *) if asPreciseAsConcrete then Interval (l, h) else Interval (l, Float_t.succ h) + ) let eval_inv_fabs = function | (_, h) when h < Float_t.zero -> Bot (* Result of fabs cannot be negative *) @@ -897,16 +901,16 @@ module FloatIntervalImplLifted = struct let tan = lift (F1.tan, F2.tan) let inv_ceil ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function - | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) - | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) - | FLong a -> FLong (F2.inv_ceil a) - | FFloat128 a -> FFloat128 (F2.inv_ceil a) + | F32 a -> F32 (F1.inv_ceil ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_ceil ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_ceil a) + | FFloat128 a -> FFloat128 (F2.inv_ceil a) let inv_floor ?(asPreciseAsConcrete=BoolDomain.MustBool.top ()) = function - | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) - | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) - | FLong a -> FLong (F2.inv_floor a) - | FFloat128 a -> FFloat128 (F2.inv_floor a) + | F32 a -> F32 (F1.inv_floor ~asPreciseAsConcrete:true a) + | F64 a -> F64 (F2.inv_floor ~asPreciseAsConcrete:true a) + | FLong a -> FLong (F2.inv_floor a) + | FFloat128 a -> FFloat128 (F2.inv_floor a) let inv_fabs = lift (F1.inv_fabs, F2.inv_fabs) let add = lift2 (F1.add, F2.add) @@ -1078,7 +1082,7 @@ module FloatDomTupleImpl = struct let starting_after fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.starting_after fkind); } let finite = - create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } + create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.finite); } let of_string fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_string fkind); } @@ -1164,7 +1168,7 @@ module FloatDomTupleImpl = struct map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_floor ~asPreciseAsConcrete:(BoolDomain.MustBool.top ())); } let inv_fabs = map { f1= (fun (type a) (module F : FloatDomain with type t = a) -> F.inv_fabs); } - + (* f2: binary ops *) let join = map2 { f2= (fun (type a) (module F : FloatDomain with type t = a) -> F.join); } From 15a5b1a1470014017df01ba51e1ddf41c35a157f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 6 Aug 2023 14:34:29 +0200 Subject: [PATCH 085/238] Return bot in BlobSize queries if there's a non-heap var or some offset --- src/analyses/base.ml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6ec15f0b31..a213170ba2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1261,11 +1261,22 @@ struct (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> - let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in - (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) - (match r with - | Blob (_,s,_) -> `Lifted s - | _ -> Queries.Result.top q) + let s = addrToLvalSet a in + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + in + (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + Queries.Result.bot q + else ( + let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in + (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) + (match r with + | Blob (_,s,_) -> `Lifted s + | _ -> Queries.Result.top q) + ) | _ -> Queries.Result.top q end | Q.MayPointTo e -> begin From ad19f893aad67e9909d1de9969ea79177f087e2e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:11:15 +0200 Subject: [PATCH 086/238] Don't warn with a newline at the end --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index d4a1bc967d..1fc25c71ef 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -36,7 +36,7 @@ struct let state = ctx.local in (* TODO: Is this too hacky of a solution? *) if f.svar.vname = "main" && not @@ D.is_empty state then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a\n" f.svar.vname D.pretty state; + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a" f.svar.vname D.pretty state; state let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From 3711fc4e63ca2676404c2b821eda444f6120e0f1 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:12:33 +0200 Subject: [PATCH 087/238] Clean up comments of 2nd memleak test case --- tests/regression/76-memleak/02-simple-mem-leak.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/76-memleak/02-simple-mem-leak.c b/tests/regression/76-memleak/02-simple-mem-leak.c index c3086433dd..3673addfdf 100644 --- a/tests/regression/76-memleak/02-simple-mem-leak.c +++ b/tests/regression/76-memleak/02-simple-mem-leak.c @@ -4,5 +4,5 @@ int main(int argc, char const *argv[]) { int *p = malloc(sizeof(int)); // No free => memory is leaked - return 0; //TODO: `make test` detects OTHER and not WARN here + return 0; //WARN } From 90d0a64f0627e0b1419d73a7e0aeff1fd9eba43c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 16:16:23 +0200 Subject: [PATCH 088/238] Remove only definitely freed heap vars from state upon free() --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 1fc25c71ef..15a8947f15 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -63,7 +63,7 @@ struct end | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) let unique_pointed_to_heap_vars = Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a From 83d2578ded2cc8f228d6bd4d8d6d79cff7063430 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:41:08 +0200 Subject: [PATCH 089/238] Handle abort and assert special functions Make more elaborate warning for mem leaks --- src/analyses/memLeak.ml | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 15a8947f15..c7a7b97843 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -22,6 +22,14 @@ struct if not (ctx.ask (Queries.MustBeSingleThreaded { since_start = true })) then M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program isn't running in single-threaded mode. A memory leak might occur due to multi-threading" + let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = + let state = ctx.local in + if not @@ D.is_empty state then + if assert_exp_imprecise && Option.is_some exp then + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp (Option.get exp) D.pretty state + else + M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = ctx.local @@ -33,11 +41,9 @@ struct ctx.local let return ctx (exp:exp option) (f:fundec) : D.t = - let state = ctx.local in - (* TODO: Is this too hacky of a solution? *) - if f.svar.vname = "main" && not @@ D.is_empty state then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak from function \"%s\": %a" f.svar.vname D.pretty state; - state + (* Returning from "main" is one possible program exit => need to check for memory leaks *) + if f.svar.vname = "main" then check_for_mem_leak ctx; + ctx.local let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, ctx.local] @@ -74,6 +80,26 @@ struct D.diff state unique_pointed_to_heap_vars | _ -> state end + | Abort -> + (* An "Abort" special function indicates program exit => need to check for memory leaks *) + check_for_mem_leak ctx; + state + | Assert { exp; _ } -> + let warn_for_assert_exp = + match ctx.ask (Queries.EvalInt exp) with + | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp + | a -> + begin match Queries.ID.to_bool a with + | Some b -> + (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) + if b = false then + check_for_mem_leak ctx + else () + | None -> check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) + end + in + warn_for_assert_exp; + state | _ -> state let threadenter ctx lval f args = [ctx.local] From 79d13cd3e1f5a24026f3ee9ac45239c493f330d6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:41:41 +0200 Subject: [PATCH 090/238] Add further regression test cases --- tests/regression/76-memleak/03-simple-exit-mem-leak.c | 7 +++++++ tests/regression/76-memleak/04-simple-abort-mem-leak.c | 7 +++++++ .../76-memleak/05-simple-assert-no-mem-leak.c | 10 ++++++++++ .../regression/76-memleak/06-simple-assert-mem-leak.c | 8 ++++++++ 4 files changed, 32 insertions(+) create mode 100644 tests/regression/76-memleak/03-simple-exit-mem-leak.c create mode 100644 tests/regression/76-memleak/04-simple-abort-mem-leak.c create mode 100644 tests/regression/76-memleak/05-simple-assert-no-mem-leak.c create mode 100644 tests/regression/76-memleak/06-simple-assert-mem-leak.c diff --git a/tests/regression/76-memleak/03-simple-exit-mem-leak.c b/tests/regression/76-memleak/03-simple-exit-mem-leak.c new file mode 100644 index 0000000000..451dafa471 --- /dev/null +++ b/tests/regression/76-memleak/03-simple-exit-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + exit(0); //WARN +} diff --git a/tests/regression/76-memleak/04-simple-abort-mem-leak.c b/tests/regression/76-memleak/04-simple-abort-mem-leak.c new file mode 100644 index 0000000000..d4001410de --- /dev/null +++ b/tests/regression/76-memleak/04-simple-abort-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + abort(); //WARN +} diff --git a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c new file mode 100644 index 0000000000..bdaf448ee8 --- /dev/null +++ b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c @@ -0,0 +1,10 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + assert(1); //NOWARN + free(p); + return 0; //NOWARN +} diff --git a/tests/regression/76-memleak/06-simple-assert-mem-leak.c b/tests/regression/76-memleak/06-simple-assert-mem-leak.c new file mode 100644 index 0000000000..aff78ddd59 --- /dev/null +++ b/tests/regression/76-memleak/06-simple-assert-mem-leak.c @@ -0,0 +1,8 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + assert(0); //WARN +} From 3bf7d4a426a10300af6983496c20d31317325d6b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:47:26 +0200 Subject: [PATCH 091/238] Fix some regression test annotations --- tests/regression/76-memleak/05-simple-assert-no-mem-leak.c | 2 +- tests/regression/76-memleak/06-simple-assert-mem-leak.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c index bdaf448ee8..8dbf20c433 100644 --- a/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c +++ b/tests/regression/76-memleak/05-simple-assert-no-mem-leak.c @@ -4,7 +4,7 @@ int main(int argc, char const *argv[]) { int *p = malloc(sizeof(int)); - assert(1); //NOWARN + assert(1); free(p); return 0; //NOWARN } diff --git a/tests/regression/76-memleak/06-simple-assert-mem-leak.c b/tests/regression/76-memleak/06-simple-assert-mem-leak.c index aff78ddd59..b2f78388dc 100644 --- a/tests/regression/76-memleak/06-simple-assert-mem-leak.c +++ b/tests/regression/76-memleak/06-simple-assert-mem-leak.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +//PARAM: --set warn.assert false --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak #include #include From 908aa591cd85ae2feb8b6d041bd6da30c3b0a073 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 8 Aug 2023 18:52:55 +0200 Subject: [PATCH 092/238] Fix the mem leak check function --- src/analyses/memLeak.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index c7a7b97843..559f2badb7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -25,10 +25,9 @@ struct let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = let state = ctx.local in if not @@ D.is_empty state then - if assert_exp_imprecise && Option.is_some exp then - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp (Option.get exp) D.pretty state - else - M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state + match assert_exp_imprecise, exp with + | true, Some exp -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "assert expression %a is unknown. Memory leak might possibly occur for heap variables: %a" d_exp exp D.pretty state + | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = From c3c4bb1f96077c17276a4bd1ba5aec52ef7c42dd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 9 Aug 2023 15:12:56 +0200 Subject: [PATCH 093/238] Clean up the code a bit --- src/analyses/memLeak.ml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 559f2badb7..99df5695a7 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -8,7 +8,7 @@ module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topnam module Spec : Analyses.MCPSpec = struct - include Analyses.DefaultSpec + include Analyses.IdentitySpec let name () = "memLeak" @@ -30,29 +30,11 @@ struct | _ -> M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables: %a" D.pretty state (* TRANSFER FUNCTIONS *) - let assign ctx (lval:lval) (rval:exp) : D.t = - ctx.local - - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local - - let body ctx (f:fundec) : D.t = - ctx.local - let return ctx (exp:exp option) (f:fundec) : D.t = (* Returning from "main" is one possible program exit => need to check for memory leaks *) if f.svar.vname = "main" then check_for_mem_leak ctx; ctx.local - let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, ctx.local] - - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - callee_local - - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = - ctx.local - let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = let state = ctx.local in let desc = LibraryFunctions.find f in @@ -69,7 +51,7 @@ struct | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> - (* TODO: Need to always set "ana.malloc.unique_address_count" to smth > 0 *) + (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a |> Queries.LS.elements @@ -101,9 +83,6 @@ struct state | _ -> state - let threadenter ctx lval f args = [ctx.local] - let threadspawn ctx lval f args fctx = ctx.local - let startstate v = D.bot () let exitstate v = D.top () end From 0652ea09f4892c4f39350b50bdb596ecdac1d048 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 9 Aug 2023 16:01:11 +0200 Subject: [PATCH 094/238] Add support for quick_exit --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 447263b3ef..f4a9ac803d 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -68,6 +68,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); ("exit", special [drop "exit_code" []] Abort); + ("quick_exit", special [drop "exit_code" []] Abort); ("ungetc", unknown [drop "c" []; drop "stream" [r; w]]); ("scanf", unknown ((drop "format" [r]) :: (VarArgs (drop' [w])))); ("fscanf", unknown ((drop "stream" [r_deep; w_deep]) :: (drop "format" [r]) :: (VarArgs (drop' [w])))); From 755e4eef2b74107d05e1c7e5498bdc1123348ce6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 11 Aug 2023 14:02:40 +0200 Subject: [PATCH 095/238] Add regression test for a memory leak with quick_exit() --- .../regression/76-memleak/07-simple-quick-exit-mem-leak.c | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c diff --git a/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c b/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c new file mode 100644 index 0000000000..eba23385b8 --- /dev/null +++ b/tests/regression/76-memleak/07-simple-quick-exit-mem-leak.c @@ -0,0 +1,7 @@ +//PARAM: --set ana.malloc.unique_address_count 1 --set ana.activated[+] memLeak +#include + +int main(int argc, char const *argv[]) { + int *p = malloc(sizeof(int)); + quick_exit(0); //WARN +} From b1b710ef597878488789a726c409c99107885b38 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 14 Aug 2023 10:57:10 +0300 Subject: [PATCH 096/238] =?UTF-8?q?Add=20local=20widen/narrow=20example=20?= =?UTF-8?q?from=20A=C2=B2I=20paper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Tilscher <66023521+stilscher@users.noreply.github.com> --- .../regression/34-localwn_restart/06-td-a2i.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/regression/34-localwn_restart/06-td-a2i.c diff --git a/tests/regression/34-localwn_restart/06-td-a2i.c b/tests/regression/34-localwn_restart/06-td-a2i.c new file mode 100644 index 0000000000..e1fe6b05d0 --- /dev/null +++ b/tests/regression/34-localwn_restart/06-td-a2i.c @@ -0,0 +1,22 @@ +// PARAM: --enable ana.int.interval --set solver td3 --enable solvers.td3.remove-wpoint +// Example from "The Top-Down Solver — An Exercise in A²I", Section 6. +#include + +int main() { + int i, j, x; + i = 0; + while (i < 42) { + j = 0; + while (j < 17) { + x = i + j; + j++; + } + __goblint_check(j == 17); + __goblint_check(i >= 0); + __goblint_check(i <= 41); + i++; + } + __goblint_check(i == 42); + __goblint_check(j == 17); // TODO + return 0; +} From e11c982aac5925edc348389305ce1a48efb73aa0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 13:53:11 +0300 Subject: [PATCH 097/238] Make sem.unknown_function.call option --- src/analyses/libraryFunctions.ml | 3 ++- src/util/options.schema.json | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6495341c7a..7c31d69fb8 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1165,7 +1165,8 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul | Read when GobConfig.get_bool "sem.unknown_function.read.args" -> args | Read -> [] | Free -> [] - | Call -> [] (* TODO: option *) + | Call when get_bool "sem.unknown_function.call.args" -> args + | Call -> [] | Spawn when get_bool "sem.unknown_function.spawn" -> args | Spawn -> [] in diff --git a/src/util/options.schema.json b/src/util/options.schema.json index efa1dfabb8..0e89ede0b5 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1317,6 +1317,13 @@ "type": "boolean", "default": true }, + "call": { + "title": "sem.unknown_function.call", + "description": + "Unknown function call calls reachable functions", + "type": "boolean", + "default": true + }, "invalidate": { "title": "sem.unknown_function.invalidate", "type": "object", From f150fdca489dfe76e1510c6a78ececddc0a0c60e Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:37:09 +0300 Subject: [PATCH 098/238] Fix rand in libraryFunctions --- src/analyses/libraryFunctions.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7c31d69fb8..5dc311a587 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -84,7 +84,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("getchar", unknown []); ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); - ("rand", unknown ~attrs:[ThreadUnsafe] []); + ("rand", special ~attrs:[ThreadUnsafe] [] Rand); ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); @@ -118,7 +118,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("rand", special [] Rand); ] (** C POSIX library functions. From 379a7ff53d0f330e8fe92042c6cec9b37b369ccc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:37:36 +0300 Subject: [PATCH 099/238] Fix side_access call --- src/analyses/raceAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index c09396bdd3..970895e971 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -105,7 +105,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g' -> (* accesses *) - (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) + (* ignore (Pretty.printf "WarnGlobal %a\n" Access.MemoRoot.pretty g'); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ancestor_accs = @@ -202,7 +202,7 @@ struct let loc = Option.get !Node.current_node in let vo = Some f in let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in - side_access ctx (conf, Call, loc, e, a) ((`Type f.vtype), `NoOffset) ; + side_access ctx (conf, Call, loc, e, a) ((`Var f), `NoOffset) ; ); ctx.local From 8465e0b2eb3a72230ec3c327faa47fc969aad211 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 14:38:05 +0300 Subject: [PATCH 100/238] Rename tests to have unique numbers --- .../{77-thread-unsafe_fun_rc.c => 90-thread-unsafe_fun_rc.c} | 0 .../{78-thread-unsafe_fun_nr.c => 91-thread-unsafe_fun_nr.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/04-mutex/{77-thread-unsafe_fun_rc.c => 90-thread-unsafe_fun_rc.c} (100%) rename tests/regression/04-mutex/{78-thread-unsafe_fun_nr.c => 91-thread-unsafe_fun_nr.c} (100%) diff --git a/tests/regression/04-mutex/77-thread-unsafe_fun_rc.c b/tests/regression/04-mutex/90-thread-unsafe_fun_rc.c similarity index 100% rename from tests/regression/04-mutex/77-thread-unsafe_fun_rc.c rename to tests/regression/04-mutex/90-thread-unsafe_fun_rc.c diff --git a/tests/regression/04-mutex/78-thread-unsafe_fun_nr.c b/tests/regression/04-mutex/91-thread-unsafe_fun_nr.c similarity index 100% rename from tests/regression/04-mutex/78-thread-unsafe_fun_nr.c rename to tests/regression/04-mutex/91-thread-unsafe_fun_nr.c From 59feaf8beebfb16ec3e69bebe300ce781f4631bb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 14 Aug 2023 18:05:18 +0300 Subject: [PATCH 101/238] Create warn_accs record and refactor Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 2 +- src/domains/access.ml | 83 +++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index c004800db6..bc0709541d 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -155,7 +155,7 @@ struct if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo) accs + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix=ancestor_accs; type_suffix_prefix=ancestor_outer_accs; type_suffix=outer_accs; node=accs}) memo ); let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in let ancestor_accs' = Access.AS.union ancestor_accs accs in diff --git a/src/domains/access.ml b/src/domains/access.ml index b31d69baf6..918573835f 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -378,11 +378,18 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = - if M.tracing then M.tracei "access" "group_may_race\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; +type warn_accs = { + node: AS.t; + prefix: AS.t; + type_suffix: AS.t; + type_suffix_prefix: AS.t; +} + +let group_may_race warn_accs = + if M.tracing then M.tracei "access" "group_may_race\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; (* BFS to traverse one component with may_race edges *) - let rec bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo ~visited = - let may_race_accs ~accs ~todo = + let rec bfs' {prefix; type_suffix_prefix; type_suffix; node} ~todo ~visited = + let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> if may_race acc acc' then @@ -392,56 +399,54 @@ let group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs = ) accs todo' ) todo (AS.empty ()) in - let accs' = AS.diff accs todo in - let ancestor_accs' = AS.diff ancestor_accs todo in - let ancestor_outer_accs' = AS.diff ancestor_outer_accs todo in - let outer_accs' = AS.diff outer_accs todo in - let todo_accs = may_race_accs ~accs:accs' ~todo in - let accs_todo = AS.inter todo accs in - let todo_ancestor_accs = may_race_accs ~accs:ancestor_accs' ~todo:accs_todo in - let todo_ancestor_outer_accs = may_race_accs ~accs:ancestor_outer_accs' ~todo:accs_todo in - let todo_outer_accs = may_race_accs ~accs:outer_accs' ~todo:accs_todo in - let todo_ancestor_accs_cross = may_race_accs ~accs:ancestor_accs' ~todo:(AS.inter todo outer_accs) in - let todo_outer_accs_cross = may_race_accs ~accs:outer_accs' ~todo:(AS.inter todo ancestor_accs) in - let todos = [todo_accs; todo_ancestor_accs; todo_ancestor_outer_accs; todo_outer_accs; todo_ancestor_accs_cross; todo_outer_accs_cross] in - let todo' = List.reduce AS.union todos in - let visited' = AS.union visited todo in + let node' = AS.diff node todo in + let prefix' = AS.diff prefix todo in + let type_suffix' = AS.diff type_suffix todo in + let type_suffix_prefix' = AS.diff type_suffix_prefix todo in + let todo_node = AS.inter todo node in + let todo_node' = may_race_accs ~accs:node' ~todo in + let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo_node (AS.inter todo type_suffix)) in + let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo_node (AS.inter todo prefix)) in + let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo_node in + let todo' = List.reduce AS.union [todo_node'; todo_prefix'; todo_type_suffix_prefix'; todo_type_suffix'] in + let visited' = AS.union visited todo in (* TODO: use warn_accs record for todo *) + let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in if AS.is_empty todo' then - (accs', ancestor_accs', ancestor_outer_accs', outer_accs', visited') + (warn_accs', visited') else - (bfs' [@tailcall]) ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' ~todo:todo' ~visited:visited' + (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in - let bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc = bfs' ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs warn_accs acc = bfs' warn_accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs = - if AS.is_empty accs then - (comps, ancestor_accs, outer_accs) + let rec components comps warn_accs = + if AS.is_empty warn_accs.node then + (comps, warn_accs) else ( - let acc = AS.choose accs in - let (accs', ancestor_accs', ancestor_outer_accs', outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs acc in + let acc = AS.choose warn_accs.node in + let (warn_accs', comp) = bfs warn_accs acc in let comps' = comp :: comps in - components comps' ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' ~outer_accs:outer_accs' ~accs:accs' + components comps' warn_accs' ) in - let (comps, ancestor_accs, outer_accs) = components [] ~ancestor_accs ~ancestor_outer_accs ~outer_accs ~accs in - if M.tracing then M.trace "access" "components\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs AS.pretty outer_accs; - let rec components_cross comps ~ancestor_accs ~outer_accs = - if AS.is_empty ancestor_accs then + let (comps, warn_accs) = components [] warn_accs in + if M.tracing then M.trace "access" "components\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; + let rec components_cross comps ~prefix ~type_suffix = + if AS.is_empty prefix then comps else ( - let ancestor_acc = AS.choose ancestor_accs in - let (_, ancestor_accs', _, outer_accs', comp) = bfs ~ancestor_accs ~ancestor_outer_accs:(AS.empty ()) ~outer_accs ~accs:(AS.empty ()) ancestor_acc in - if M.tracing then M.trace "access" "components_cross\n\tancestors_accs: %a\n\touter_accs: %a\n" AS.pretty ancestor_accs' AS.pretty outer_accs'; + let prefix_acc = AS.choose prefix in + let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} prefix_acc in + if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; let comps' = if AS.cardinal comp > 1 then comp :: comps else - comps (* ignore self-race ancestor_acc component, self-race checked at ancestor's level *) + comps (* ignore self-race prefix_acc component, self-race checked at prefix's level *) in - components_cross comps' ~ancestor_accs:ancestor_accs' ~outer_accs:outer_accs' + components_cross comps' ~prefix:warn_accs'.prefix ~type_suffix:warn_accs'.type_suffix ) in - let components_cross = components_cross comps ~ancestor_accs ~outer_accs in + let components_cross = components_cross comps ~prefix:warn_accs.prefix ~type_suffix:warn_accs.type_suffix in if M.tracing then M.traceu "access" "group_may_race\n"; components_cross @@ -507,7 +512,7 @@ let print_accesses memo grouped_accs = M.msg_group Success ~category:Race "Memory location %a (safe)" Memo.pretty memo (msgs safe_accs) ) -let warn_global ~safe ~vulnerable ~unsafe ~ancestor_accs ~ancestor_outer_accs ~outer_accs memo accs = - let grouped_accs = group_may_race ~ancestor_accs ~ancestor_outer_accs ~outer_accs accs in (* do expensive component finding only once *) +let warn_global ~safe ~vulnerable ~unsafe warn_accs memo = + let grouped_accs = group_may_race warn_accs in (* do expensive component finding only once *) incr_summary ~safe ~vulnerable ~unsafe memo grouped_accs; print_accesses memo grouped_accs From 49e529f627e9fc1ae8fe9f4fbd6b31a065fc6cbf Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 10:55:09 +0300 Subject: [PATCH 102/238] Refactor distribute_inner in raceAnalysis --- src/analyses/raceAnalysis.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index bc0709541d..2cf97afffd 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -111,7 +111,7 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo - let outer_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None @@ -125,12 +125,12 @@ struct | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let outer_accs = - match outer_memo (root, offset) with - | Some outer_memo -> distribute_outer ctx outer_memo + let type_suffix = + match type_suffix_memo (root, offset) with + | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo | None -> Access.AS.empty () in - Access.AS.union accs outer_accs + Access.AS.union accs type_suffix let query ctx (type a) (q: a Queries.t): a Queries.result = match q with @@ -141,29 +141,29 @@ struct (* ignore (Pretty.printf "WarnGlobal %a\n" CilType.Varinfo.pretty g); *) let trie = G.access (ctx.global g) in (** Distribute access to contained fields. *) - let rec distribute_inner offset (accs, children) ~ancestor_accs ~ancestor_outer_accs = + let rec distribute_inner offset (accs, children) ~prefix ~type_suffix_prefix = let accs = match accs with | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let outer_accs = - match outer_memo (g', offset) with - | Some outer_memo -> distribute_outer ctx outer_memo + let type_suffix = + match type_suffix_memo (g', offset) with + | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo | None -> Access.AS.empty () in - if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty ancestor_accs) && not (Access.AS.is_empty outer_accs)) then ( + if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix=ancestor_accs; type_suffix_prefix=ancestor_outer_accs; type_suffix=outer_accs; node=accs}) memo + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix; type_suffix_prefix; type_suffix; node=accs}) memo ); - let ancestor_outer_accs' = Access.AS.union ancestor_outer_accs outer_accs in - let ancestor_accs' = Access.AS.union ancestor_accs accs in + let prefix' = Access.AS.union prefix accs in + let type_suffix_prefix' = Access.AS.union type_suffix_prefix type_suffix in OffsetTrie.ChildMap.iter (fun child_key child_trie -> - distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~ancestor_accs:ancestor_accs' ~ancestor_outer_accs:ancestor_outer_accs' + distribute_inner (Offset.Unit.add_offset offset (OneOffset.to_offset child_key)) child_trie ~prefix:prefix' ~type_suffix_prefix:type_suffix_prefix' ) children; in - distribute_inner `NoOffset trie ~ancestor_accs:(Access.AS.empty ()) ~ancestor_outer_accs:(Access.AS.empty ()) + distribute_inner `NoOffset trie ~prefix:(Access.AS.empty ()) ~type_suffix_prefix:(Access.AS.empty ()) | `Right _ -> (* vars *) () end From 64c761bee01954873ce85148d9cb1b818f0d238f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 11:29:24 +0300 Subject: [PATCH 103/238] Use warn_accs for todo Co-authored-by: Simmo Saan --- src/domains/access.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 918573835f..85626c4463 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -399,31 +399,31 @@ let group_may_race warn_accs = ) accs todo' ) todo (AS.empty ()) in - let node' = AS.diff node todo in - let prefix' = AS.diff prefix todo in - let type_suffix' = AS.diff type_suffix todo in - let type_suffix_prefix' = AS.diff type_suffix_prefix todo in - let todo_node = AS.inter todo node in - let todo_node' = may_race_accs ~accs:node' ~todo in - let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo_node (AS.inter todo type_suffix)) in - let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo_node (AS.inter todo prefix)) in - let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo_node in - let todo' = List.reduce AS.union [todo_node'; todo_prefix'; todo_type_suffix_prefix'; todo_type_suffix'] in - let visited' = AS.union visited todo in (* TODO: use warn_accs record for todo *) + let node' = AS.diff node todo.node in + let prefix' = AS.diff prefix todo.prefix in + let type_suffix' = AS.diff type_suffix todo.type_suffix in + let type_suffix_prefix' = AS.diff type_suffix_prefix todo.type_suffix_prefix in + let todo_all = AS.union todo.node (AS.union (AS.union todo.prefix todo.type_suffix) todo.type_suffix_prefix) in + let todo_node' = may_race_accs ~accs:node' ~todo:todo_all in + let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo.node todo.type_suffix) in + let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo.node todo.prefix) in + let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo.node in + let todo' = {prefix=todo_prefix'; type_suffix=todo_type_suffix'; type_suffix_prefix=todo_type_suffix_prefix'; node=todo_node'} in + let visited' = AS.union visited todo_all in let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in - if AS.is_empty todo' then + if AS.is_empty todo'.node && AS.is_empty todo'.prefix && AS.is_empty todo'.type_suffix && AS.is_empty todo'.type_suffix_prefix then (warn_accs', visited') else (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in - let bfs warn_accs acc = bfs' warn_accs ~todo:(AS.singleton acc) ~visited:(AS.empty ()) in + let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in (* repeat BFS to find all components *) let rec components comps warn_accs = if AS.is_empty warn_accs.node then (comps, warn_accs) else ( let acc = AS.choose warn_accs.node in - let (warn_accs', comp) = bfs warn_accs acc in + let (warn_accs', comp) = bfs warn_accs {node=AS.singleton acc; prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in let comps' = comp :: comps in components comps' warn_accs' ) @@ -435,7 +435,7 @@ let group_may_race warn_accs = comps else ( let prefix_acc = AS.choose prefix in - let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} prefix_acc in + let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} {node=AS.empty (); prefix=AS.singleton prefix_acc; type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; let comps' = if AS.cardinal comp > 1 then From b06b3e342981f8dfda512b58c1e1049053332321 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 11:48:23 +0300 Subject: [PATCH 104/238] Make warn_accs record to a module Co-authored-by: Simmo Saan --- src/domains/access.ml | 77 ++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 85626c4463..4c752096ac 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -378,17 +378,42 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true -type warn_accs = { - node: AS.t; - prefix: AS.t; - type_suffix: AS.t; - type_suffix_prefix: AS.t; -} - -let group_may_race warn_accs = - if M.tracing then M.tracei "access" "group_may_race\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; +module WarnAccs = +struct + type t = { + node: AS.t; + prefix: AS.t; + type_suffix: AS.t; + type_suffix_prefix: AS.t; + } + + let diff w1 w2 = { + node = AS.diff w1.node w2.node; + prefix = AS.diff w1.prefix w2.prefix; + type_suffix = AS.diff w1.type_suffix w2.type_suffix; + type_suffix_prefix = AS.diff w1.type_suffix_prefix w2.type_suffix_prefix; + } + + let union_all w = + AS.union + (AS.union w.node w.prefix) + (AS.union w.type_suffix w.type_suffix_prefix) + + let is_empty w = + AS.is_empty w.node && AS.is_empty w.prefix && AS.is_empty w.type_suffix && AS.is_empty w.type_suffix_prefix + + let empty () = + {node=AS.empty (); prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} + + let pretty () w = + Pretty.dprintf "{node = %a; prefix = %a; type_suffix = %a; type_suffix_prefix = %a}" + AS.pretty w.node AS.pretty w.prefix AS.pretty w.type_suffix AS.pretty w.type_suffix_prefix +end + +let group_may_race (warn_accs:WarnAccs.t) = + if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) - let rec bfs' {prefix; type_suffix_prefix; type_suffix; node} ~todo ~visited = + let rec bfs' warn_accs ~todo ~visited = let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> @@ -399,44 +424,42 @@ let group_may_race warn_accs = ) accs todo' ) todo (AS.empty ()) in - let node' = AS.diff node todo.node in - let prefix' = AS.diff prefix todo.prefix in - let type_suffix' = AS.diff type_suffix todo.type_suffix in - let type_suffix_prefix' = AS.diff type_suffix_prefix todo.type_suffix_prefix in - let todo_all = AS.union todo.node (AS.union (AS.union todo.prefix todo.type_suffix) todo.type_suffix_prefix) in - let todo_node' = may_race_accs ~accs:node' ~todo:todo_all in - let todo_prefix' = may_race_accs ~accs:prefix' ~todo:(AS.union todo.node todo.type_suffix) in - let todo_type_suffix' = may_race_accs ~accs:type_suffix' ~todo:(AS.union todo.node todo.prefix) in - let todo_type_suffix_prefix' = may_race_accs ~accs:type_suffix_prefix' ~todo:todo.node in - let todo' = {prefix=todo_prefix'; type_suffix=todo_type_suffix'; type_suffix_prefix=todo_type_suffix_prefix'; node=todo_node'} in + let warn_accs' = WarnAccs.diff warn_accs todo in + let todo_all = WarnAccs.union_all todo in let visited' = AS.union visited todo_all in - let warn_accs' = {prefix=prefix'; type_suffix_prefix=type_suffix_prefix'; type_suffix=type_suffix'; node=node'} in - if AS.is_empty todo'.node && AS.is_empty todo'.prefix && AS.is_empty todo'.type_suffix && AS.is_empty todo'.type_suffix_prefix then + let todo' : WarnAccs.t = { + node = may_race_accs ~accs:warn_accs'.node ~todo:todo_all; + prefix = may_race_accs ~accs:warn_accs'.prefix ~todo:(AS.union todo.node todo.type_suffix); + type_suffix = may_race_accs ~accs:warn_accs'.type_suffix ~todo:(AS.union todo.node todo.prefix); + type_suffix_prefix = may_race_accs ~accs:warn_accs'.type_suffix_prefix ~todo:todo.node + } + in + if WarnAccs.is_empty todo' then (warn_accs', visited') else (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in (* repeat BFS to find all components *) - let rec components comps warn_accs = + let rec components comps (warn_accs:WarnAccs.t) = if AS.is_empty warn_accs.node then (comps, warn_accs) else ( let acc = AS.choose warn_accs.node in - let (warn_accs', comp) = bfs warn_accs {node=AS.singleton acc; prefix=AS.empty (); type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in + let (warn_accs', comp) = bfs warn_accs {(WarnAccs.empty ()) with node=AS.singleton acc} in let comps' = comp :: comps in components comps' warn_accs' ) in let (comps, warn_accs) = components [] warn_accs in - if M.tracing then M.trace "access" "components\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs.prefix AS.pretty warn_accs.type_suffix; + if M.tracing then M.trace "access" "components %a\n" WarnAccs.pretty warn_accs; let rec components_cross comps ~prefix ~type_suffix = if AS.is_empty prefix then comps else ( let prefix_acc = AS.choose prefix in - let (warn_accs', comp) = bfs {prefix; type_suffix_prefix=(AS.empty ()); type_suffix; node=(AS.empty ())} {node=AS.empty (); prefix=AS.singleton prefix_acc; type_suffix=AS.empty (); type_suffix_prefix=AS.empty ()} in - if M.tracing then M.trace "access" "components_cross\n\tprefix: %a\n\ttype_suffix: %a\n" AS.pretty warn_accs'.prefix AS.pretty warn_accs'.type_suffix; + let (warn_accs', comp) = bfs {(WarnAccs.empty ()) with prefix; type_suffix} {(WarnAccs.empty ()) with prefix=AS.singleton prefix_acc} in + if M.tracing then M.trace "access" "components_cross %a\n" WarnAccs.pretty warn_accs'; let comps' = if AS.cardinal comp > 1 then comp :: comps From 69ddfdffab00cf5c3699a9490d86d64800b990c3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 12:13:20 +0300 Subject: [PATCH 105/238] Refactor and document todo' computation Co-authored-by: Simmo Saan --- src/domains/access.ml | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 4c752096ac..6a802e5c14 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -414,7 +414,11 @@ let group_may_race (warn_accs:WarnAccs.t) = if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' warn_accs ~todo ~visited = - let may_race_accs ~accs ~todo = (* TODO: rename to from-to *) + let warn_accs' = WarnAccs.diff warn_accs todo in + let todo_all = WarnAccs.union_all todo in + let visited' = AS.union visited todo_all in + + let step_may_race ~todo ~accs = (* step from todo to accs if may_race *) AS.fold (fun acc todo' -> AS.fold (fun acc' todo' -> if may_race acc acc' then @@ -424,16 +428,28 @@ let group_may_race (warn_accs:WarnAccs.t) = ) accs todo' ) todo (AS.empty ()) in - let warn_accs' = WarnAccs.diff warn_accs todo in - let todo_all = WarnAccs.union_all todo in - let visited' = AS.union visited todo_all in + (* Undirected graph of may_race checks: + + type_suffix_prefix + | + | + type_suffix --+-- prefix + \ | / + \ | / + node + / \ + \_/ + + Each undirected edge is handled by two opposite step_may_race-s. + All missing edges are checked at other nodes by other group_may_race calls. *) let todo' : WarnAccs.t = { - node = may_race_accs ~accs:warn_accs'.node ~todo:todo_all; - prefix = may_race_accs ~accs:warn_accs'.prefix ~todo:(AS.union todo.node todo.type_suffix); - type_suffix = may_race_accs ~accs:warn_accs'.type_suffix ~todo:(AS.union todo.node todo.prefix); - type_suffix_prefix = may_race_accs ~accs:warn_accs'.type_suffix_prefix ~todo:todo.node + node = step_may_race ~todo:todo_all ~accs:warn_accs'.node; + prefix = step_may_race ~todo:(AS.union todo.node todo.type_suffix) ~accs:warn_accs'.prefix; + type_suffix = step_may_race ~todo:(AS.union todo.node todo.prefix) ~accs:warn_accs'.type_suffix; + type_suffix_prefix = step_may_race ~todo:todo.node ~accs:warn_accs'.type_suffix_prefix } in + if WarnAccs.is_empty todo' then (warn_accs', visited') else From 857d1cbaa31673ced488acbf42b97f9e3c3413aa Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 12:36:18 +0300 Subject: [PATCH 106/238] Use Typsig for Access MemoRoot Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 6 +++--- src/domains/access.ml | 29 +++++++++-------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 2cf97afffd..72d73f9de1 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -113,9 +113,9 @@ struct let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with - | `Var v, _ -> Some (`Type v.vtype, offset) (* TODO: Alloc variables void type *) + | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None - | _, `Field (f, offset') -> Some (`Type f.ftype, offset') + | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') | _, `Index ((), offset') -> None (* TODO *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = @@ -190,7 +190,7 @@ struct in let add_access_struct conf ci = let a = part_access None in - Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TComp (ci, [])), `NoOffset) + Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls diff --git a/src/domains/access.ml b/src/domains/access.ml index 6a802e5c14..ce71fcc98c 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -83,8 +83,7 @@ type acc_typ = [ `Type of CilType.Typ.t | `Struct of CilType.Compinfo.t * Offset module MemoRoot = struct include Printable.StdLeaf - type t = [`Var of CilType.Varinfo.t | `Type of CilType.Typ.t] [@@deriving eq, ord, hash] - (* Can't use typsig for `Type because there's no function to follow offsets on typsig. *) + type t = [`Var of CilType.Varinfo.t | `Type of CilType.Typsig.t] [@@deriving eq, ord, hash] let name () = "memoroot" @@ -92,8 +91,8 @@ struct (* Imitate old printing for now *) match vt with | `Var v -> Pretty.dprintf "%a@@%a" CilType.Varinfo.pretty v CilType.Location.pretty v.vdecl - | `Type (TComp (c, _)) -> Pretty.dprintf "(struct %s)" c.cname - | `Type t -> Pretty.dprintf "(%a)" CilType.Typ.pretty t + | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name + | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t include Printable.SimplePretty ( struct @@ -108,7 +107,6 @@ module Memo = struct include Printable.StdLeaf type t = MemoRoot.t * Offset.Unit.t [@@deriving eq, ord, hash] - (* Can't use typsig for `Type because there's no function to follow offsets on typsig. *) let name () = "memo" @@ -116,8 +114,8 @@ struct (* Imitate old printing for now *) match vt with | `Var v -> Pretty.dprintf "%a%a@@%a" CilType.Varinfo.pretty v Offset.Unit.pretty o CilType.Location.pretty v.vdecl - | `Type (TComp (c, _)) -> Pretty.dprintf "(struct %s)%a" c.cname Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typ.pretty t Offset.Unit.pretty o + | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o + | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o include Printable.SimplePretty ( struct @@ -128,23 +126,14 @@ struct let of_ty (ty: acc_typ): t = match ty with - | `Struct (c, o) -> (`Type (TComp (c, [])), o) - | `Type t -> (`Type t, `NoOffset) + | `Struct (c, o) -> (`Type (TSComp (c.cstruct, c.cname, [])), o) + | `Type t -> (`Type (Cil.typeSig t), `NoOffset) let to_mval: t -> Mval.Unit.t option = function | (`Var v, o) -> Some (v, o) | (`Type _, _) -> None let add_offset ((vt, o): t) o2: t = (vt, Offset.Unit.add_offset o o2) - - let type_of_base ((vt, _): t): typ = - match vt with - | `Var v -> v.vtype - | `Type t -> t - - (** @raise Offset.Type_of_error *) - let type_of ((vt, o) as memo: t): typ = - Offset.Unit.type_of ~base:(type_of_base memo) o end (* TODO: What is the logic for get_type? *) @@ -213,12 +202,12 @@ let add_one side memo: unit = (** Distribute type-based access to variables and containing fields. *) let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = - let memo = (`Type t, o) in + let ts = typeSig t in + let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; add_one side memo; (* distribute to variables of the type *) - let ts = typeSig t in let vars = TSH.find_all typeVar ts in List.iter (fun v -> add_one side0 (`Var v, o) (* same offset, but on variable *) From b28cbba1abb527b6d1768bbd1bd584348d49d2e3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 13:32:38 +0300 Subject: [PATCH 107/238] Fix index in type_suffix_memo Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 72d73f9de1..f8705613c8 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -116,7 +116,8 @@ struct | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) | _, `NoOffset -> None | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') - | _, `Index ((), offset') -> None (* TODO *) + | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') + | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in From d6c5bbac05531ec6f52b2f45d419f841a61793cb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 13:58:54 +0300 Subject: [PATCH 108/238] Make new cram tests deterministic Co-authored-by: Simmo Saan --- .../04-mutex/77-type-nested-fields.t | 38 ++++++++--------- .../04-mutex/79-type-nested-fields-deep1.t | 38 ++++++++--------- .../04-mutex/80-type-nested-fields-deep2.t | 38 ++++++++--------- .../04-mutex/90-distribute-fields-type-1.t | 42 +++++++++---------- .../04-mutex/91-distribute-fields-type-2.t | 42 +++++++++---------- .../04-mutex/92-distribute-fields-type-deep.t | 42 +++++++++---------- .../93-distribute-fields-type-global.t | 32 +++++++------- 7 files changed, 136 insertions(+), 136 deletions(-) diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index 2cbd339dfa..dc87d0a85e 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -1,29 +1,29 @@ - $ goblint --enable allglobs 77-type-nested-fields.c - [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + $ goblint --enable warn.deterministic --enable allglobs 77-type-nested-fields.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 4075dab33b..5fc60223b9 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -1,24 +1,6 @@ - $ goblint --enable allglobs 79-type-nested-fields-deep1.c - [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + $ goblint --enable warn.deterministic --enable allglobs 79-type-nested-fields-deep1.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) @@ -27,3 +9,21 @@ vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index f14a315de2..a0541235ee 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -1,24 +1,6 @@ - $ goblint --enable allglobs 80-type-nested-fields-deep2.c - [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + $ goblint --enable warn.deterministic --enable allglobs 80-type-nested-fields-deep2.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) - [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) @@ -27,3 +9,21 @@ vulnerable: 0 unsafe: 1 total memory locations: 2 + [Success][Race] Memory location (struct T).s.field (safe): + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index 65689ff4d4..fb6af7ceca 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -1,31 +1,31 @@ - $ goblint --enable allglobs 90-distribute-fields-type-1.c - [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + $ goblint --enable warn.deterministic --enable allglobs 90-distribute-fields-type-1.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + [Success][Race] Memory location (struct T).s (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index be365577f2..0b4ec982e8 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -1,31 +1,31 @@ - $ goblint --enable allglobs 91-distribute-fields-type-2.c - [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + $ goblint --enable warn.deterministic --enable allglobs 91-distribute-fields-type-2.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) - [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) - [Success][Race] Memory location (struct S) (safe): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S) (safe): + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + [Success][Race] Memory location (struct T) (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index c0f3beae2c..aedc66f101 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -1,26 +1,6 @@ - $ goblint --enable allglobs 92-distribute-fields-type-deep.c - [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + $ goblint --enable warn.deterministic --enable allglobs 92-distribute-fields-type-deep.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) - [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) @@ -29,3 +9,23 @@ vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + [Success][Race] Memory location (struct U).t (safe): + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index 3761e9f7b4..fcc3e804c7 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,25 +1,25 @@ - $ goblint --enable allglobs 93-distribute-fields-type-global.c - [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + $ goblint --enable warn.deterministic --enable allglobs 93-distribute-fields-type-global.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 7 - dead: 0 - total lines: 7 - [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:9:10-9:11) read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) - [Success][Race] Memory location (struct S).field (safe): - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 + [Success][Race] Memory location (struct S).field (safe): + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) + [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) From 21332cc346d78e84f5b51d24800e005971db2a08 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 15 Aug 2023 14:17:18 +0300 Subject: [PATCH 109/238] Refactor distribute_outer Co-authored-by: Simmo Saan --- src/analyses/raceAnalysis.ml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 93c2f0994f..5f100c869a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -119,20 +119,21 @@ struct | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) - let rec distribute_outer ctx ((root, offset) : Access.Memo.t) : Access.AS.t = + let rec find_type_suffix' ctx ((root, offset) as memo : Access.Memo.t) : Access.AS.t = let trie = G.access (ctx.global (V.access root)) in let accs = match OffsetTrie.find offset trie with | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = - match type_suffix_memo (root, offset) with - | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo - | None -> Access.AS.empty () - in + let type_suffix = find_type_suffix ctx memo in Access.AS.union accs type_suffix + and find_type_suffix ctx (memo : Access.Memo.t) : Access.AS.t = + match type_suffix_memo memo with + | Some type_suffix_memo -> find_type_suffix' ctx type_suffix_memo + | None -> Access.AS.empty () + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> @@ -148,11 +149,7 @@ struct | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = - match type_suffix_memo (g', offset) with - | Some type_suffix_memo -> distribute_outer ctx type_suffix_memo - | None -> Access.AS.empty () - in + let type_suffix = find_type_suffix ctx (g', offset) in if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in From e3e322a7ee08a8ab8424329a746355b9d59f46dc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 16:08:41 +0300 Subject: [PATCH 110/238] Clean up and comment lazy outer distribution changes --- src/analyses/raceAnalysis.ml | 27 ++++++++++++++--------- src/domains/access.ml | 42 ++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 5f100c869a..3265bc6979 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -12,7 +12,7 @@ struct let name () = "race" (* Two global invariants: - 1. memoroot -> (offset -> accesses) -- used for warnings + 1. memoroot -> (offset --trie--> accesses) -- used for warnings 2. varinfo -> set of memo -- used for IterSysVars Global *) module V = @@ -52,6 +52,9 @@ struct module OffsetTrie = struct + (* LiftBot such that add_distribute_outer can side-effect empty set to indicate + all offsets that exist for prefix-type_suffix race checking. + Otherwise, there are no trie nodes to traverse to where this check must happen. *) include TrieDomain.Make (OneOffset) (Lattice.LiftBot (Access.AS)) let rec find (offset : Offset.Unit.t) ((accs, children) : t) : value = @@ -105,18 +108,19 @@ struct ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); side_vars ctx memo - let side_access0 ctx ((memoroot, offset) as memo) = - (* ignore (Pretty.printf "memo: %a\n" Access.Memo.pretty memo); *) + (** Side-effect empty access set for prefix-type_suffix race checking. *) + let side_access_empty ctx ((memoroot, offset) as memo) = if !AnalysisState.should_warn then ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); side_vars ctx memo + (** Get immediate type_suffix memo. *) let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = match root, offset with - | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* TODO: Alloc variables void type *) - | _, `NoOffset -> None - | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') - | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') + | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) + | _, `NoOffset -> None (* primitive type *) + | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) + | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) let rec find_type_suffix' ctx ((root, offset) as memo : Access.Memo.t) : Access.AS.t = @@ -129,6 +133,7 @@ struct let type_suffix = find_type_suffix ctx memo in Access.AS.union accs type_suffix + (** Find accesses from all type_suffixes transitively. *) and find_type_suffix ctx (memo : Access.Memo.t) : Access.AS.t = match type_suffix_memo memo with | Some type_suffix_memo -> find_type_suffix' ctx type_suffix_memo @@ -153,8 +158,10 @@ struct if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in - Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {prefix; type_suffix_prefix; type_suffix; node=accs}) memo + Timing.wrap ~args:[("memory location", `String mem_loc_str)] "race" (Access.warn_global ~safe ~vulnerable ~unsafe {node=accs; prefix; type_suffix; type_suffix_prefix}) memo ); + + (* Recurse to children. *) let prefix' = Access.AS.union prefix accs in let type_suffix_prefix' = Access.AS.union type_suffix_prefix type_suffix in OffsetTrie.ChildMap.iter (fun child_key child_trie -> @@ -184,11 +191,11 @@ struct let loc = Option.get !Node.current_node in let add_access conf voffs = let a = part_access (Option.map fst voffs) in - Access.add (side_access octx (conf, kind, loc, e, a)) (side_access0 octx) e voffs; + Access.add ~side:(side_access octx (conf, kind, loc, e, a)) ~side_empty:(side_access_empty octx) e voffs; in let add_access_struct conf ci = let a = part_access None in - Access.add_one (side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) + Access.add_one ~side:(side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls diff --git a/src/domains/access.ml b/src/domains/access.ml index 457063950d..07d1632529 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -93,7 +93,7 @@ struct match vt with | `Var v -> CilType.Varinfo.pretty () v | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name - | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t + | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t (* TODO: fix TSBase printing *) include Printable.SimplePretty ( struct @@ -116,7 +116,7 @@ struct match vt with | `Var v -> Pretty.dprintf "%a%a" CilType.Varinfo.pretty v Offset.Unit.pretty o | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o + | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o (* TODO: fix TSBase printing *) include Printable.SimplePretty ( struct @@ -194,42 +194,44 @@ let get_val_type e: acc_typ = (** Add access to {!Memo} after distributing. *) -let add_one side memo: unit = +let add_one ~side memo: unit = let mv = Memo.to_mval memo in let ignorable = is_ignorable mv in if M.tracing then M.trace "access" "add_one %a (ignorable = %B)\n" Memo.pretty memo ignorable; if not ignorable then side memo -(** Distribute type-based access to variables and containing fields. *) -let rec add_distribute_outer side side0 (t: typ) (o: Offset.Unit.t) = +(** Distribute empty access set for type-based access to variables and containing fields. + Empty access sets are needed for prefix-type_suffix race checking. *) +let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = let ts = typeSig t in let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; - add_one side memo; + add_one ~side memo; (* Add actual access for non-recursive call, or empty access for recursive call when side is side_empty. *) (* distribute to variables of the type *) let vars = TSH.find_all typeVar ts in List.iter (fun v -> - add_one side0 (`Var v, o) (* same offset, but on variable *) + (* same offset, but on variable *) + add_one ~side:side_empty (`Var v, o) (* Switch to side_empty. *) ) vars; (* recursively distribute to fields containing the type *) let fields = TSH.find_all typeIncl ts in List.iter (fun f -> (* prepend field and distribute to outer struct *) - add_distribute_outer side0 side0 (TComp (f.fcomp, [])) (`Field (f, o)) + add_distribute_outer ~side:side_empty ~side_empty (TComp (f.fcomp, [])) (`Field (f, o)) (* Switch to side_empty. *) ) fields; if M.tracing then M.traceu "access" "add_distribute_outer\n" (** Add access to known variable with offsets or unknown variable from expression. *) -let add side side0 e voffs = +let add ~side ~side_empty e voffs = begin match voffs with | Some (v, o) -> (* known variable *) if M.tracing then M.traceli "access" "add var %a%a\n" CilType.Varinfo.pretty v CilType.Offset.pretty o; let memo = (`Var v, Offset.Unit.of_cil o) in - add_one side memo + add_one ~side memo | None -> (* unknown variable *) if M.tracing then M.traceli "access" "add type %a\n" CilType.Exp.pretty e; let ty = get_val_type e in (* extract old acc_typ from expression *) @@ -239,7 +241,7 @@ let add side side0 e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer side side0 t o (* distribute to variables and outer offsets *) + | _ -> add_distribute_outer ~side ~side_empty t o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" @@ -368,13 +370,14 @@ let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),lo else true +(** Access sets for race detection and warnings. *) module WarnAccs = struct type t = { - node: AS.t; - prefix: AS.t; - type_suffix: AS.t; - type_suffix_prefix: AS.t; + node: AS.t; (** Accesses for current memo. From accesses at trie node corresponding to memo offset. *) + prefix: AS.t; (** Accesses for all prefixes. From accesses to trie node ancestors. *) + type_suffix: AS.t; (** Accesses for all type suffixes. From offset suffixes in other tries. *) + type_suffix_prefix: AS.t; (** Accesses to all prefixes of all type suffixes. *) } let diff w1 w2 = { @@ -404,9 +407,9 @@ let group_may_race (warn_accs:WarnAccs.t) = if M.tracing then M.tracei "access" "group_may_race %a\n" WarnAccs.pretty warn_accs; (* BFS to traverse one component with may_race edges *) let rec bfs' warn_accs ~todo ~visited = - let warn_accs' = WarnAccs.diff warn_accs todo in let todo_all = WarnAccs.union_all todo in - let visited' = AS.union visited todo_all in + let visited' = AS.union visited todo_all in (* Add all todo accesses to component. *) + let warn_accs' = WarnAccs.diff warn_accs todo in (* Todo accesses don't need to be considered as step targets, because they're already in the component. *) let step_may_race ~todo ~accs = (* step from todo to accs if may_race *) AS.fold (fun acc todo' -> @@ -437,7 +440,7 @@ let group_may_race (warn_accs:WarnAccs.t) = prefix = step_may_race ~todo:(AS.union todo.node todo.type_suffix) ~accs:warn_accs'.prefix; type_suffix = step_may_race ~todo:(AS.union todo.node todo.prefix) ~accs:warn_accs'.type_suffix; type_suffix_prefix = step_may_race ~todo:todo.node ~accs:warn_accs'.type_suffix_prefix - } + } in if WarnAccs.is_empty todo' then @@ -446,7 +449,7 @@ let group_may_race (warn_accs:WarnAccs.t) = (bfs' [@tailcall]) warn_accs' ~todo:todo' ~visited:visited' in let bfs warn_accs todo = bfs' warn_accs ~todo ~visited:(AS.empty ()) in - (* repeat BFS to find all components *) + (* repeat BFS to find all components starting from node accesses *) let rec components comps (warn_accs:WarnAccs.t) = if AS.is_empty warn_accs.node then (comps, warn_accs) @@ -459,6 +462,7 @@ let group_may_race (warn_accs:WarnAccs.t) = in let (comps, warn_accs) = components [] warn_accs in if M.tracing then M.trace "access" "components %a\n" WarnAccs.pretty warn_accs; + (* repeat BFS to find all prefix-type_suffix-only components starting from prefix accesses (symmetric) *) let rec components_cross comps ~prefix ~type_suffix = if AS.is_empty prefix then comps From 81dac32fd56807e6695197c36e4c375eb073962a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 16:58:39 +0300 Subject: [PATCH 111/238] Document new race analysis --- src/analyses/raceAnalysis.ml | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 3265bc6979..f617df2048 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -3,6 +3,85 @@ open GoblintCil open Analyses +(** Data race analysis with tries and hookers. + + Accesses are to memory locations ({{!Access.Memo} memos}) which consist of a root and offset. + {{!Access.MemoRoot} Root} can be: + + variable, if access is to known global variable or alloc-variable; + + type, if access is to unknown pointer. + + Accesses are (now) collected to sets for each corresponding memo, + after points-to sets are resolved, during postsolving. + + Race checking is performed per-memo, + except must additionally account for accesses to other memos (see diagram below): + + access to [s.f] can race with access to a prefix like [s], which writes an entire struct at once; + + access to [s.f] can race with type-based access like [(struct S).f]; + + access to [(struct S).f] can race with type-based access to a suffix like [(int)]. + + access to [(struct T).s.f] can race with type-based access like [(struct S)], which is a combination of the above. + + These are accounted for lazily (unlike in the past). + + Prefixes (a.k.a. inner distribution) are handled using a trie data structure enriched with lattice properties. + Race checking starts at the root and passes accesses to ancestor nodes down to children. + + Type suffixes (a.k.a. outer distribution) are handled by computing successive immediate type suffixes transitively + and accessing corresponding offsets from corresponding root tries in the global invariant. + + Type suffix prefixes (for the combination of the two) are handled by passing type suffix accesses down when traversing the prefix trie. + + Race checking happens at each trie node with the above three access sets at hand using {!Access.group_may_race}. + All necessary combinations between the four classes are handled, but unnecessary repeated work is carefully avoided. + E.g. accesses which are pairwise checked at some prefix are not re-checked pairwise at a node. + Thus, races (with prefixes or type suffixes) are reported for most precise memos with actual accesses: + at the longest prefix and longest type suffix. + + Additionally, accesses between prefix and type suffix intersecting at a node are checked. + These races are reported at the unique memo at the intersection of the prefix and the type suffix. + This requires an implementation hack to still eagerly do outer distribution, but only of empty access sets. + It ensures that corresponding trie nodes exist for traversal later. *) + +(** Example structure of related memos for race checking: + {v + (int) (S) (T) + \ / \ / \ + f s t + \ / \ / + f s + \ / + f + v} + where: + - [(int)] is a type-based memo root for the primitive [int] type; + - [(S)] and [(T)] are short for [(struct S)] and [(struct T)], which are type-based memo roots; + - [f] is a field of [S] and [s] is a field of [T]; + - [t] is a global variable of type [T]. + - prefix relations are indicated by [/]; + - type suffix relations are indicated by [\ ]. + + Prefix races: + - Race between [t.s.f] and [t.s] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [t] is checked/reported at [t.s.f]. + - Race between [t.s] and [t] is checked/reported at [t.s]. + - Race between [t] and [t] is checked/reported at [t]. + - Race between [(S).f] and [(S)] is checked/reported at [(S).f]. + + Type suffix races: + - Race between [t.s.f] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(int)] is checked/reported at [t.s.f]. + - Race between [(S).f] and [(int)] is checked/reported at [(S).f]. + + Type suffix prefix races: + - Race between [t.s.f] and [(T).s] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(T)] is checked/reported at [t.s.f]. + - Race between [t.s.f] and [(S)] is checked/reported at [t.s.f]. + - Race between [(T).s.f] and [(S)] is checked/reported at [(T).s.f]. + + Prefix-type suffix races: + - Race between [(T).s] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [t] and [(S).f] is checked/reported at [t.s.f]. *) + (** Data race analyzer without base --- this is the new standard *) module Spec = From 7297241f591bc1719011dda4fe0ef0906ec2cddd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:15:48 +0300 Subject: [PATCH 112/238] Remove --enable ana.race.direct-arithmetic from some tests Irrelevant because no (int) accesses. --- .../regression/04-mutex/49-type-invariants.c | 1 - .../regression/04-mutex/49-type-invariants.t | 48 +++++++++---------- .../04-mutex/77-type-nested-fields.c | 5 +- .../04-mutex/77-type-nested-fields.t | 34 ++++++------- tests/regression/04-mutex/78-type-array.c | 1 - .../04-mutex/79-type-nested-fields-deep1.c | 5 +- .../04-mutex/79-type-nested-fields-deep1.t | 34 ++++++------- .../04-mutex/80-type-nested-fields-deep2.c | 5 +- .../04-mutex/80-type-nested-fields-deep2.t | 34 ++++++------- .../04-mutex/90-distribute-fields-type-1.c | 5 +- .../04-mutex/90-distribute-fields-type-1.t | 36 +++++++------- .../04-mutex/91-distribute-fields-type-2.c | 5 +- .../04-mutex/91-distribute-fields-type-2.t | 36 +++++++------- .../04-mutex/92-distribute-fields-type-deep.c | 5 +- .../04-mutex/92-distribute-fields-type-deep.t | 36 +++++++------- .../93-distribute-fields-type-global.c | 1 - .../93-distribute-fields-type-global.t | 28 +++++------ 17 files changed, 155 insertions(+), 164 deletions(-) diff --git a/tests/regression/04-mutex/49-type-invariants.c b/tests/regression/04-mutex/49-type-invariants.c index 4f69986478..e6ac17dcd9 100644 --- a/tests/regression/04-mutex/49-type-invariants.c +++ b/tests/regression/04-mutex/49-type-invariants.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 3d3f7442ef..3ddd8f237d 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -1,47 +1,47 @@ $ goblint --enable warn.deterministic --enable ana.race.direct-arithmetic --enable allglobs 49-type-invariants.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:22:3-22:21) - [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) - read with [mhp:{tid=[main, t_fun@49-type-invariants.c:21:3-21:40#top]}, thread:[main, t_fun@49-type-invariants.c:21:3-21:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:12:3-12:23) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:21:3-21:21) + [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) + read with [mhp:{tid=[main, t_fun@49-type-invariants.c:20:3-20:40#top]}, thread:[main, t_fun@49-type-invariants.c:20:3-20:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:11:3-11:23) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:22:3-22:21) + [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) $ goblint --enable warn.deterministic --disable ana.race.direct-arithmetic --enable allglobs 49-type-invariants.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:22:3-22:21) - [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) - read with [mhp:{tid=[main, t_fun@49-type-invariants.c:21:3-21:40#top]}, thread:[main, t_fun@49-type-invariants.c:21:3-21:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:12:3-12:23) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (49-type-invariants.c:21:3-21:21) + [Warning][Race] Memory location s.field (race with conf. 110): (49-type-invariants.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) + read with [mhp:{tid=[main, t_fun@49-type-invariants.c:20:3-20:40#top]}, thread:[main, t_fun@49-type-invariants.c:20:3-20:40#top]] (conf. 110) (exp: & s.field) (49-type-invariants.c:11:3-11:23) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:21:3-21:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:22:3-22:21) + write with [mhp:{tid=[main]; created={[main, t_fun@49-type-invariants.c:20:3-20:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->field) (49-type-invariants.c:21:3-21:21) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:22:3-22:21) - [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:22:3-22:21) - [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:22:3-22:21) + [Info][Unsound] Unknown address in {&tmp} has escaped. (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (49-type-invariants.c:21:3-21:21) + [Info][Unsound] Write to unknown address: privatization is unsound. (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (49-type-invariants.c:21:3-21:21) + [Error][Imprecise][Unsound] Function definition missing for getS (49-type-invariants.c:21:3-21:21) diff --git a/tests/regression/04-mutex/77-type-nested-fields.c b/tests/regression/04-mutex/77-type-nested-fields.c index a526defb06..00b21e3fcf 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.c +++ b/tests/regression/04-mutex/77-type-nested-fields.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s t -// \ / \ / +// \ / \ / // >f< s // \ / // f diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index dc87d0a85e..738784f5d5 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 77-type-nested-fields.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:32:3-32:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:39:3-39:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:31:3-31:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (77-type-nested-fields.c:38:3-38:22) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) - write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:39:3-39:22) + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:31:3-31:20) + write with [mhp:{tid=[main]; created={[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s.field) (77-type-nested-fields.c:38:3-38:22) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:32:3-32:20) + write with [mhp:{tid=[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]}, thread:[main, t_fun@77-type-nested-fields.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (77-type-nested-fields.c:31:3-31:20) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:39:3-39:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:39:3-39:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:39:3-39:22) - [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:39:3-39:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:31:3-31:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (77-type-nested-fields.c:38:3-38:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (77-type-nested-fields.c:38:3-38:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (77-type-nested-fields.c:38:3-38:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:31:3-31:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (77-type-nested-fields.c:38:3-38:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (77-type-nested-fields.c:38:3-38:22) + [Error][Imprecise][Unsound] Function definition missing for getS (77-type-nested-fields.c:31:3-31:20) + [Error][Imprecise][Unsound] Function definition missing for getT (77-type-nested-fields.c:38:3-38:22) diff --git a/tests/regression/04-mutex/78-type-array.c b/tests/regression/04-mutex/78-type-array.c index cdffe244b9..835f6163a3 100644 --- a/tests/regression/04-mutex/78-type-array.c +++ b/tests/regression/04-mutex/78-type-array.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.c b/tests/regression/04-mutex/79-type-nested-fields-deep1.c index c38e700829..e100404960 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.c +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s t -// \ / \ / +// \ / \ / // f s // \ / // >f< diff --git a/tests/regression/04-mutex/79-type-nested-fields-deep1.t b/tests/regression/04-mutex/79-type-nested-fields-deep1.t index 5fc60223b9..2e15f83c39 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 79-type-nested-fields-deep1.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:37:3-37:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:36:3-36:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (79-type-nested-fields-deep1.c:43:3-43:24) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) - write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:44:3-44:24) + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:36:3-36:20) + write with [mhp:{tid=[main]; created={[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (79-type-nested-fields-deep1.c:43:3-43:24) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:37:3-37:20) + write with [mhp:{tid=[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]}, thread:[main, t_fun@79-type-nested-fields-deep1.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (79-type-nested-fields-deep1.c:36:3-36:20) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:44:3-44:24) - [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:36:3-36:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (79-type-nested-fields-deep1.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (79-type-nested-fields-deep1.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing for getS (79-type-nested-fields-deep1.c:36:3-36:20) + [Error][Imprecise][Unsound] Function definition missing for getU (79-type-nested-fields-deep1.c:43:3-43:24) diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.c b/tests/regression/04-mutex/80-type-nested-fields-deep2.c index 9a1e3028a3..4ddd4684f7 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.c +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // f s t -// \ / \ / +// \ / \ / // >f< s // \ / // >f< diff --git a/tests/regression/04-mutex/80-type-nested-fields-deep2.t b/tests/regression/04-mutex/80-type-nested-fields-deep2.t index a0541235ee..48e726f623 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -1,29 +1,29 @@ $ goblint --enable warn.deterministic --enable allglobs 80-type-nested-fields-deep2.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:37:3-37:22) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:44:3-44:24) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:36:3-36:22) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (80-type-nested-fields-deep2.c:43:3-43:24) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) - write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:44:3-44:24) + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:36:3-36:22) + write with [mhp:{tid=[main]; created={[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t.s.field) (80-type-nested-fields-deep2.c:43:3-43:24) [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 1 total memory locations: 2 [Success][Race] Memory location (struct T).s.field (safe): - write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:37:3-37:22) + write with [mhp:{tid=[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]}, thread:[main, t_fun@80-type-nested-fields-deep2.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->s.field) (80-type-nested-fields-deep2.c:36:3-36:22) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:37:3-37:22) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:44:3-44:24) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:44:3-44:24) - [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:37:3-37:22) - [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:44:3-44:24) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Unsound] Unknown address in {&tmp} has escaped. (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Unsound] Write to unknown address: privatization is unsound. (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:36:3-36:22) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (80-type-nested-fields-deep2.c:43:3-43:24) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (80-type-nested-fields-deep2.c:43:3-43:24) + [Error][Imprecise][Unsound] Function definition missing for getT (80-type-nested-fields-deep2.c:36:3-36:22) + [Error][Imprecise][Unsound] Function definition missing for getU (80-type-nested-fields-deep2.c:43:3-43:24) diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.c b/tests/regression/04-mutex/90-distribute-fields-type-1.c index 51f0e52426..062b7421e6 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.c +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< >s< t -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/90-distribute-fields-type-1.t b/tests/regression/04-mutex/90-distribute-fields-type-1.t index fb6af7ceca..ba3e3da0ed 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 90-distribute-fields-type-1.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:32:3-32:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:40:3-40:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:31:3-31:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (90-distribute-fields-type-1.c:39:3-39:17) [Warning][Race] Memory location (struct T).s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:31:3-31:20) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:39:3-39:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:32:3-32:20) + write with [mhp:{tid=[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}, thread:[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]] (conf. 100) (exp: & tmp->field) (90-distribute-fields-type-1.c:31:3-31:20) [Success][Race] Memory location (struct T).s (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:40:3-40:17) + write with [mhp:{tid=[main]; created={[main, t_fun@90-distribute-fields-type-1.c:37:3-37:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->s) (90-distribute-fields-type-1.c:39:3-39:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:32:3-32:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:40:3-40:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:40:3-40:17) - [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:32:3-32:20) - [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:40:3-40:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:31:3-31:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (90-distribute-fields-type-1.c:39:3-39:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (90-distribute-fields-type-1.c:39:3-39:17) + [Error][Imprecise][Unsound] Function definition missing for getS (90-distribute-fields-type-1.c:31:3-31:20) + [Error][Imprecise][Unsound] Function definition missing for getT (90-distribute-fields-type-1.c:39:3-39:17) diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.c b/tests/regression/04-mutex/91-distribute-fields-type-2.c index 12866105f6..01c945f730 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.c +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) >(S)< >(T)< (U) -// \ / \ / \ / +// \ / \ / \ / // f s t -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/91-distribute-fields-type-2.t b/tests/regression/04-mutex/91-distribute-fields-type-2.t index 0b4ec982e8..fd544b0b0b 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 91-distribute-fields-type-2.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:33:3-33:17) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:41:3-41:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:32:3-32:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (91-distribute-fields-type-2.c:40:3-40:17) [Warning][Race] Memory location (struct T).s (race with conf. 100): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:32:3-32:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:40:3-40:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S) (safe): - write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:33:3-33:17) + write with [mhp:{tid=[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}, thread:[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:32:3-32:17) [Success][Race] Memory location (struct T) (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:39:3-39:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:41:3-41:17) + write with [mhp:{tid=[main]; created={[main, t_fun@91-distribute-fields-type-2.c:38:3-38:40#top]}}, thread:[main]] (conf. 100) (exp: & *tmp) (91-distribute-fields-type-2.c:40:3-40:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:33:3-33:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:41:3-41:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:41:3-41:17) - [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:33:3-33:17) - [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:41:3-41:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:32:3-32:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (91-distribute-fields-type-2.c:40:3-40:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (91-distribute-fields-type-2.c:40:3-40:17) + [Error][Imprecise][Unsound] Function definition missing for getS (91-distribute-fields-type-2.c:32:3-32:17) + [Error][Imprecise][Unsound] Function definition missing for getT (91-distribute-fields-type-2.c:40:3-40:17) diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.c b/tests/regression/04-mutex/92-distribute-fields-type-deep.c index 891f5fb51f..59fb09a605 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.c +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.c @@ -1,11 +1,10 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include // (int) (S) (T) (U) -// \ / \ / \ / +// \ / \ / \ / // >f< s >t< -// \ / \ / +// \ / \ / // f s // \ / // f diff --git a/tests/regression/04-mutex/92-distribute-fields-type-deep.t b/tests/regression/04-mutex/92-distribute-fields-type-deep.t index aedc66f101..aefc1520d1 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -1,31 +1,31 @@ $ goblint --enable warn.deterministic --enable allglobs 92-distribute-fields-type-deep.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:37:3-37:20) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:45:3-45:17) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:36:3-36:20) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (92-distribute-fields-type-deep.c:44:3-44:17) [Warning][Race] Memory location (struct U).t.s.field (race with conf. 100): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:36:3-36:20) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:37:3-37:20) + write with [mhp:{tid=[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}, thread:[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]] (conf. 100) (exp: & tmp->field) (92-distribute-fields-type-deep.c:36:3-36:20) [Success][Race] Memory location (struct U).t (safe): - write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:43:3-43:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:45:3-45:17) + write with [mhp:{tid=[main]; created={[main, t_fun@92-distribute-fields-type-deep.c:42:3-42:40#top]}}, thread:[main]] (conf. 100) (exp: & tmp->t) (92-distribute-fields-type-deep.c:44:3-44:17) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:37:3-37:20) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:45:3-45:17) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:45:3-45:17) - [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:37:3-37:20) - [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:45:3-45:17) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Unsound] Unknown address in {&tmp} has escaped. (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Unsound] Write to unknown address: privatization is unsound. (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:36:3-36:20) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (92-distribute-fields-type-deep.c:44:3-44:17) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (92-distribute-fields-type-deep.c:44:3-44:17) + [Error][Imprecise][Unsound] Function definition missing for getS (92-distribute-fields-type-deep.c:36:3-36:20) + [Error][Imprecise][Unsound] Function definition missing for getU (92-distribute-fields-type-deep.c:44:3-44:17) diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.c b/tests/regression/04-mutex/93-distribute-fields-type-global.c index ad7839d95f..466d47e7fc 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.c +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.c @@ -1,4 +1,3 @@ -//PARAM: --enable ana.race.direct-arithmetic #include #include diff --git a/tests/regression/04-mutex/93-distribute-fields-type-global.t b/tests/regression/04-mutex/93-distribute-fields-type-global.t index fcc3e804c7..2199c689b1 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -1,25 +1,25 @@ $ goblint --enable warn.deterministic --enable allglobs 93-distribute-fields-type-global.c - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:14:3-14:29) - [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:9:10-9:11) - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (93-distribute-fields-type-global.c:13:3-13:29) + [Warning][Race] Memory location s.field (race with conf. 110): (93-distribute-fields-type-global.c:8:10-8:11) + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:13:3-13:29) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:22:3-22:9) [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 unsafe: 1 total memory locations: 3 [Success][Race] Memory location (struct S).field (safe): - read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:14:3-14:29) - [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:9:10-9:11) - write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:21:3-21:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:23:3-23:9) + read with [mhp:{tid=[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}, thread:[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]] (conf. 100) (exp: & tmp->field) (93-distribute-fields-type-global.c:13:3-13:29) + [Success][Race] Memory location s (safe): (93-distribute-fields-type-global.c:8:10-8:11) + write with [mhp:{tid=[main]; created={[main, t_fun@93-distribute-fields-type-global.c:20:3-20:40#top]}}, thread:[main]] (conf. 110) (exp: & s) (93-distribute-fields-type-global.c:22:3-22:9) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 0 total lines: 7 - [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:14:3-14:29) - [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:14:3-14:29) + [Info][Unsound] Unknown address in {&tmp} has escaped. (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Unsound] Unknown value in {?} could be an escaped pointer address! (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Unsound] Write to unknown address: privatization is unsound. (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(s, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(tmp, NoOffset)) (93-distribute-fields-type-global.c:13:3-13:29) + [Error][Imprecise][Unsound] Function definition missing for getS (93-distribute-fields-type-global.c:13:3-13:29) From 48884b3a2a17474092fff20f5298e410ff7be239 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:40:46 +0300 Subject: [PATCH 113/238] Fix non-struct typsig pretty in MemoRoot --- src/domains/access.ml | 4 +- src/util/cilfacade.ml | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 07d1632529..4069763b99 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -93,7 +93,7 @@ struct match vt with | `Var v -> CilType.Varinfo.pretty () v | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)" name - | `Type t -> Pretty.dprintf "(%a)" CilType.Typsig.pretty t (* TODO: fix TSBase printing *) + | `Type t -> Pretty.dprintf "(%a)" Cilfacade.pretty_typsig_like_typ t include Printable.SimplePretty ( struct @@ -116,7 +116,7 @@ struct match vt with | `Var v -> Pretty.dprintf "%a%a" CilType.Varinfo.pretty v Offset.Unit.pretty o | `Type (TSComp (_, name, _)) -> Pretty.dprintf "(struct %s)%a" name Offset.Unit.pretty o - | `Type t -> Pretty.dprintf "(%a)%a" CilType.Typsig.pretty t Offset.Unit.pretty o (* TODO: fix TSBase printing *) + | `Type t -> Pretty.dprintf "(%a)%a" Cilfacade.pretty_typsig_like_typ t Offset.Unit.pretty o include Printable.SimplePretty ( struct diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index d0dcc428ad..c77d2cd738 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -359,6 +359,101 @@ let split_anoncomp_name name = else invalid_arg "Cilfacade.split_anoncomp_name" +(** Pretty-print typsig like typ, because + {!d_typsig} prints with CIL constructors. *) +let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = + (* Copied & modified from Cil.defaultCilPrinterClass#pType. *) + let open Pretty in + let name = match nameOpt with None -> nil | Some d -> d in + let printAttributes (a: attributes) = + let pa = d_attrlist () a in + match nameOpt with + | None when not !print_CIL_Input -> + (* Cannot print the attributes in this case because gcc does not + like them here, except if we are printing for CIL. *) + if pa = nil then nil else + text "/*" ++ pa ++ text "*/" + | _ -> pa + in + match ts with + | TSBase t -> dn_type () t + | TSComp (cstruct, cname, a) -> + let su = if cstruct then "struct" else "union" in + text (su ^ " " ^ cname ^ " ") + ++ d_attrlist () a + ++ name + | TSEnum (ename, a) -> + text ("enum " ^ ename ^ " ") + ++ d_attrlist () a + ++ name + | TSPtr (bt, a) -> + (* Parenthesize the ( * attr name) if a pointer to a function or an + array. *) + let (paren: doc option), (bt': typsig) = + match bt with + | TSFun _ | TSArray _ -> Some (text "("), bt + | _ -> None, bt + in + let name' = text "*" ++ printAttributes a ++ name in + let name'' = (* Put the parenthesis *) + match paren with + Some p -> p ++ name' ++ text ")" + | _ -> name' + in + pretty_typsig_like_typ + (Some name'') + () + bt' + + | TSArray (elemt, lo, a) -> + (* ignore the const attribute for arrays *) + let a' = dropAttributes [ "pconst" ] a in + let name' = + if a' == [] then name else + if nameOpt == None then printAttributes a' else + text "(" ++ printAttributes a' ++ name ++ text ")" + in + pretty_typsig_like_typ + (Some (name' + ++ text "[" + ++ (match lo with None -> nil | Some e -> text (Z.to_string e)) + ++ text "]")) + () + elemt + + | TSFun (restyp, args, isvararg, a) -> + let name' = + if a == [] then name else + if nameOpt == None then printAttributes a else + text "(" ++ printAttributes a ++ name ++ text ")" + in + pretty_typsig_like_typ + (Some + (name' + ++ text "(" + ++ (align + ++ + (if args = Some [] && isvararg then + text "..." + else + (if args = None then nil + else if args = Some [] then text "void" + else + let pArg atype = + (pretty_typsig_like_typ None () atype) + in + (docList ~sep:(chr ',' ++ break) pArg) () + (match args with None -> [] | Some args -> args)) + ++ (if isvararg then break ++ text ", ..." else nil)) + ++ unalign) + ++ text ")")) + () + restyp + +(** Pretty-print typsig like typ, because + {!d_typsig} prints with CIL constructors. *) +let pretty_typsig_like_typ = pretty_typsig_like_typ None + (** HashSet of line numbers *) let locs = Hashtbl.create 200 From 789dd00022dfe69776c4e589b1d799c9120d433b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 15 Aug 2023 17:51:56 +0300 Subject: [PATCH 114/238] Use fewer Cil.typeSig calls for Access.add_distribute_outer --- src/domains/access.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 4069763b99..675d1c2e72 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -203,8 +203,7 @@ let add_one ~side memo: unit = (** Distribute empty access set for type-based access to variables and containing fields. Empty access sets are needed for prefix-type_suffix race checking. *) -let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = - let ts = typeSig t in +let rec add_distribute_outer ~side ~side_empty (ts: typsig) (o: Offset.Unit.t) = let memo = (`Type ts, o) in if M.tracing then M.tracei "access" "add_distribute_outer %a\n" Memo.pretty memo; add_one ~side memo; (* Add actual access for non-recursive call, or empty access for recursive call when side is side_empty. *) @@ -220,7 +219,7 @@ let rec add_distribute_outer ~side ~side_empty (t: typ) (o: Offset.Unit.t) = let fields = TSH.find_all typeIncl ts in List.iter (fun f -> (* prepend field and distribute to outer struct *) - add_distribute_outer ~side:side_empty ~side_empty (TComp (f.fcomp, [])) (`Field (f, o)) (* Switch to side_empty. *) + add_distribute_outer ~side:side_empty ~side_empty (TSComp (f.fcomp.cstruct, f.fcomp.cname, [])) (`Field (f, o)) (* Switch to side_empty. *) ) fields; if M.tracing then M.traceu "access" "add_distribute_outer\n" @@ -241,7 +240,7 @@ let add ~side ~side_empty e voffs = in match o with | `NoOffset when not !collect_direct_arithmetic && isArithmeticType t -> () - | _ -> add_distribute_outer ~side ~side_empty t o (* distribute to variables and outer offsets *) + | _ -> add_distribute_outer ~side ~side_empty (Cil.typeSig t) o (* distribute to variables and outer offsets *) end; if M.tracing then M.traceu "access" "add\n" From 5713231a6cc58d4edf85529f657f50e2ec2b51b0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 16 Aug 2023 17:14:35 +0300 Subject: [PATCH 115/238] Add some library functions that are used in the silver searcher --- src/analyses/libraryFunctions.ml | 15 +++++++++++++++ src/util/options.schema.json | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..42fa55a2b7 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -118,6 +118,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** C POSIX library functions. @@ -191,6 +192,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); + ("__open_missing_mode", unknown []); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); @@ -344,6 +346,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("sem_timedwait", unknown [drop "sem" [r]; drop "abs_timeout" [r]]); (* no write accesses to sem because sync primitive itself has no race *) + ("pthread_setaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); + ("pthread_getaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); ] (** GCC builtin functions. @@ -454,6 +458,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) + ("madvise", unknown [drop "addr" []; drop "length" []; drop "advice" []]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -803,6 +808,15 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wbkgd", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); ] +let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("pcre_compile", unknown [drop "pattern" [r]; drop "options" []; drop "errptr" [w]; drop "erroffset" [w]; drop "tableptr" [r]]); + ("pcre_compile2", unknown [drop "pattern" [r]; drop "options" []; drop "errorcodeptr" [w]; drop "errptr" [w]; drop "erroffset" [w]; drop "tableptr" [r]]); + ("pcre_config", unknown [drop "what" []; drop "where" [w]]); + ("pcre_exec", unknown [drop "code" [r_deep]; drop "extra" [r_deep]; drop "subject" [r]; drop "length" []; drop "startoffset" []; drop "options" []; drop "ovector" [w]; drop "ovecsize" []]); + ("pcre_study", unknown [drop "code" [r_deep]; drop "options" []; drop "errptr" [w]]); + ("pcre_version", unknown []); + ] + let libraries = Hashtbl.of_list [ ("c", c_descs_list @ math_descs_list); ("posix", posix_descs_list); @@ -815,6 +829,7 @@ let libraries = Hashtbl.of_list [ ("sv-comp", svcomp_descs_list); ("ncurses", ncurses_descs_list); ("zstd", zstd_descs_list); + ("pcre", pcre_descs_list); ] let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 0e89ede0b5..1ef73378fb 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1284,7 +1284,8 @@ "goblint", "sv-comp", "ncurses", - "zstd" + "zstd", + "pcre" ] }, "default": [ From a7656fbb3b926bc55b7e8b6afb1555caa7ceef98 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 09:43:49 +0200 Subject: [PATCH 116/238] Add check for memory deallocation at an offset --- src/analyses/base.ml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a213170ba2..2ef8257d59 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2013,14 +2013,21 @@ struct let st' = invalidate ~deep:false ~ctx (Analyses.ask_of_ctx ctx) gs st shallow_addrs in invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs - let check_free_of_non_heap_mem ctx special_fn ptr = + let check_invalid_mem_dealloc ctx special_fn ptr = + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then - M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr + else if Q.LS.exists (fun (_, o) -> has_offset o) points_to_set then + M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname let special ctx (lv:lval option) (f: varinfo) (args: exp list) = @@ -2304,7 +2311,7 @@ struct end | Realloc { ptr = p; size }, _ -> (* Realloc shouldn't be passed non-dynamically allocated memory *) - check_free_of_non_heap_mem ctx f p; + check_invalid_mem_dealloc ctx f p; begin match lv with | Some lv -> let ask = Analyses.ask_of_ctx ctx in @@ -2338,7 +2345,7 @@ struct end | Free ptr, _ -> (* Free shouldn't be passed non-dynamically allocated memory *) - check_free_of_non_heap_mem ctx f ptr; + check_invalid_mem_dealloc ctx f ptr; st | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine | Setjmp { env }, _ -> From 6ac2a6f2b0e2a149b064937369bb6d098efd7077 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 09:46:31 +0200 Subject: [PATCH 117/238] Refactor out "has_offset" as helper function --- src/analyses/base.ml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2ef8257d59..1087678c8b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -138,6 +138,11 @@ struct let project ask p_opt cpa fundec = CPA.mapi (fun varinfo value -> project_val ask (attributes_varinfo varinfo fundec) p_opt value (is_privglob varinfo)) cpa + let has_offset = function + | `NoOffset -> false + | `Field _ + | `Index _ -> true + (************************************************************************** * Initializing my variables @@ -1262,11 +1267,6 @@ struct match p with | Address a -> let s = addrToLvalSet a in - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then Queries.Result.bot q @@ -2014,11 +2014,6 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_invalid_mem_dealloc ctx special_fn ptr = - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> let points_to_set = addrToLvalSet a in From 0ed93f2db6901bc94e2d9ef4162d11064cf44b3f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 10:02:05 +0200 Subject: [PATCH 118/238] Add more regression test cases for invalid dealloc --- .../75-invalid_dealloc/05-free-at-offset.c | 9 +++++++++ .../75-invalid_dealloc/06-realloc-at-offset.c | 11 +++++++++++ .../75-invalid_dealloc/07-free-at-struct-offset.c | 15 +++++++++++++++ .../08-realloc-at-struct-offset.c | 15 +++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/05-free-at-offset.c create mode 100644 tests/regression/75-invalid_dealloc/06-realloc-at-offset.c create mode 100644 tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c create mode 100644 tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c diff --git a/tests/regression/75-invalid_dealloc/05-free-at-offset.c b/tests/regression/75-invalid_dealloc/05-free-at-offset.c new file mode 100644 index 0000000000..c9ec66c769 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/05-free-at-offset.c @@ -0,0 +1,9 @@ +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(42 * sizeof(char)); + ptr = ptr + 7; + free(ptr); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c b/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c new file mode 100644 index 0000000000..64a42654e1 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/06-realloc-at-offset.c @@ -0,0 +1,11 @@ +#include + +#define MAX_SIZE 5000 + +int main(int argc, char const *argv[]) { + char *ptr = malloc(42 * sizeof(char)); + ptr = ptr + 7; + realloc(ptr, MAX_SIZE); //WARN + + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c new file mode 100644 index 0000000000..f5f727f280 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c @@ -0,0 +1,15 @@ +#include + +typedef struct custom_t { + char *x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) { + custom_t *struct_ptr = malloc(sizeof(custom_t)); + struct_ptr->x = malloc(10 * sizeof(char)); + free(struct_ptr->x); //NOWARN + free(struct_ptr->y); //WARN + free(struct_ptr); //NOWARN + return 0; +} diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c new file mode 100644 index 0000000000..c163396524 --- /dev/null +++ b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c @@ -0,0 +1,15 @@ +#include + +typedef struct custom_t { + char *x; + int y; +} custom_t; + +int main(int argc, char const *argv[]) { + custom_t *struct_ptr = malloc(sizeof(custom_t)); + struct_ptr->x = malloc(10 * sizeof(char)); + realloc(struct_ptr->x, 50); //NOWARN + realloc(struct_ptr->y, 50); //WARN + realloc(struct_ptr, 2 * sizeof(custom_t)); //NOWARN + return 0; +} From cb811e1f6d6ff42c4f3f62f93d18c34c767fad75 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 11:19:28 +0200 Subject: [PATCH 119/238] Replace has_offset with comparisons --- src/analyses/base.ml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1087678c8b..558646a8e2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -138,11 +138,6 @@ struct let project ask p_opt cpa fundec = CPA.mapi (fun varinfo value -> project_val ask (attributes_varinfo varinfo fundec) p_opt value (is_privglob varinfo)) cpa - let has_offset = function - | `NoOffset -> false - | `Field _ - | `Index _ -> true - (************************************************************************** * Initializing my variables @@ -1268,7 +1263,7 @@ struct | Address a -> let s = addrToLvalSet a in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset) s then Queries.Result.bot q else ( let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in @@ -2021,7 +2016,7 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if Q.LS.exists (fun (_, o) -> has_offset o) points_to_set then + else if Q.LS.exists (fun (_, o) -> o <> `NoOffset) points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 0e61b9edf96f6c9fbb80121d0f3b81c9bfdbdfff Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:48:34 +0300 Subject: [PATCH 120/238] Add script for finding modules missing from API documentation --- scripts/goblint-lib-modules.py | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 scripts/goblint-lib-modules.py diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py new file mode 100755 index 0000000000..0347f5dc86 --- /dev/null +++ b/scripts/goblint-lib-modules.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 + +from pathlib import Path +import re +import sys + +src_root_path = Path("./src") + +goblint_lib_path = src_root_path / "goblint_lib.ml" +goblint_lib_modules = set() + +with goblint_lib_path.open() as goblint_lib_file: + for line in goblint_lib_file: + line = line.strip() + m = re.match(r"module (.*) = .*", line) + if m is not None: + module_name = m.group(1) + goblint_lib_modules.add(module_name) + +src_vendor_path = src_root_path / "vendor" +exclude_module_names = set([ + "Goblint_lib", # itself + + # executables + "Goblint", + "MessagesCompare", + "PrivPrecCompare", + "ApronPrecCompare", + "Mainspec", + + # libraries + "Goblint_timing", + "Goblint_backtrace", + "Goblint_sites", + "Goblint_build_info", + + "MessageCategory", # included in Messages + "PreValueDomain", # included in ValueDomain + "SpecCore", # spec stuff + "SpecUtil", # spec stuff +]) + +src_modules = set() + +for ml_path in src_root_path.glob("**/*.ml"): + if str(ml_path).startswith(str(src_vendor_path)): + continue + + module_name = ml_path.with_suffix("").with_suffix("").name + module_name = module_name[0].upper() + module_name[1:] + if module_name.endswith("0") or module_name.endswith("_intf") or module_name in exclude_module_names: + continue + + src_modules.add(module_name) + +if len(src_modules) > 0: + print(f"Modules missing from {goblint_lib_path}: {src_modules - goblint_lib_modules}") + sys.exit(1) From f618594ef2e9b1644a79890cab29b46340157d9b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:52:10 +0300 Subject: [PATCH 121/238] Add missing analyses to API documentation --- src/analyses/tmpSpecial.ml | 7 ++++--- src/goblint_lib.ml | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index c4af80906e..2d38972d7a 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,5 +1,6 @@ -(* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) +(** Analysis that tracks which variables hold the results of calls to math library functions ([tmpSpecial]). *) + +(** For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed. *) module VarEq = VarEq.Spec @@ -69,7 +70,7 @@ struct (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d - else + else D.add (v, Offset.Exp.of_cil offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index d8d74acc0f..625e81f18b 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -74,6 +74,7 @@ module ApronAnalysis = ApronAnalysis module AffineEqualityAnalysis = AffineEqualityAnalysis module VarEq = VarEq module CondVars = CondVars +module TmpSpecial = TmpSpecial (** {2 Heap} @@ -82,6 +83,8 @@ module CondVars = CondVars module Region = Region module MallocFresh = MallocFresh module Malloc_null = Malloc_null +module MemLeak = MemLeak +module UseAfterFree = UseAfterFree (** {2 Concurrency} From b9de7cb70349a8d6d416fffb3a7cc917166a217e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:54:15 +0300 Subject: [PATCH 122/238] Remove unused OSEK FlagModeDomain --- src/cdomains/flagModeDomain.ml | 52 ---------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 src/cdomains/flagModeDomain.ml diff --git a/src/cdomains/flagModeDomain.ml b/src/cdomains/flagModeDomain.ml deleted file mode 100644 index 70ee6d0015..0000000000 --- a/src/cdomains/flagModeDomain.ml +++ /dev/null @@ -1,52 +0,0 @@ -(* TODO: unused *) - -module Eq = IntDomain.MakeBooleans (struct let truename="==" let falsename="!=" end) -module Method = IntDomain.MakeBooleans (struct let truename="guard" let falsename="assign" end) - -module L_names = -struct - let bot_name = "unreachable" - let top_name = "unknown" -end - -module P = -struct - include Lattice.Flat (Printable.Prod3 (Method) (Eq) (IntDomain.FlatPureIntegers)) (L_names) - let show x = match x with - | `Lifted (m,b,e) -> Method.show m ^"ed "^ Eq.show b ^ " " ^ IntDomain.FlatPureIntegers.show e - | `Top -> top_name - | `Bot -> bot_name - - let join x y = match x,y with - | `Bot , z | z , `Bot -> z - | `Lifted (false,_,c1),`Lifted (false,_,c2) when c1=c2 -> y - | `Lifted (true,false,c1),`Lifted (true,false,c2) when c1=c2 -> y - | `Lifted (true,true,c1),`Lifted (true, true, c2) when c1=c2 -> y - | `Lifted (true,true,c1),`Lifted (true, false, c2) when not(c1=c2) -> y - | `Lifted (true,false,c1),`Lifted (true, true, c2) when not(c1=c2) -> x - | _ -> `Top - - - let leq (x:t) (y:t) = match x,y with - | `Bot , _ -> true - | _ , `Top -> true - | _, `Bot -> false - | `Top ,_ -> false - | `Lifted (false,_,c1), `Lifted (false,_,c2) -> c1=c2 - | _, `Lifted (false,_,_) -> false - | `Lifted (false,_,_), _ -> true - | `Lifted (true,true,c1),`Lifted (true, true, c2) -> c1=c2 - | _, `Lifted (true,true,_) -> false - | `Lifted (true, true, _), _ -> true - | `Lifted (true,false,c1),`Lifted (true,false,c2) -> c1=c2 - (* | _, `Lifted (true,false,c1) -> false - | `Lifted (true,false,_), _ -> true *) - (* | _ -> false *) -end - -module Dom = -struct - include MapDomain.MapTop_LiftBot (Basetype.Variables) (P) - - (* let find k x = if mem k x then find k x else P.top() *) -end From 67dcdfd73d30797884d29f55832f609d8c1986bc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:54:25 +0300 Subject: [PATCH 123/238] Fix goblint-lib-modules.py exit code --- scripts/goblint-lib-modules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 0347f5dc86..342f9a76bd 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -53,6 +53,7 @@ src_modules.add(module_name) -if len(src_modules) > 0: - print(f"Modules missing from {goblint_lib_path}: {src_modules - goblint_lib_modules}") +missing_modules = src_modules - goblint_lib_modules +if len(missing_modules) > 0: + print(f"Modules missing from {goblint_lib_path}: {missing_modules}") sys.exit(1) From a135dea1f9a0146893f1b38458f3839d8a75f8f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Aug 2023 12:55:45 +0300 Subject: [PATCH 124/238] Add goblint-lib-modules.py to docs workflow --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cd0414d6fe..aa69baf958 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,6 +32,9 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Check for undocumented modules + run: python scripts/goblint-lib-modules.py + - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: # otherwise setup-ocaml pins non-locked dependencies From 8614b522db13b1586bbe0f6ad0d162d07695e13d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 12:01:24 +0200 Subject: [PATCH 125/238] Fix two test cases by taking addresses of struct fields --- .../regression/75-invalid_dealloc/07-free-at-struct-offset.c | 4 ++-- .../75-invalid_dealloc/08-realloc-at-struct-offset.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c index f5f727f280..f64d66d8fc 100644 --- a/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c +++ b/tests/regression/75-invalid_dealloc/07-free-at-struct-offset.c @@ -8,8 +8,8 @@ typedef struct custom_t { int main(int argc, char const *argv[]) { custom_t *struct_ptr = malloc(sizeof(custom_t)); struct_ptr->x = malloc(10 * sizeof(char)); - free(struct_ptr->x); //NOWARN - free(struct_ptr->y); //WARN + free(&struct_ptr->x); //NOWARN + free(&struct_ptr->y); //WARN free(struct_ptr); //NOWARN return 0; } diff --git a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c index c163396524..fddb8a7694 100644 --- a/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c +++ b/tests/regression/75-invalid_dealloc/08-realloc-at-struct-offset.c @@ -8,8 +8,8 @@ typedef struct custom_t { int main(int argc, char const *argv[]) { custom_t *struct_ptr = malloc(sizeof(custom_t)); struct_ptr->x = malloc(10 * sizeof(char)); - realloc(struct_ptr->x, 50); //NOWARN - realloc(struct_ptr->y, 50); //WARN + realloc(&struct_ptr->x, 50); //NOWARN + realloc(&struct_ptr->y, 50); //WARN realloc(struct_ptr, 2 * sizeof(custom_t)); //NOWARN return 0; } From 92c0dcae14badbb2be23a62d7c5e15951f877bc2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 18 Aug 2023 12:01:45 +0200 Subject: [PATCH 126/238] Use cmp_zero_offset for checking invalid free at offset --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 558646a8e2..824fde18d9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2016,7 +2016,7 @@ struct M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potentially invalid memory deallocation may occur" d_exp ptr special_fn.vname else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr - else if Q.LS.exists (fun (_, o) -> o <> `NoOffset) points_to_set then + else if Q.LS.exists (fun (_, o) -> Offset.Exp.cmp_zero_offset o <> `MustZero) points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 761] "Free of memory not at start of buffer in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From fe1660e0e74d6934aec68d590ade46d29b171d0c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:24:32 +0300 Subject: [PATCH 127/238] Remove RaceAnalysis joke --- src/analyses/raceAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f617df2048..462834e2f4 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -3,7 +3,7 @@ open GoblintCil open Analyses -(** Data race analysis with tries and hookers. +(** Data race analysis with tries for offsets and type-based memory locations for open code. Accesses are to memory locations ({{!Access.Memo} memos}) which consist of a root and offset. {{!Access.MemoRoot} Root} can be: From 00fb4964810643a998fb522cfc4e470b07a21ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:30:09 +0300 Subject: [PATCH 128/238] Add C declarations to race analysis documentation --- src/analyses/raceAnalysis.ml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 462834e2f4..d452b6b131 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -41,7 +41,20 @@ open Analyses This requires an implementation hack to still eagerly do outer distribution, but only of empty access sets. It ensures that corresponding trie nodes exist for traversal later. *) -(** Example structure of related memos for race checking: +(** Given C declarations: + {@c[ + struct S { + int f; + }; + + struct T { + struct S s; + }; + + struct T t; + ]} + + Example structure of related memos for race checking: {v (int) (S) (T) \ / \ / \ @@ -54,9 +67,7 @@ open Analyses where: - [(int)] is a type-based memo root for the primitive [int] type; - [(S)] and [(T)] are short for [(struct S)] and [(struct T)], which are type-based memo roots; - - [f] is a field of [S] and [s] is a field of [T]; - - [t] is a global variable of type [T]. - - prefix relations are indicated by [/]; + - prefix relations are indicated by [/], so access paths run diagonally from top-right to bottom-left; - type suffix relations are indicated by [\ ]. Prefix races: From 0329b30ad74cce93b55d4998f169faf108ec3eca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:44:05 +0300 Subject: [PATCH 129/238] Make race analysis documentation example races list complete --- src/analyses/raceAnalysis.ml | 46 ++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d452b6b131..4ca1094b4a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -70,28 +70,60 @@ open Analyses - prefix relations are indicated by [/], so access paths run diagonally from top-right to bottom-left; - type suffix relations are indicated by [\ ]. - Prefix races: + All same-node races: + - Race between [t.s.f] and [t.s.f] is checked/reported at [t.s.f]. + - Race between [t.s] and [t.s] is checked/reported at [t.s]. + - Race between [t] and [t] is checked/reported at [t]. + - Race between [(T).s.f] and [(T).s.f] is checked/reported at [(T).s.f]. + - Race between [(T).s] and [(T).s] is checked/reported at [(T).s]. + - Race between [(T)] and [(T)] is checked/reported at [(T)]. + - Race between [(S).f] and [(S).f] is checked/reported at [(S).f]. + - Race between [(S)] and [(S)] is checked/reported at [(S)]. + - Race between [(int)] and [(int)] is checked/reported at [(int)]. + + All prefix races: - Race between [t.s.f] and [t.s] is checked/reported at [t.s.f]. - Race between [t.s.f] and [t] is checked/reported at [t.s.f]. - Race between [t.s] and [t] is checked/reported at [t.s]. - - Race between [t] and [t] is checked/reported at [t]. + - Race between [(T).s.f] and [(T).s] is checked/reported at [(T).s.f]. + - Race between [(T).s.f] and [(T)] is checked/reported at [(T).s.f]. + - Race between [(T).s] and [(T)] is checked/reported at [(T).s]. - Race between [(S).f] and [(S)] is checked/reported at [(S).f]. - Type suffix races: + All type suffix races: - Race between [t.s.f] and [(T).s.f] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(S).f] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(int)] is checked/reported at [t.s.f]. + - Race between [(T).s.f] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [(T).s.f] and [(int)] is checked/reported at [(T).s.f]. - Race between [(S).f] and [(int)] is checked/reported at [(S).f]. + - Race between [t.s] and [(T).s] is checked/reported at [t.s]. + - Race between [t.s] and [(S)] is checked/reported at [t.s]. + - Race between [(T).s] and [(S)] is checked/reported at [(T).s]. + - Race between [t] and [(T)] is checked/reported at [t]. - Type suffix prefix races: + All type suffix prefix races: - Race between [t.s.f] and [(T).s] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(T)] is checked/reported at [t.s.f]. - Race between [t.s.f] and [(S)] is checked/reported at [t.s.f]. - Race between [(T).s.f] and [(S)] is checked/reported at [(T).s.f]. - - Prefix-type suffix races: + - Race between [t.s] and [(T)] is checked/reported at [t.s]. + + All prefix-type suffix races: + - Race between [t.s] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t.s] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t.s] and [(int)] is checked/reported at [t.s.f]. + - Race between [t] and [(T).s.f] is checked/reported at [t.s.f]. + - Race between [t] and [(S).f] is checked/reported at [t.s.f]. + - Race between [t] and [(int)] is checked/reported at [t.s.f]. + - Race between [t] and [(T).s] is checked/reported at [t.s]. + - Race between [t] and [(S)] is checked/reported at [t.s]. - Race between [(T).s] and [(S).f] is checked/reported at [(T).s.f]. - - Race between [t] and [(S).f] is checked/reported at [t.s.f]. *) + - Race between [(T).s] and [(int)] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(S).f] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(int)] is checked/reported at [(T).s.f]. + - Race between [(T)] and [(S)] is checked/reported at [(T).s]. + - Race between [(S)] and [(int)] is checked/reported at [(S).f]. *) (** Data race analyzer without base --- this is the new standard *) From 039edfe67d1fe91cae4099407f9416dccb422f24 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 21 Aug 2023 12:41:25 +0300 Subject: [PATCH 130/238] Move fnmatch to posix group --- src/analyses/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 42fa55a2b7..6cb9675e9f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -118,7 +118,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** C POSIX library functions. @@ -281,6 +280,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ] (** Pthread functions. *) From c416816f05b46b6a52e00f92ec4a283ec4901f80 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:42:10 +0300 Subject: [PATCH 131/238] Fix pthread_setaffinity_np and pthread_getaffinity_np Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6cb9675e9f..deafddb473 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -346,8 +346,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("pthread_attr_setschedparam", unknown [drop "attr" [r; w]; drop "param" [r]]); ("sem_timedwait", unknown [drop "sem" [r]; drop "abs_timeout" [r]]); (* no write accesses to sem because sync primitive itself has no race *) - ("pthread_setaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); - ("pthread_getaffinity_np", unknown [drop "thread" [w]; drop "cpusetsize" []; drop "cpuset" [r; w]]); + ("pthread_setaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [r]]); + ("pthread_getaffinity_np", unknown [drop "thread" []; drop "cpusetsize" []; drop "cpuset" [w]]); ] (** GCC builtin functions. From a8a8899d94f237cb76825b8a78f508fa50d3f450 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 17:43:25 +0300 Subject: [PATCH 132/238] Add TODO for ana.race.direct-arithmetic in lazy outer distribution --- src/analyses/raceAnalysis.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 4ca1094b4a..d6621154b7 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -241,6 +241,7 @@ struct match root, offset with | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) | _, `NoOffset -> None (* primitive type *) + (* TODO: should handle ana.race.direct-arithmetic special case here? *) | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) From 24c07c4af851ce6856f0551aca995e2c7edf5049 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:34:40 +0300 Subject: [PATCH 133/238] Add MayPointTo query with address domain Co-authored-by: Simmo Saan --- src/analyses/base.ml | 6 ++++++ src/domains/queries.ml | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a213170ba2..a08e4a14f4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1289,6 +1289,12 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end + | Q.MayPointToA e -> begin + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with + | Address a -> a + | Bot -> Queries.Result.bot q (* TODO: remove *) + | _ -> Queries.Result.top q + end | Q.EvalThread e -> begin let v = eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore (Pretty.eprintf "evalthread %a (%a): %a" d_exp e d_plainexp e VD.pretty v); *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 214fcf1384..fcbaa4cba8 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -34,6 +34,7 @@ module FlatYojson = Lattice.Flat (Printable.Yojson) (struct module SD = Basetype.Strings module VD = ValueDomain.Compound +module AD = ValueDomain.AD module MayBool = BoolDomain.MayBool module MustBool = BoolDomain.MustBool @@ -72,6 +73,7 @@ type invariant_context = Invariant.context = { type _ t = | EqualSet: exp -> ES.t t | MayPointTo: exp -> LS.t t + | MayPointToA: exp -> AD.t t | ReachableFrom: exp -> LS.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -141,6 +143,7 @@ struct | EqualSet _ -> (module ES) | CondVars _ -> (module ES) | MayPointTo _ -> (module LS) + | MayPointToA _ -> (module AD) | ReachableFrom _ -> (module LS) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -205,6 +208,7 @@ struct | EqualSet _ -> ES.top () | CondVars _ -> ES.top () | MayPointTo _ -> LS.top () + | MayPointToA _ -> AD.top () | ReachableFrom _ -> LS.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -265,6 +269,7 @@ struct let order = function | Any (EqualSet _) -> 0 | Any (MayPointTo _) -> 1 + | Any (MayPointToA _) -> 999 | Any (ReachableFrom _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 @@ -321,6 +326,7 @@ struct match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 + | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -366,6 +372,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e | Any (MayPointTo e) -> CilType.Exp.hash e + | Any (MayPointToA e) -> CilType.Exp.hash e | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -408,6 +415,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e + | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e From 0ecf5755fc758d46620c70b0f8fddf7b2ee2c5d1 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:41:08 +0300 Subject: [PATCH 134/238] Use MayPointToA in mutexEventAnalysis Co-authored-by: Simmo Saan --- src/analyses/mutexEventsAnalysis.ml | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 2c57fa360b..e5839741b2 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -18,24 +18,12 @@ struct include UnitAnalysis.Spec let name () = "mutexEvents" - (* TODO: Use AddressDomain for queries *) - let eval_exp_addr (a: Queries.ask) exp = - let gather_addr (v,o) b = ValueDomain.Addr.of_mval (v, Addr.Offs.of_exp o) :: b in - match a.f (Queries.MayPointTo exp) with - | a when Queries.LS.is_top a -> - [Addr.UnknownPtr] - | a -> - let top_elt = (dummyFunDec.svar, `NoOffset) in - let addrs = Queries.LS.fold gather_addr (Queries.LS.remove top_elt a) [] in - if Queries.LS.mem top_elt a then - Addr.UnknownPtr :: addrs - else - addrs + let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) let lock ctx rw may_fail nonzero_return_when_aquired a lv arg = match lv with | None -> - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [Events.Lock (e, rw)] ) (eval_exp_addr a arg); if may_fail then @@ -43,7 +31,7 @@ struct raise Analyses.Deadcode | Some lv -> let sb = Events.SplitBranch (Lval lv, nonzero_return_when_aquired) in - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [sb; Events.Lock (e, rw)]; ) (eval_exp_addr a arg); if may_fail then ( @@ -67,7 +55,7 @@ struct let special (ctx: (unit, _, _, _) ctx) lv f arglist : D.t = let remove_rw x = x in let unlock arg remove_fn = - List.iter (fun e -> + Queries.AD.iter (fun e -> ctx.split () [Events.Unlock (remove_fn e)] ) (eval_exp_addr (Analyses.ask_of_ctx ctx) arg); raise Analyses.Deadcode @@ -83,7 +71,7 @@ struct (* mutex is unlocked while waiting but relocked when returns *) (* emit unlock-lock events for privatization *) let ms = eval_exp_addr (Analyses.ask_of_ctx ctx) m_arg in - List.iter (fun m -> + Queries.AD.iter (fun m -> (* unlock-lock each possible mutex as a split to be dependent *) (* otherwise may-point-to {a, b} might unlock a, but relock b *) ctx.split () [Events.Unlock m; Events.Lock (m, true)]; From fb43b5fff6197013f038b529dfbd4a8ef742b5bc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:50:27 +0300 Subject: [PATCH 135/238] Use MayPointToA in threadEscape Co-authored-by: Simmo Saan --- src/analyses/threadEscape.ml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 8a14f4102e..e8499f2fbf 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -4,6 +4,7 @@ open GoblintCil open Analyses module M = Messages +module AD = Queries.AD let has_escaped (ask: Queries.ask) (v: varinfo): bool = assert (not v.vglob); @@ -35,13 +36,17 @@ struct D.empty () let mpt (ask: Queries.ask) e: D.t = - match ask.f (Queries.MayPointTo e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) set = D.add v set in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) (D.empty ()) + match ask.f (Queries.MayPointToA e) with + | a when not (AD.is_top a) -> + let to_extra addr set = + match addr with + | AD.Addr.Addr (v,_) -> D.add v set + | _ -> set + in + AD.fold to_extra (AD.remove UnknownPtr a) (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | a -> - if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e Queries.LS.pretty a; + if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty a; D.empty () let thread_id ctx = From e1263bc86e179b9daeb92b82762ed867638aba9f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:15:51 +0300 Subject: [PATCH 136/238] Use MayPointToA in memLeak --- src/analyses/memLeak.ml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 99df5695a7..026a526439 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -49,14 +49,15 @@ struct | _ -> state end | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && Queries.LS.cardinal a = 1 -> + begin match ctx.ask (Queries.MayPointToA ptr) with + | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = - Queries.LS.filter (fun (v, _) -> ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v)) a - |> Queries.LS.elements - |> List.map fst - |> D.of_list + Queries.AD.fold (fun addr s -> + match addr with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s + | _ -> s + ) a (D.empty ()) in D.diff state unique_pointed_to_heap_vars | _ -> state From eb951b669d2295b7d8552e85fcc7bf88e53d69e9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:27:00 +0300 Subject: [PATCH 137/238] Use MayPointToA in mutexTypeAnalysis --- src/analyses/mutexTypeAnalysis.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 00e49260b4..815b3fdb67 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -54,9 +54,13 @@ struct match desc.special arglist with | MutexInit {mutex = mutex; attr = attr} -> let attr = ctx.ask (Queries.EvalMutexAttr attr) in - let mutexes = ctx.ask (Queries.MayPointTo mutex) in + let mutexes = ctx.ask (Queries.MayPointToA mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) - Queries.LS.iter (function (v, o) -> ctx.sideg (v,O.of_offs o) attr) mutexes; + Queries.AD.iter (function addr -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> ctx.sideg (v,O.of_offs o) attr + | _ -> () + ) mutexes; ctx.local | _ -> ctx.local From 4bd38f5c55fe4282c8f19322bddf98732c582684 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:45:19 +0300 Subject: [PATCH 138/238] Use MayPointToA in pthreadSignals --- src/analyses/pthreadSignals.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index 036d1bd2c6..f5d0e2c9d0 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,13 +17,7 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - (* TODO: Use AddressDomain for queries *) - let eval_exp_addr (a: Queries.ask) exp = - let gather_addr (v,o) b = ValueDomain.Addr.of_mval (v, ValueDomain.Addr.Offs.of_exp o) :: b in - match a.f (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - Queries.LS.fold gather_addr (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] - | _ -> [] + let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointToA exp)) let possible_vinfos a cv_arg = List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) From 68f5bb2ddae927199811ac13685237c90eaa67c3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 12:59:33 +0300 Subject: [PATCH 139/238] Use MayPointToA in useAfterFree --- src/analyses/useAfterFree.ml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c3aebc985e..52935a0c12 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,16 +82,18 @@ struct | Var _ -> Lval lval | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - match ctx.ask (Queries.MayPointTo lval_to_query) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + match ctx.ask (Queries.MayPointToA lval_to_query) with + | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) -> let warn_for_heap_var var = if D.mem var state then M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name in let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + Queries.AD.fold (fun addr l -> + match addr with + | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l + | _ -> l + ) a [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) From 940a77119cc2834ce4c1d5dd72c6d98dfd41080f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 13:33:34 +0300 Subject: [PATCH 140/238] Use MayPointToA in relationAnalysis --- src/analyses/apron/relationAnalysis.apron.ml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c5ad08ec76..dcc1a2d10d 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -157,15 +157,14 @@ struct {st' with rel = rel''} ) | (Mem v, NoOffset) -> - (let r = ask.f (Queries.MayPointTo v) in - match r with - | `Top -> - st - | `Lifted s -> - let lvals = Queries.LS.elements r in - let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (Mval.Exp.to_cil lv) f) lvals in - List.fold_right D.join ass' (D.bot ()) - ) + begin match ask.f (Queries.MayPointToA v) with + | a when Queries.AD.is_top a -> + st + | a -> + let lvals = List.filter_map Queries.AD.Addr.to_mval (Queries.AD.elements a) in + let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in + List.fold_right D.join ass' (D.bot ()) + end (* Ignoring all other assigns *) | _ -> st From ca39dafa55bdd4c675ece1857674313ad63cd170 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 13:36:47 +0300 Subject: [PATCH 141/238] Add mutex addr to unlock warning Co-authored-by: Simmo Saan --- src/analyses/mutexAnalysis.ml | 2 +- tests/regression/06-symbeq/21-mult_accs_rc.t | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 5a3aeb55ce..154f7ba183 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -155,7 +155,7 @@ struct let remove' ctx ~warn l = let s, m = ctx.local in let rm s = Lockset.remove (l, true) (Lockset.remove (l, false) s) in - if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex which may not be held"; + if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" Addr.pretty l; match Addr.to_mval l with | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> let m',rmed = Multiplicity.decrement l m in diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 7a4439141d..9b02dcde76 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -10,6 +10,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Race] Memory location (struct s).data (race with conf. 100): write with [mhp:{tid=[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}, thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{tid=[main]; created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) + [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) @@ -24,6 +25,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with [mhp:{tid=[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]}, thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37#top]] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) + [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) From 158a4505c4727fd64c551ff7d6eff804dba85304 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:13:23 +0300 Subject: [PATCH 142/238] Use MayPointToA in relationAnalysis cont. --- src/analyses/apron/relationAnalysis.apron.ml | 26 ++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index dcc1a2d10d..5052631d72 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -216,12 +216,16 @@ struct | CastE (t,e) -> CastE (t, inner e) | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> - (match ask (Queries.MayPointTo e) with - | a when not (Queries.LS.is_top a || Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) && (Queries.LS.cardinal a) = 1 -> - Mval.Exp.to_cil_exp (Queries.LS.choose a) - (* It would be possible to do better here, exploiting e.g. that the things pointed to are known to be equal *) - (* see: https://github.com/goblint/analyzer/pull/742#discussion_r879099745 *) - | _ -> Lval (Mem e, NoOffset)) + begin match ask (Queries.MayPointToA e) with + | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval + | None -> Lval (Mem e, NoOffset) + end + (* It would be possible to do better here, exploiting e.g. that the things pointed to are known to be equal *) + (* see: https://github.com/goblint/analyzer/pull/742#discussion_r879099745 *) + | _ -> Lval (Mem e, NoOffset) + end | e -> e (* TODO: Potentially recurse further? *) in inner e @@ -514,10 +518,12 @@ struct | None -> st) | _, _ -> let lvallist e = - let s = ask.f (Queries.MayPointTo e) in - match s with - | `Top -> [] - | `Lifted _ -> List.map Mval.Exp.to_cil (Queries.LS.elements s) + match ask.f (Queries.MayPointToA e) with + | a when Queries.AD.is_top a -> [] + | a -> + Queries.AD.elements a + |> List.filter_map Queries.AD.Addr.to_mval + |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in From 4bfa786dee3df950fadfb2c8ad41d0bd9205799a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:13:59 +0300 Subject: [PATCH 143/238] Add TODO for removing fold from memLeak --- src/analyses/memLeak.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 026a526439..ce0c047d3e 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -52,7 +52,7 @@ struct begin match ctx.ask (Queries.MayPointToA ptr) with | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = + let unique_pointed_to_heap_vars = (* TODO: no need for fold due to a being singleton *) Queries.AD.fold (fun addr s -> match addr with | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s From 71789d172ab6b7f4e3e92129332ecb05afe9232d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:20:34 +0300 Subject: [PATCH 144/238] Use MayPointToA in useAfterFree cont. --- src/analyses/useAfterFree.ml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 52935a0c12..d5d2ba0266 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -83,7 +83,7 @@ struct | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in match ctx.ask (Queries.MayPointToA lval_to_query) with - | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) -> + | a when not (Queries.AD.is_top a) -> let warn_for_heap_var var = if D.mem var state then M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name @@ -93,7 +93,7 @@ struct match addr with | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l | _ -> l - ) a [] + ) a [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) @@ -180,13 +180,14 @@ struct let desc = LibraryFunctions.find f in match desc.special arglist with | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + begin match ctx.ask (Queries.MayPointToA ptr) with + | a when not (Queries.AD.is_top a) -> let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) - |> D.of_list + Queries.AD.fold (fun addr s -> + match addr with + | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> D.add var s + | _ -> s + ) a (D.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); From 3cbd31f59f3e8914ef418610cdd13dbd829cb1e9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 14:54:59 +0300 Subject: [PATCH 145/238] Use MayPointToA in varEq --- src/analyses/varEq.ml | 75 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 99307d5d37..90ea4e5eae 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -137,7 +137,7 @@ struct (* TODO: why unused? how different from below? *) let may_change_pt ask (b:exp) (a:exp) : bool = - let pt e = ask (Queries.MayPointTo e) in + let pt e = ask (Queries.MayPointToA e) in let rec lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = match o with @@ -175,7 +175,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) - let pt e = ask.f (Queries.MayPointTo e) in + let pt e = ask.f (Queries.MayPointToA e) in let bls = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with @@ -208,26 +208,26 @@ struct | at -> at in bt = voidType || (isIntegralType at && isIntegralType bt) || (deref && typ_equal (TPtr (at,[]) ) bt) || typ_equal at bt || - match a with - | Const _ - | SizeOf _ - | SizeOfE _ - | SizeOfStr _ - | AlignOf _ - | AlignOfE _ - | AddrOfLabel _ -> false (* TODO: some may contain exps? *) - | UnOp (_,e,_) - | Real e - | Imag e -> type_may_change_t deref e - | BinOp (_,e1,e2,_) -> type_may_change_t deref e1 || type_may_change_t deref e2 - | Lval (Var _,o) - | AddrOf (Var _,o) - | StartOf (Var _,o) -> may_change_t_offset o - | Lval (Mem e,o) -> may_change_t_offset o || type_may_change_t true e - | AddrOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e - | StartOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e - | CastE (t,e) -> type_may_change_t deref e - | Question (b, t, f, _) -> type_may_change_t deref b || type_may_change_t deref t || type_may_change_t deref f + match a with + | Const _ + | SizeOf _ + | SizeOfE _ + | SizeOfStr _ + | AlignOf _ + | AlignOfE _ + | AddrOfLabel _ -> false (* TODO: some may contain exps? *) + | UnOp (_,e,_) + | Real e + | Imag e -> type_may_change_t deref e + | BinOp (_,e1,e2,_) -> type_may_change_t deref e1 || type_may_change_t deref e2 + | Lval (Var _,o) + | AddrOf (Var _,o) + | StartOf (Var _,o) -> may_change_t_offset o + | Lval (Mem e,o) -> may_change_t_offset o || type_may_change_t true e + | AddrOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e + | StartOf (Mem e,o) -> may_change_t_offset o || type_may_change_t false e + | CastE (t,e) -> type_may_change_t deref e + | Question (b, t, f, _) -> type_may_change_t deref b || type_may_change_t deref t || type_may_change_t deref f and lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = @@ -255,18 +255,21 @@ struct | `Index (i1,o), `Index (i2,s) when exp_equal i1 i2 -> oleq o s | _ -> false in - if Queries.LS.is_top als + if Queries.AD.is_top als then false - else Queries.LS.exists (fun (u,s) -> CilType.Varinfo.equal v u && oleq o s) als + else Queries.AD.exists (function + | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) + | _ -> false + ) als in let (als, test) = match addrOfExp a with - | None -> (Queries.LS.bot (), false) + | None -> (Queries.AD.bot (), false) | Some e -> let als = pt e in (als, lval_is_not_disjoint bl als) in - if (Queries.LS.is_top als) || Queries.LS.mem (dummyFunDec.svar, `NoOffset) als + if Queries.AD.is_top als then type_may_change_apt a else test || match a with @@ -292,9 +295,12 @@ struct in let r = if Cil.isConstant b then false - else if Queries.LS.is_top bls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) bls + else if Queries.AD.is_top bls then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) - else Queries.LS.exists (lval_may_change_pt a) bls + else Queries.AD.exists (function + | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) + | _ -> false + ) bls in (* if r then (Messages.warn ~category:Analyzer ~msg:("Kill " ^sprint 80 (Exp.pretty () a)^" because of "^sprint 80 (Exp.pretty () b)) (); r) @@ -338,9 +344,12 @@ struct | Lval (Var v,_) -> Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> - begin match ask.f (Queries.MayPointTo e) with - | ls when not (Queries.LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) ls) -> - Some (Queries.LS.exists (fun (v, _) -> is_global_var ask (Lval (var v)) = Some true) ls) + begin match ask.f (Queries.MayPointToA e) with + | ls when not (Queries.AD.is_top ls) -> + Some (Queries.AD.exists (function + | Addr (v, _) -> is_global_var ask (Lval (var v)) = Some true + | _ -> false + ) ls) | _ -> Some true end | CastE (t,e) -> is_global_var ask e @@ -489,8 +498,8 @@ struct end | ThreadCreate { arg; _ } -> begin match D.is_bot ctx.local with - | true -> raise Analyses.Deadcode - | false -> remove_reachable ~deep:true (Analyses.ask_of_ctx ctx) [arg] ctx.local + | true -> raise Analyses.Deadcode + | false -> remove_reachable ~deep:true (Analyses.ask_of_ctx ctx) [arg] ctx.local end | _ -> unknown_fn ctx lval f args (* query stuff *) From b5f6c4b5e0fe53d66d5ec7043b0c1960376e3c4a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:09:32 +0300 Subject: [PATCH 146/238] Use MayPointToA in uninit --- src/analyses/uninit.ml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 850bd677bd..55c9d2052a 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -31,10 +31,14 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = - match ask.f (Queries.MayPointTo (AddrOf lv)) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = (v, Addr.Offs.of_exp o, write) :: xs in - Queries.LS.fold to_extra a [] + match ask.f (Queries.MayPointToA (AddrOf lv)) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr (v,o) -> (v, o, write) :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] | _ -> M.info ~category:Unsound "Access to unknown address could be global"; [] @@ -164,10 +168,11 @@ struct let init_vo (v: varinfo) (ofs: lval_offs) : D.t = List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in - match a.f (Queries.MayPointTo (AddrOf lv)) with - | a when Queries.LS.cardinal a = 1 -> begin - let var, ofs = Queries.LS.choose a in - init_vo var (Addr.Offs.of_exp ofs) + match a.f (Queries.MayPointToA (AddrOf lv)) with + | a when Queries.AD.cardinal a = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | Some (var, ofs) -> init_vo var ofs + | None -> st end | _ -> st From 4e1cf15f28143ec2e231e791f38f75e25ebd8efe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 22 Aug 2023 15:10:16 +0300 Subject: [PATCH 147/238] Add comment about ana.race.direct-arithmetic --- src/analyses/raceAnalysis.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d6621154b7..74a98af6be 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -238,10 +238,11 @@ struct (** Get immediate type_suffix memo. *) let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = + (* No need to make ana.race.direct-arithmetic return None here, + because (int) is empty anyway since Access.add_distribute_outer isn't called. *) match root, offset with | `Var v, _ -> Some (`Type (Cil.typeSig v.vtype), offset) (* global.foo.bar -> (struct S).foo.bar *) (* TODO: Alloc variables void type *) | _, `NoOffset -> None (* primitive type *) - (* TODO: should handle ana.race.direct-arithmetic special case here? *) | _, `Field (f, offset') -> Some (`Type (Cil.typeSig f.ftype), offset') (* (struct S).foo.bar -> (struct T).bar *) | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) From 8937455d739a08a72c04484fc1bdcf4d01ed7324 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:19:32 +0300 Subject: [PATCH 148/238] Use MayPointToA in spec --- src/analyses/spec.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index d7328310dd..336b0c82f8 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -204,14 +204,18 @@ struct | _ -> Queries.Result.top q let query_lv ask exp = - match ask (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> - Queries.LS.elements l + match ask (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> + Queries.AD.elements l | _ -> [] let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [(v,_)] -> Some v + | [addr] -> + begin match addr with + | Queries.AD.Addr.Addr (v,_) -> Some v + | _ -> None + end | _ -> None From e4174431c0cdc358a71bafbff4a926a09af9b0a0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:20:35 +0300 Subject: [PATCH 149/238] Use MayPointToA in mallocFresh --- src/analyses/mallocFresh.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index c4a0c035f2..a7c1afb35e 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -18,10 +18,13 @@ struct let exitstate _ = D.empty () let assign_lval (ask: Queries.ask) lval local = - match ask.f (MayPointTo (AddrOf lval)) with - | ls when Queries.LS.is_top ls || Queries.LS.mem (dummyFunDec.svar, `NoOffset) ls -> + match ask.f (MayPointToA (AddrOf lval)) with + | ls when Queries.AD.is_top ls -> D.empty () - | ls when Queries.LS.exists (fun (v, _) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v)) ls -> + | ls when Queries.AD.exists (function + | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) + | _ -> false + ) ls -> D.empty () | _ -> local From 16f35001545a54d9f5779b023cf9807b85fdfd1f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:32:16 +0300 Subject: [PATCH 150/238] Use MayPointToA in malloc_null --- src/analyses/malloc_null.ml | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 656e1e6f14..d7c1c954e4 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -37,10 +37,12 @@ struct with SetDomain.Unsupported _ -> () end;*) match e with | Lval (Var v, offs) -> - begin match a.f (Queries.MayPointTo (mkAddrOf (Var v,offs))) with - | a when not (Queries.LS.is_top a) - && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - Queries.LS.iter (fun (v,o) -> warn_lval st (v, Offs.of_exp o)) a + begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with + | a when not (Queries.AD.is_top a) -> + Queries.AD.iter (function + | Queries.AD.Addr.Addr addr -> warn_lval st addr + | _ -> () + ) a | _ -> () end | _ -> () @@ -112,11 +114,9 @@ struct else D.filter (fun x -> AD.mem x vars) st let get_concrete_lval (ask: Queries.ask) (lval:lval) = - match ask.f (Queries.MayPointTo (mkAddrOf lval)) with - | a when Queries.LS.cardinal a = 1 - && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - let v, o = Queries.LS.choose a in - Some (Var v, Offs.of_exp o) + match ask.f (Queries.MayPointToA (mkAddrOf lval)) with + | a when Queries.AD.cardinal a = 1 && not (Queries.AD.mem UnknownPtr a) -> + Queries.AD.Addr.to_mval (Queries.AD.choose a) | _ -> None let get_concrete_exp (exp:exp) gl (st:D.t) = @@ -126,12 +126,14 @@ struct | _ -> None let might_be_null (ask: Queries.ask) lv gl st = - match ask.f (Queries.MayPointTo (mkAddrOf lv)) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) -> - let one_addr_might (v,o) = - D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of (v, Offs.of_exp o) x) (Addr.to_mval x)) st + match ask.f (Queries.MayPointToA (mkAddrOf lv)) with + | a when not (Queries.AD.is_top a) -> + let one_addr_might = function + | Queries.AD.Addr.Addr addr -> + D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of addr x) (Addr.to_mval x)) st + | _ -> false in - Queries.LS.exists one_addr_might a + Queries.AD.exists one_addr_might a | _ -> false (* @@ -143,8 +145,8 @@ struct warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval lval) ; warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local rval; match get_concrete_exp rval ctx.global ctx.local, get_concrete_lval (Analyses.ask_of_ctx ctx) lval with - | Some rv , Some (Var vt,ot) when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> - D.add (Addr.of_mval (vt,ot)) ctx.local + | Some rv, Some addr when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> + D.add (Addr.of_mval addr) ctx.local | _ -> ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = @@ -185,7 +187,7 @@ struct match lval, D.mem (return_addr ()) au with | Some lv, true -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some (Var v,ofs) -> D.add (Addr.of_mval (v,ofs)) ctx.local + | Some addr -> D.add (Addr.of_mval addr) ctx.local | _ -> ctx.local end | _ -> ctx.local @@ -198,9 +200,9 @@ struct | Malloc _, Some lv -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some (Var v, offs) -> + | Some addr -> ctx.split ctx.local [Events.SplitBranch ((Lval lv), true)]; - ctx.split (D.add (Addr.of_mval (v,offs)) ctx.local) [Events.SplitBranch ((Lval lv), false)]; + ctx.split (D.add (Addr.of_mval addr) ctx.local) [Events.SplitBranch ((Lval lv), false)]; raise Analyses.Deadcode | _ -> ctx.local end From 68d658852592584f9ff4ed0c9b9b6b49c7f8b01d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:46:35 +0300 Subject: [PATCH 151/238] Use MayPointToA in fileUse --- src/analyses/fileUse.ml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 174cd6a914..2f88ce03dc 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,22 +24,28 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q let query_lv (ask: Queries.ask) exp = - match ask.f (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> - Queries.LS.elements l + match ask.f (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> + Queries.AD.elements l | _ -> [] let print_query_lv ?msg:(msg="") ask exp = let xs = query_lv ask exp in (* MayPointTo -> LValSet *) - let pretty_key k = Pretty.text (D.string_of_key k) in - if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs + let pretty_key = function + | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) + | _ -> Pretty.text "" in + if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [(v,_)] -> Some v + | [addr] -> + begin match addr with + | Queries.AD.Addr.Addr (v,_) -> Some v + | _ -> None + end | _ -> None From e6975045f806420fbf3e5c9c2db543fe213de231 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 15:55:58 +0300 Subject: [PATCH 152/238] Use MayPointToA in extractPthread --- src/analyses/extractPthread.ml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 2041c23e1b..4fa912a75a 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -880,20 +880,23 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = let mayPointTo ctx exp = - let a = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.LS.is_top a)) && Queries.LS.cardinal a > 0 then - let top_elt = (dummyFunDec.svar, `NoOffset) in + let a = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then let a' = - if Queries.LS.mem top_elt a + if Queries.AD.mem UnknownPtr a then (* UNSOUND *) - Queries.LS.remove top_elt a + Queries.AD.remove UnknownPtr a else a in - Queries.LS.elements a' + Queries.AD.elements a' else [] in - List.map fst @@ mayPointTo ctx exp + List.fold (fun l addr -> + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: l + | _ -> l + ) [] (mayPointTo ctx exp) let eval_var ctx exp = From 076da538d68b64a0957db70dff4ba283ac2e6b9f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:10:34 +0300 Subject: [PATCH 153/238] Use MayPointToA in condVars Co-authored-by: Simmo Saan --- src/analyses/condVars.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 5a2e97139c..5f50d3968d 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,15 +64,17 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && Queries.LS.cardinal a > 0 -> - let top_elt = (dummyFunDec.svar, `NoOffset) in - let a' = if Queries.LS.mem top_elt a then ( + match ctx.ask (Queries.MayPointToA exp) with + | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> + let a' = if Queries.AD.mem UnknownPtr a then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.LS.remove top_elt a + Queries.AD.remove UnknownPtr a ) else a in - Queries.LS.elements a' + List.filter_map (function + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) + | _ -> None + ) (Queries.AD.elements a') | _ -> [] let mustPointTo ctx exp = (* this is just to get Mval.Exp *) From 1c7186cb910e8a041d6b3c6dfd65c3cd2e348e46 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:13:38 +0300 Subject: [PATCH 154/238] Use MayPointToA in mvalMapDomain --- src/cdomains/mvalMapDomain.ml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 9d7625c4f5..78357a6d73 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,13 +281,19 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with - | l when not (Queries.LS.is_top l) -> Queries.LS.elements l + let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + | l when not (Queries.AD.is_top l) -> Queries.AD.elements l | _ -> [] in let exp = AddrOf lval in let xs = query_lv ask exp in (* MayPointTo -> LValSet *) + let keys = List.fold (fun l addr -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: l + | _ -> l + ) [] xs + in let pretty_key k = Pretty.text (string_of_key k) in - Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs; - xs + Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; + keys end From f7b38a0ca666e433f79e2057de8cd4eb59a52a7b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 16:43:21 +0300 Subject: [PATCH 155/238] Add ReachableFrom query with address domain --- src/analyses/base.ml | 11 +++++++++++ src/domains/queries.ml | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a08e4a14f4..7967b10df2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1317,6 +1317,17 @@ struct addrs | _ -> Q.LS.empty () end + | Q.ReachableFromA e -> begin + match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with + | Top -> Queries.Result.top q + | Bot -> Queries.Result.bot q (* TODO: remove *) + | Address a -> + let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) + let xs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in + let addrs = List.fold_left (Q.AD.join) (Q.AD.empty ()) xs in + addrs + | _ -> Q.AD.empty () + end | Q.ReachableUkTypes e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index fcbaa4cba8..42be039d96 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -75,6 +75,7 @@ type _ t = | MayPointTo: exp -> LS.t t | MayPointToA: exp -> AD.t t | ReachableFrom: exp -> LS.t t + | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t | MayEscape: varinfo -> MayBool.t t @@ -145,6 +146,7 @@ struct | MayPointTo _ -> (module LS) | MayPointToA _ -> (module AD) | ReachableFrom _ -> (module LS) + | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) | EvalFunvar _ -> (module LS) @@ -210,6 +212,7 @@ struct | MayPointTo _ -> LS.top () | MayPointToA _ -> AD.top () | ReachableFrom _ -> LS.top () + | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () | EvalFunvar _ -> LS.top () @@ -271,6 +274,7 @@ struct | Any (MayPointTo _) -> 1 | Any (MayPointToA _) -> 999 | Any (ReachableFrom _) -> 2 + | Any (ReachableFromA _) -> 666 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -328,6 +332,7 @@ struct | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 + | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 | Any (MayEscape vi1), Any (MayEscape vi2) -> CilType.Varinfo.compare vi1 vi2 @@ -374,6 +379,7 @@ struct | Any (MayPointTo e) -> CilType.Exp.hash e | Any (MayPointToA e) -> CilType.Exp.hash e | Any (ReachableFrom e) -> CilType.Exp.hash e + | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e | Any (MayEscape vi) -> CilType.Varinfo.hash vi @@ -417,6 +423,7 @@ struct | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e + | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e | Any (MayEscape vi) -> Pretty.dprintf "MayEscape %a" CilType.Varinfo.pretty vi From b49f0e0553df9b8fc34205636f9559745bd2e690 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 17:50:06 +0300 Subject: [PATCH 156/238] Use MayPointToA and ReachableFromA in accessAnalysis Co-authored-by: Simmo Saan --- src/analyses/accessAnalysis.ml | 33 ++++++++++++--------------- src/analyses/modifiedSinceLongjmp.ml | 13 ++++++++++- src/analyses/mutexAnalysis.ml | 34 +++++++++++++--------------- src/analyses/poisonVariables.ml | 17 +++++++++----- src/analyses/raceAnalysis.ml | 32 +++++++++++++------------- src/domains/events.ml | 4 ++-- 6 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index bc5330726c..9c2cc31eda 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in let ls = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; lvals=ls; kind; reach}) @@ -138,24 +138,19 @@ struct let event ctx e octx = match e with | Events.Access {lvals; kind; _} when !collect_local && !AnalysisState.postsolving -> - begin match lvals with - | ls when Queries.LS.is_top ls -> - let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in - ctx.sideg ctx.node (G.singleton access) - | ls -> - let events = Queries.LS.fold (fun (var, offs) acc -> - let coffs = Offset.Exp.to_cil offs in - let access: AccessDomain.Event.t = - if CilType.Varinfo.equal var dummyFunDec.svar then - {var_opt = None; offs_opt = (Some coffs); kind} - else - {var_opt = (Some var); offs_opt = (Some coffs); kind} - in - G.add access acc - ) ls (G.empty ()) - in - ctx.sideg ctx.node events - end + let events = Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (var, offs) -> + let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp offs) in + let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in + G.add access acc + | UnknownPtr -> + let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in + G.add access acc + | _ -> acc + ) lvals (G.empty ()) + in + ctx.sideg ctx.node events | _ -> ctx.local end diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index f489b08fe9..836cf6f827 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -29,6 +29,17 @@ struct else Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) + let relevants_from_ad ls = + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top ls then + VS.top () + else + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v, _) -> if is_relevant v then VS.add v acc else acc + | _ -> acc + ) ls (VS.empty ()) + (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) @@ -63,7 +74,7 @@ struct let event ctx (e: Events.t) octx = match e with | Access {lvals; kind = Write; _} -> - add_to_all_defined (relevants_from_ls lvals) ctx.local + add_to_all_defined (relevants_from_ad lvals) ctx.local | _ -> ctx.local end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 154f7ba183..7f504badf2 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -296,7 +296,7 @@ struct | Events.Access {exp; lvals; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in (* must use original (pre-assign, etc) ctx queries *) - let old_access var_opt offs_opt = + let old_access var_opt = (* TODO: this used to use ctx instead of octx, why? *) (*privatization*) match var_opt with @@ -325,24 +325,22 @@ struct ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in - let module LS = Queries.LS in + let module AD = Queries.AD in let has_escaped g = octx.ask (Queries.MayEscape g) in - let on_lvals ls = - let ls = LS.filter (fun (g,_) -> g.vglob || has_escaped g) ls in - let f (var, offs) = - let coffs = Offset.Exp.to_cil offs in - if CilType.Varinfo.equal var dummyFunDec.svar then - old_access None (Some coffs) - else - old_access (Some var) (Some coffs) + let on_lvals ad = + let f addr = + match addr with + | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> old_access (Some g) + | UnknownPtr -> old_access None + | _ -> () in - LS.iter f ls + AD.iter f ad in begin match lvals with - | ls when not (LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) ls) -> + | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ls - | ls when not (LS.is_top ls) -> + on_lvals ad + | ad -> (* the case where the points-to set is non top and contains unknown values *) (* now we need to access all fields that might be pointed to: is this correct? *) begin match octx.ask (ReachableUkTypes exp) with @@ -354,11 +352,11 @@ struct | _ -> false in if Queries.TS.exists f ts then - old_access None None + old_access None end; - on_lvals ls - | _ -> - old_access None None + on_lvals ad + (* | _ -> + old_access None None *) end; ctx.local | _ -> diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 5cb34baa26..b124cb90f0 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -80,23 +80,28 @@ struct ) longjmp_nodes; D.join modified_locals ctx.local | Access {lvals; kind = Read; _} -> - if Queries.LS.is_top lvals then ( + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top lvals then ( if not (VS.is_empty octx.local) then M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" ) else ( - Queries.LS.iter (fun lv -> + Queries.AD.iter (function (* Use original access state instead of current with removed written vars. *) - check_lval octx.local lv + | Queries.AD.Addr.Addr (v,o) -> check_lval octx.local (v, ValueDomain.Offs.to_exp o) + | _ -> () ) lvals ); ctx.local | Access {lvals; kind = Write; _} -> - if Queries.LS.is_top lvals then + (* TODO: what about AD with both known and unknown pointers? *) + if Queries.AD.is_top lvals then ctx.local else ( - Queries.LS.fold (fun lv acc -> - rem_lval acc lv + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> rem_lval acc (v, ValueDomain.Offs.to_exp o) + | _ -> acc ) lvals ctx.local ) | _ -> ctx.local diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 970895e971..11e77bf902 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -134,7 +134,7 @@ struct | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in - let module LS = Queries.LS in + let module AD = Queries.AD in let part_access (vo:varinfo option): MCPAccess.A.t = (*partitions & locks*) Obj.obj (octx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind}))) @@ -151,24 +151,24 @@ struct let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls -- this is the common case if we have a sound points-to set. *) - let on_lvals ls includes_uk = - let ls = LS.filter (fun (g,_) -> g.vglob || has_escaped g) ls in + let on_lvals ad includes_uk = let conf = if reach then conf - 20 else conf in let conf = if includes_uk then conf - 10 else conf in - let f (var, offs) = - let coffs = Offset.Exp.to_cil offs in - if CilType.Varinfo.equal var dummyFunDec.svar then - add_access conf None - else - add_access conf (Some (var, coffs)) + let f addr = + match addr with + | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> + let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp o) in + add_access conf (Some (g, coffs)) + | UnknownPtr -> add_access conf None + | _ -> () in - LS.iter f ls + AD.iter f ad in begin match lvals with - | ls when not (LS.is_top ls) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) ls) -> + | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ls false - | ls when not (LS.is_top ls) -> + on_lvals ad false + | ad -> (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in (* now we need to access all fields that might be pointed to: is this correct? *) @@ -185,9 +185,9 @@ struct in Queries.TS.iter f ts end; - on_lvals ls !includes_uk - | _ -> - add_access (conf - 60) None + on_lvals ad !includes_uk + (* | _ -> + add_access (conf - 60) None *) end; ctx.local | _ -> diff --git a/src/domains/events.ml b/src/domains/events.ml index 2141ad17dd..41e745ed8a 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -10,7 +10,7 @@ type t = | EnterMultiThreaded | SplitBranch of exp * bool (** Used to simulate old branch-based split. *) | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) - | Access of {exp: CilType.Exp.t; lvals: Queries.LS.t; kind: AccessKind.t; reach: bool} + | Access of {exp: CilType.Exp.t; lvals: Queries.AD.t; kind: AccessKind.t; reach: bool} | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp @@ -41,7 +41,7 @@ let pretty () = function | EnterMultiThreaded -> text "EnterMultiThreaded" | SplitBranch (exp, tv) -> dprintf "SplitBranch (%a, %B)" d_exp exp tv | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid - | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.LS.pretty lvals AccessKind.pretty kind reach + | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty lvals AccessKind.pretty kind reach | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp From 20b75c6303e25de30aa9472f87b90dce63d90110 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 18:32:08 +0300 Subject: [PATCH 157/238] Use MayPointToA and ReachableFromA in varEq Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 1 + src/analyses/varEq.ml | 33 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..d9211ce897 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -279,6 +279,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w_deep]]); ] (** Pthread functions. *) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 90ea4e5eae..71ab214481 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -389,17 +389,11 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - match st with - | None -> None - | Some st -> - let q = if deep then Queries.ReachableFrom e else Queries.MayPointTo e in - let vs = ask.f q in - if Queries.LS.is_top vs then - None - else - Some (Queries.LS.join vs st) + let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in + let vs = ask.f q in + Queries.AD.join vs st in - List.fold_right reachable es (Some (Queries.LS.empty ())) + List.fold_right reachable es (Queries.AD.empty ()) (* Probably ok as is. *) @@ -460,15 +454,16 @@ struct | None -> ctx.local let remove_reachable ~deep ask es st = - match reachables ~deep ask es with - | None -> D.top () - | Some rs -> - (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": - each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. - It is unknown, why that was necessary. *) - Queries.LS.fold (fun lval st -> - remove ask (Mval.Exp.to_cil lval) st - ) rs st + let rs = reachables ~deep ask es in + (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": + each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. + It is unknown, why that was necessary. *) + Queries.AD.fold (fun addr st -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> remove ask (Mval.Exp.to_cil (v,ValueDomain.Offs.to_exp o)) st + | UnknownPtr -> D.top () + | _ -> st + ) rs st let unknown_fn ctx lval f args = let desc = LF.find f in From 5e8e2c7b87586a8fcdeb1e7324a81c0dc72b5273 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 10:33:29 +0300 Subject: [PATCH 158/238] Make getaddrinfo specification more precise --- src/analyses/libraryFunctions.ml | 2 +- src/analyses/varEq.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index d9211ce897..67ba8c8c20 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -279,7 +279,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w_deep]]); + ("getaddrinfo", unknown [drop "node" [r]; drop "service" [r]; drop "hints" [r_deep]; drop "res" [w]]); (* only write res non-deep because it doesn't write to existing fields of res *) ] (** Pthread functions. *) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 71ab214481..811d1afffc 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -455,6 +455,7 @@ struct let remove_reachable ~deep ask es st = let rs = reachables ~deep ask es in + if M.tracing then M.tracel "var_eq" "remove_reachable %a: %a\n" (Pretty.d_list ", " d_exp) es AD.pretty rs; (* Prior to https://github.com/goblint/analyzer/pull/694 checks were done "in the other direction": each expression in st was checked for reachability from es/rs using very conservative but also unsound reachable_from. It is unknown, why that was necessary. *) From e0792129058919f0ea12ce075e47085d5ad175b7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 12:46:40 +0300 Subject: [PATCH 159/238] Remove unnecessary offset double conversions --- src/analyses/accessAnalysis.ml | 4 ++-- src/analyses/raceAnalysis.ml | 4 ++-- src/analyses/varEq.ml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 9c2cc31eda..500a6e1494 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -141,10 +141,10 @@ struct let events = Queries.AD.fold (fun addr acc -> match addr with | Queries.AD.Addr.Addr (var, offs) -> - let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp offs) in + let coffs = ValueDomain.Offs.to_cil offs in let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in G.add access acc - | UnknownPtr -> + | UnknownPtr -> let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in G.add access acc | _ -> acc diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 11e77bf902..d911dafd91 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -157,10 +157,10 @@ struct let f addr = match addr with | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> - let coffs = Offset.Exp.to_cil (ValueDomain.Offs.to_exp o) in + let coffs = ValueDomain.Offs.to_cil o in add_access conf (Some (g, coffs)) | UnknownPtr -> add_access conf None - | _ -> () + | _ -> () in AD.iter f ad in diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 811d1afffc..edec2f40d4 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -461,7 +461,7 @@ struct It is unknown, why that was necessary. *) Queries.AD.fold (fun addr st -> match addr with - | Queries.AD.Addr.Addr (v,o) -> remove ask (Mval.Exp.to_cil (v,ValueDomain.Offs.to_exp o)) st + | Queries.AD.Addr.Addr mval -> remove ask (ValueDomain.Mval.to_cil mval) st | UnknownPtr -> D.top () | _ -> st ) rs st From 2f4119d28a487f29d45e0a7cfe80d218a9884565 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Aug 2023 12:58:48 +0300 Subject: [PATCH 160/238] Use AddressDomain helper functions for to_mval and to_var_may --- src/analyses/apron/relationAnalysis.apron.ml | 5 ++-- src/analyses/extractPthread.ml | 30 +++++++------------- src/analyses/fileUse.ml | 6 +--- src/analyses/spec.ml | 6 +--- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 5052631d72..513663a2cd 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -161,7 +161,7 @@ struct | a when Queries.AD.is_top a -> st | a -> - let lvals = List.filter_map Queries.AD.Addr.to_mval (Queries.AD.elements a) in + let lvals = Queries.AD.to_mval a in let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in List.fold_right D.join ass' (D.bot ()) end @@ -521,8 +521,7 @@ struct match ask.f (Queries.MayPointToA e) with | a when Queries.AD.is_top a -> [] | a -> - Queries.AD.elements a - |> List.filter_map Queries.AD.Addr.to_mval + Queries.AD.to_mval a |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 4fa912a75a..774e115050 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -879,25 +879,17 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let mayPointTo ctx exp = - let a = ctx.ask (Queries.MayPointToA exp) in - if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then - let a' = - if Queries.AD.mem UnknownPtr a - then (* UNSOUND *) - Queries.AD.remove UnknownPtr a - else a - in - Queries.AD.elements a' - else - [] - in - List.fold (fun l addr -> - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: l - | _ -> l - ) [] (mayPointTo ctx exp) - + let a = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then + let a' = + if Queries.AD.mem UnknownPtr a + then (* UNSOUND *) + Queries.AD.remove UnknownPtr a + else a + in + Queries.AD.to_var_may a' + else + [] let eval_var ctx exp = match exp with diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 2f88ce03dc..06b8d43c40 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -41,11 +41,7 @@ struct let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [addr] -> - begin match addr with - | Queries.AD.Addr.Addr (v,_) -> Some v - | _ -> None - end + | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 336b0c82f8..568482631c 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -211,11 +211,7 @@ struct let eval_fv ask exp: varinfo option = match query_lv ask exp with - | [addr] -> - begin match addr with - | Queries.AD.Addr.Addr (v,_) -> Some v - | _ -> None - end + | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None From cfab28c088480e8d1bff2b0ae24a5b40684f4bfb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 13:49:05 +0300 Subject: [PATCH 161/238] Use ReachableFromA in extractPthread --- src/analyses/extractPthread.ml | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 774e115050..5361fefbe3 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -244,7 +244,7 @@ let fun_ctx ctx f = f.vname ^ "_" ^ ctx_hash -module Tasks = SetDomain.Make (Lattice.Prod (Queries.LS) (PthreadDomain.D)) +module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (PthreadDomain.D)) module rec Env : sig type t @@ -869,7 +869,7 @@ module Spec : Analyses.MCPSpec = struct module C = D (** Set of created tasks to spawn when going multithreaded *) - module Tasks = SetDomain.Make (Lattice.Prod (Queries.LS) (D)) + module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (D)) module G = Tasks @@ -1119,18 +1119,21 @@ module Spec : Analyses.MCPSpec = struct let arglist' = List.map (stripCasts % constFold false) arglist in match (LibraryFunctions.find f).special arglist', f.vname, arglist with | ThreadCreate { thread; start_routine = func; _ }, _, _ -> - let funs_ls = - let ls = ctx.ask (Queries.ReachableFrom func) in - Queries.LS.filter - (fun lv -> - let lval = Mval.Exp.to_cil lv in - isFunctionType (typeOfLval lval)) - ls + let funs_ad = + let ad = ctx.ask (Queries.ReachableFromA func) in + Queries.AD.filter + (function + | Queries.AD.Addr.Addr addr -> + isFunctionType (ValueDomain.Addr.Mval.type_of addr) + | _ -> false) + ad in let thread_fun = - funs_ls - |> Queries.LS.elements - |> List.map fst + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: acc + | _ -> acc + ) funs_ad [] |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd in @@ -1143,7 +1146,7 @@ module Spec : Analyses.MCPSpec = struct ; ctx = Ctx.top () } in - Tasks.singleton (funs_ls, f_d) + Tasks.singleton (funs_ad, f_d) in ctx.sideg tasks_var tasks ; in @@ -1255,7 +1258,10 @@ module Spec : Analyses.MCPSpec = struct (* TODO: optimize finding *) let tasks_f = Tasks.filter - (fun (fs, f_d) -> Queries.LS.exists (fun (ls_f, _) -> ls_f = f) fs) + (fun (fs, f_d) -> Queries.AD.exists (function + | Queries.AD.Addr.Addr (ls_f, _) -> ls_f = f + | _ -> false) + fs) tasks in let f_d = snd (Tasks.choose tasks_f) in From 178a9c62e040549f8258fc05d4a281c87caeaa02 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 13:53:33 +0300 Subject: [PATCH 162/238] Remove duplicated Tasks module from extractPthread --- src/analyses/extractPthread.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 5361fefbe3..2cba97425d 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -869,8 +869,6 @@ module Spec : Analyses.MCPSpec = struct module C = D (** Set of created tasks to spawn when going multithreaded *) - module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (D)) - module G = Tasks let tasks_var = From 08c9f1d35eb068e81218e870c3571968e89234bf Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:03:46 +0300 Subject: [PATCH 163/238] Use ReachableFromA in malloc_null --- src/analyses/malloc_null.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index d7c1c954e4..ee5c23914c 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -95,10 +95,14 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = AD.of_mval (v, Offs.of_exp o) :: xs in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + Queries.AD.fold ( + fun addr xs -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> AD.of_mval (v,o) :: xs + | _ -> xs + ) a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From 5dbfb0255d3b86615371cfaf6bbe39a88481dcb1 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:32:33 +0300 Subject: [PATCH 164/238] Use ReachableFromA in poisonVariables --- src/analyses/poisonVariables.ml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index b124cb90f0..49bd338538 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -42,14 +42,17 @@ struct if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in - if Queries.LS.is_top reachable_from_args || VS.is_top ctx.local then + let reachable_from_args = List.fold (fun ls e -> Queries.AD.join ls (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else let reachable_vars = - Queries.LS.elements reachable_from_args - |> List.map fst - |> VS.of_list + let get_vars addr vs = + match addr with + | Queries.AD.Addr.Addr (v,_) -> VS.add v vs + | _ -> vs + in + Queries.AD.fold get_vars reachable_from_args (VS.empty ()) in [VS.diff ctx.local reachable_vars, VS.inter reachable_vars ctx.local] ) From de9c475bcb5e68253e954f66499248da1a2328a8 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:45:43 +0300 Subject: [PATCH 165/238] Use ReachableFromA in threadEscape --- src/analyses/threadEscape.ml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index e8499f2fbf..3e2b41d903 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -26,13 +26,17 @@ struct module G = ThreadIdSet let reachable (ask: Queries.ask) e: D.t = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) set = D.add v set in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) (D.empty ()) + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr set = + match addr with + | Queries.AD.Addr.Addr (v,_) -> D.add v set + | _ -> set + in + Queries.AD.fold to_extra a (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) | a -> - if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.LS.pretty a; + if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty a; D.empty () let mpt (ask: Queries.ask) e: D.t = From 2cd0ae6294e31ba881be57149c48c04ac21a66b9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:47:10 +0300 Subject: [PATCH 166/238] Extract to_extra fun from fold in malloc_null --- src/analyses/malloc_null.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index ee5c23914c..2bebd5c0f6 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -96,13 +96,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - Queries.AD.fold ( - fun addr xs -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> AD.of_mval (v,o) :: xs - | _ -> xs - ) a [] + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From e4bef9c9638eb1184bc65388a067b2c32c370b06 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 14:53:56 +0300 Subject: [PATCH 167/238] Use ReachableFromA in uninit --- src/analyses/uninit.ml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 55c9d2052a..1f04964d58 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -195,10 +195,14 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFrom e) with - | a when not (Queries.LS.is_top a) -> - let to_extra (v,o) xs = AD.of_mval (v, Addr.Offs.of_exp o) :: xs in - Queries.LS.fold to_extra (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) [] + match ask.f (Queries.ReachableFromA e) with + | a when not (Queries.AD.is_top a) -> + let to_extra addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs + | _ -> xs + in + Queries.AD.fold to_extra a [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in From 973edf6c9c6b411ec0a91287015ad369b1407363 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 15:03:03 +0300 Subject: [PATCH 168/238] Use ReachableFromA in useAfterFree --- src/analyses/useAfterFree.ml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index d5d2ba0266..4e28c49771 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,11 +156,18 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun acc arg -> Queries.LS.join acc (ctx.ask (ReachableFrom arg))) (Queries.LS.empty ()) args in - if Queries.LS.is_top reachable_from_args || D.is_top caller_state then + let reachable_from_args = List.fold_left (fun acc arg -> Queries.AD.join acc (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else - let reachable_vars = List.map fst (Queries.LS.elements reachable_from_args) in + let reachable_vars = + let get_vars addr vs = + match addr with + | Queries.AD.Addr.Addr (v,_) -> v :: vs + | _ -> vs + in + Queries.AD.fold get_vars reachable_from_args [] + in let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in [caller_state, callee_state] ) From cf58111767c31dcadd57222dbf89441bbf8ca257 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 23 Aug 2023 18:51:40 +0300 Subject: [PATCH 169/238] Use ReachableFromA in relationAnalysis --- src/analyses/apron/relationAnalysis.apron.ml | 37 ++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 513663a2cd..d93a96e1b2 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -11,6 +11,7 @@ open Analyses open RelationDomain module M = Messages +module VS = SetDomain.Make (CilType.Varinfo) module SpecFunctor (Priv: RelationPriv.S) (RD: RelationDomain.RD) (PCU: RelationPrecCompareUtil.Util) = struct @@ -271,7 +272,15 @@ struct let any_local_reachable fundec reachable_from_args = let locals = fundec.sformals @ fundec.slocals in let locals_id = List.map (fun v -> v.vid) locals in - Queries.LS.exists (fun (v',_) -> List.mem v'.vid locals_id && RD.Tracked.varinfo_tracked v') reachable_from_args + VS.exists (fun v -> List.mem v.vid locals_id && RD.Tracked.varinfo_tracked v) reachable_from_args + + let reachable_from_args ctx args = + let vs e = + ctx.ask (ReachableFromA e) + |> LockDomain.MayLocksetNoRW.to_var_may + |> VS.of_list + in + List.fold (fun ls e -> VS.join ls (vs e)) (VS.empty ()) args let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) @@ -291,7 +300,6 @@ struct |> List.filter (fun (x, _) -> RD.Tracked.varinfo_tracked x) |> List.map (Tuple2.map1 RV.arg) in - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in let arg_vars = List.map fst arg_assigns in let new_rel = RD.add_vars st.rel arg_vars in (* RD.assign_exp_parallel_with new_rel arg_assigns; (* doesn't need to be parallel since exps aren't arg vars directly *) *) @@ -307,6 +315,7 @@ struct ) ) new_rel arg_assigns in + let reachable_from_args = reachable_from_args ctx args in let any_local_reachable = any_local_reachable fundec reachable_from_args in RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with @@ -369,16 +378,20 @@ struct let combine_env ctx r fe f args fc fun_st (f_ask : Queries.ask) = let st = ctx.local in - let reachable_from_args = List.fold (fun ls e -> Queries.LS.join ls (ctx.ask (ReachableFrom e))) (Queries.LS.empty ()) args in + let reachable_from_args = reachable_from_args ctx args in let fundec = Node.find_fundec ctx.node in if M.tracing then M.tracel "combine" "relation f: %a\n" CilType.Varinfo.pretty f.svar; if M.tracing then M.tracel "combine" "relation formals: %a\n" (d_list "," CilType.Varinfo.pretty) f.sformals; if M.tracing then M.tracel "combine" "relation args: %a\n" (d_list "," d_exp) args; let new_fun_rel = RD.add_vars fun_st.rel (RD.vars st.rel) in let arg_substitutes = + let filter_actuals (x,e) = + RD.Tracked.varinfo_tracked x + && List.for_all (fun v -> not (VS.mem v reachable_from_args)) (Basetype.CilExp.get_vars e) + in GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) (* Do not do replacement for actuals whose value may be modified after the call *) - |> List.filter (fun (x, e) -> RD.Tracked.varinfo_tracked x && List.for_all (fun v -> not (Queries.LS.exists (fun (v',_) -> v'.vid = v.vid) reachable_from_args)) (Basetype.CilExp.get_vars e)) + |> List.filter filter_actuals |> List.map (Tuple2.map1 RV.arg) in (* RD.substitute_exp_parallel_with new_fun_rel arg_substitutes; (* doesn't need to be parallel since exps aren't arg vars directly *) *) @@ -441,13 +454,13 @@ struct match st with | None -> None | Some st -> - let vs = ask.f (Queries.ReachableFrom e) in - if Queries.LS.is_top vs then + let vs = ask.f (Queries.ReachableFromA e) in + if Queries.AD.is_top vs then None else - Some (Queries.LS.join vs st) + Some (Queries.AD.join vs st) in - List.fold_right reachable es (Some (Queries.LS.empty ())) + List.fold_right reachable es (Some (Queries.AD.empty ())) let forget_reachable ctx st es = @@ -460,8 +473,12 @@ struct |> List.filter_map RV.to_cil_varinfo |> List.map Cil.var | Some rs -> - Queries.LS.elements rs - |> List.map Mval.Exp.to_cil + let to_cil addr xs = + match addr with + | Queries.AD.Addr.Addr addr -> (ValueDomain.Addr.Mval.to_cil addr) :: xs + | _ -> xs + in + Queries.AD.fold to_cil rs [] in List.fold_left (fun st lval -> invalidate_one ask ctx st lval From 2e4b9f34cd69da48af447b178a438f45fa01efd7 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 11:25:31 +0300 Subject: [PATCH 170/238] Refactor everything until memLeak --- src/analyses/apron/relationAnalysis.apron.ml | 35 +++++++++--------- src/analyses/base.ml | 5 +-- src/analyses/extractPthread.ml | 39 ++++++++++---------- src/analyses/fileUse.ml | 13 +++---- src/analyses/mallocFresh.ml | 11 ++---- src/analyses/malloc_null.ml | 38 +++++++++---------- src/analyses/memLeak.ml | 12 +++--- 7 files changed, 72 insertions(+), 81 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index d93a96e1b2..c096ba8e6c 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -159,11 +159,10 @@ struct ) | (Mem v, NoOffset) -> begin match ask.f (Queries.MayPointToA v) with - | a when Queries.AD.is_top a -> - st - | a -> - let lvals = Queries.AD.to_mval a in - let ass' = List.map (fun lv -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil lv) f) lvals in + | ad when Queries.AD.is_top ad -> st + | ad -> + let mvals = Queries.AD.to_mval ad in + let ass' = List.map (fun mval -> assign_to_global_wrapper ask getg sideg st (ValueDomain.Addr.Mval.to_cil mval) f) mvals in List.fold_right D.join ass' (D.bot ()) end (* Ignoring all other assigns *) @@ -275,12 +274,12 @@ struct VS.exists (fun v -> List.mem v.vid locals_id && RD.Tracked.varinfo_tracked v) reachable_from_args let reachable_from_args ctx args = - let vs e = + let to_vs e = ctx.ask (ReachableFromA e) |> LockDomain.MayLocksetNoRW.to_var_may |> VS.of_list in - List.fold (fun ls e -> VS.join ls (vs e)) (VS.empty ()) args + List.fold (fun vs e -> VS.join vs (to_vs e)) (VS.empty ()) args let pass_to_callee fundec any_local_reachable var = (* TODO: currently, we pass all locals of the caller to the callee, provided one of them is reachbale to preserve relationality *) @@ -454,11 +453,11 @@ struct match st with | None -> None | Some st -> - let vs = ask.f (Queries.ReachableFromA e) in - if Queries.AD.is_top vs then + let ad = ask.f (Queries.ReachableFromA e) in + if Queries.AD.is_top ad then None else - Some (Queries.AD.join vs st) + Some (Queries.AD.join ad st) in List.fold_right reachable es (Some (Queries.AD.empty ())) @@ -472,13 +471,13 @@ struct RD.vars st.rel |> List.filter_map RV.to_cil_varinfo |> List.map Cil.var - | Some rs -> - let to_cil addr xs = + | Some ad -> + let to_cil addr rs = match addr with - | Queries.AD.Addr.Addr addr -> (ValueDomain.Addr.Mval.to_cil addr) :: xs - | _ -> xs + | Queries.AD.Addr.Addr mval -> (ValueDomain.Addr.Mval.to_cil mval) :: rs + | _ -> rs in - Queries.AD.fold to_cil rs [] + Queries.AD.fold to_cil ad [] in List.fold_left (fun st lval -> invalidate_one ask ctx st lval @@ -536,9 +535,9 @@ struct | _, _ -> let lvallist e = match ask.f (Queries.MayPointToA e) with - | a when Queries.AD.is_top a -> [] - | a -> - Queries.AD.to_mval a + | ad when Queries.AD.is_top ad -> [] + | ad -> + Queries.AD.to_mval ad |> List.map ValueDomain.Addr.Mval.to_cil in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7967b10df2..a4a1262589 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1323,9 +1323,8 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) - let xs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in - let addrs = List.fold_left (Q.AD.join) (Q.AD.empty ()) xs in - addrs + let addrs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in + List.fold_left (Q.AD.join) (Q.AD.empty ()) addrs | _ -> Q.AD.empty () end | Q.ReachableUkTypes e -> begin diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 2cba97425d..9c08269058 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,15 +877,14 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let a = ctx.ask (Queries.MayPointToA exp) in - if (not (Queries.AD.is_top a)) && Queries.AD.cardinal a > 0 then - let a' = - if Queries.AD.mem UnknownPtr a - then (* UNSOUND *) - Queries.AD.remove UnknownPtr a - else a - in - Queries.AD.to_var_may a' + let ad = ctx.ask (Queries.MayPointToA exp) in + if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then + if Queries.AD.mem UnknownPtr ad + then (* UNSOUND *) + Queries.AD.remove UnknownPtr ad + |> Queries.AD.to_var_may + else + Queries.AD.to_var_may ad else [] @@ -1121,16 +1120,16 @@ module Spec : Analyses.MCPSpec = struct let ad = ctx.ask (Queries.ReachableFromA func) in Queries.AD.filter (function - | Queries.AD.Addr.Addr addr -> - isFunctionType (ValueDomain.Addr.Mval.type_of addr) + | Queries.AD.Addr.Addr mval -> + isFunctionType (ValueDomain.Mval.type_of mval) | _ -> false) ad in let thread_fun = - Queries.AD.fold (fun addr acc -> + Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: acc - | _ -> acc + | Queries.AD.Addr.Addr (v,_) -> v :: vars + | _ -> vars ) funs_ad [] |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd @@ -1255,12 +1254,12 @@ module Spec : Analyses.MCPSpec = struct let tasks = ctx.global tasks_var in (* TODO: optimize finding *) let tasks_f = - Tasks.filter - (fun (fs, f_d) -> Queries.AD.exists (function - | Queries.AD.Addr.Addr (ls_f, _) -> ls_f = f - | _ -> false) - fs) - tasks + let var_in_ad ad f = Queries.AD.exists (function + | Queries.AD.Addr.Addr (ls_f,_) -> ls_f = f + | _ -> false + ) ad + in + Tasks.filter (fun (ad,_) -> var_in_ad ad f) tasks in let f_d = snd (Tasks.choose tasks_f) in [ { f_d with pred = d.pred } ] diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index 06b8d43c40..d4635722b9 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,23 +24,22 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointToA: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q - let query_lv (ask: Queries.ask) exp = + let query_ad (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> - Queries.AD.elements l + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let print_query_lv ?msg:(msg="") ask exp = - let xs = query_lv ask exp in (* MayPointTo -> LValSet *) + let addrs = query_ad ask exp in (* MayPointTo -> LValSet *) let pretty_key = function | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) xs + if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs let eval_fv ask exp: varinfo option = - match query_lv ask exp with + match query_ad ask exp with | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index a7c1afb35e..3bea7b8c73 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -19,15 +19,12 @@ struct let assign_lval (ask: Queries.ask) lval local = match ask.f (MayPointToA (AddrOf lval)) with - | ls when Queries.AD.is_top ls -> - D.empty () - | ls when Queries.AD.exists (function + | ad when Queries.AD.is_top ad -> D.empty () + | ad when Queries.AD.exists (function | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) | _ -> false - ) ls -> - D.empty () - | _ -> - local + ) ad -> D.empty () + | _ -> local let assign ctx lval rval = assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 2bebd5c0f6..feb1d97a51 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -38,11 +38,11 @@ struct match e with | Lval (Var v, offs) -> begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> Queries.AD.iter (function - | Queries.AD.Addr.Addr addr -> warn_lval st addr + | Queries.AD.Addr.Addr mval -> warn_lval st mval | _ -> () - ) a + ) ad | _ -> () end | _ -> () @@ -96,13 +96,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - let to_extra addr xs = + | ad when not (Queries.AD.is_top ad) -> + let to_extra addr ads = match addr with - | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs - | _ -> xs + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | _ -> ads in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in @@ -119,8 +119,8 @@ struct let get_concrete_lval (ask: Queries.ask) (lval:lval) = match ask.f (Queries.MayPointToA (mkAddrOf lval)) with - | a when Queries.AD.cardinal a = 1 && not (Queries.AD.mem UnknownPtr a) -> - Queries.AD.Addr.to_mval (Queries.AD.choose a) + | ad when Queries.AD.cardinal ad = 1 && not (Queries.AD.mem UnknownPtr ad) -> + Queries.AD.Addr.to_mval (Queries.AD.choose ad) | _ -> None let get_concrete_exp (exp:exp) gl (st:D.t) = @@ -131,13 +131,13 @@ struct let might_be_null (ask: Queries.ask) lv gl st = match ask.f (Queries.MayPointToA (mkAddrOf lv)) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let one_addr_might = function - | Queries.AD.Addr.Addr addr -> - D.exists (fun x -> GobOption.exists (fun x -> is_prefix_of addr x) (Addr.to_mval x)) st + | Queries.AD.Addr.Addr mval -> + D.exists (fun addr -> GobOption.exists (fun x -> is_prefix_of mval x) (Addr.to_mval addr)) st | _ -> false in - Queries.AD.exists one_addr_might a + Queries.AD.exists one_addr_might ad | _ -> false (* @@ -149,8 +149,8 @@ struct warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval lval) ; warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local rval; match get_concrete_exp rval ctx.global ctx.local, get_concrete_lval (Analyses.ask_of_ctx ctx) lval with - | Some rv, Some addr when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> - D.add (Addr.of_mval addr) ctx.local + | Some rv, Some mval when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> + D.add (Addr.of_mval mval) ctx.local | _ -> ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = @@ -191,7 +191,7 @@ struct match lval, D.mem (return_addr ()) au with | Some lv, true -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some addr -> D.add (Addr.of_mval addr) ctx.local + | Some mval -> D.add (Addr.of_mval mval) ctx.local | _ -> ctx.local end | _ -> ctx.local @@ -204,9 +204,9 @@ struct | Malloc _, Some lv -> begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some addr -> + | Some mval -> ctx.split ctx.local [Events.SplitBranch ((Lval lv), true)]; - ctx.split (D.add (Addr.of_mval addr) ctx.local) [Events.SplitBranch ((Lval lv), false)]; + ctx.split (D.add (Addr.of_mval mval) ctx.local) [Events.SplitBranch ((Lval lv), false)]; raise Analyses.Deadcode | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index ce0c047d3e..660d9ba591 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -50,14 +50,12 @@ struct end | Free ptr -> begin match ctx.ask (Queries.MayPointToA ptr) with - | a when not (Queries.AD.is_top a) && not (Queries.AD.mem UnknownPtr a) && Queries.AD.cardinal a = 1 -> + | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = (* TODO: no need for fold due to a being singleton *) - Queries.AD.fold (fun addr s -> - match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.add v s - | _ -> s - ) a (D.empty ()) + let unique_pointed_to_heap_vars = + match Queries.AD.choose ad with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v + | _ -> D.empty () in D.diff state unique_pointed_to_heap_vars | _ -> state From 641de7dfe06d68630a18d320634cd82710b5efd3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 16:47:30 +0300 Subject: [PATCH 171/238] Refactor everything after memLeak --- src/analyses/accessAnalysis.ml | 16 ++++----- src/analyses/modifiedSinceLongjmp.ml | 16 ++++----- src/analyses/mutexAnalysis.ml | 19 +++++------ src/analyses/mutexEventsAnalysis.ml | 4 +-- src/analyses/poisonVariables.ml | 50 ++++++++++++++-------------- src/analyses/raceAnalysis.ml | 12 +++---- src/analyses/spec.ml | 7 ++-- src/analyses/threadEscape.ml | 18 +++++----- src/analyses/uninit.ml | 12 +++---- src/analyses/useAfterFree.ml | 34 +++++++++---------- src/analyses/varEq.ml | 30 ++++++++--------- src/cdomains/mvalMapDomain.ml | 14 ++++---- src/domains/events.ml | 4 +-- 13 files changed, 117 insertions(+), 119 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index 500a6e1494..cf024afb10 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -34,8 +34,8 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in - let ls = ctx.ask reach_or_mpt in - ctx.emit (Access {exp=e; lvals=ls; kind; reach}) + let ad = ctx.ask reach_or_mpt in + ctx.emit (Access {exp=e; ad; kind; reach}) (** Three access levels: + [deref=false], [reach=false] - Access [exp] without dereferencing, used for all normal reads and all function call arguments. @@ -137,18 +137,18 @@ struct let event ctx e octx = match e with - | Events.Access {lvals; kind; _} when !collect_local && !AnalysisState.postsolving -> - let events = Queries.AD.fold (fun addr acc -> + | Events.Access {ad; kind; _} when !collect_local && !AnalysisState.postsolving -> + let events = Queries.AD.fold (fun addr es -> match addr with | Queries.AD.Addr.Addr (var, offs) -> let coffs = ValueDomain.Offs.to_cil offs in let access: AccessDomain.Event.t = {var_opt = (Some var); offs_opt = (Some coffs); kind} in - G.add access acc + G.add access es | UnknownPtr -> let access: AccessDomain.Event.t = {var_opt = None; offs_opt = None; kind} in - G.add access acc - | _ -> acc - ) lvals (G.empty ()) + G.add access es + | _ -> es + ) ad (G.empty ()) in ctx.sideg ctx.node events | _ -> diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 836cf6f827..0375bd3f74 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -29,16 +29,16 @@ struct else Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) - let relevants_from_ad ls = + let relevants_from_ad ad = (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top ls then + if Queries.AD.is_top ad then VS.top () else - Queries.AD.fold (fun addr acc -> + Queries.AD.fold (fun addr vs -> match addr with - | Queries.AD.Addr.Addr (v, _) -> if is_relevant v then VS.add v acc else acc - | _ -> acc - ) ls (VS.empty ()) + | Queries.AD.Addr.Addr (v,_) -> if is_relevant v then VS.add v vs else vs + | _ -> vs + ) ad (VS.empty ()) (* transfer functions *) let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = @@ -73,8 +73,8 @@ struct let event ctx (e: Events.t) octx = match e with - | Access {lvals; kind = Write; _} -> - add_to_all_defined (relevants_from_ad lvals) ctx.local + | Access {ad; kind = Write; _} -> + add_to_all_defined (relevants_from_ad ad) ctx.local | _ -> ctx.local end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 7f504badf2..7d8298a0a4 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -293,7 +293,7 @@ struct let event ctx e octx = match e with - | Events.Access {exp; lvals; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in (* must use original (pre-assign, etc) ctx queries *) let old_access var_opt = @@ -327,19 +327,18 @@ struct in let module AD = Queries.AD in let has_escaped g = octx.ask (Queries.MayEscape g) in - let on_lvals ad = - let f addr = - match addr with - | AD.Addr.Addr (g,o) when g.vglob || has_escaped g -> old_access (Some g) + let on_ad ad = + let f = function + | AD.Addr.Addr (g,_) when g.vglob || has_escaped g -> old_access (Some g) | UnknownPtr -> old_access None - | _ -> () + | _ -> () in AD.iter f ad in - begin match lvals with + begin match ad with | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ad + on_ad ad | ad -> (* the case where the points-to set is non top and contains unknown values *) (* now we need to access all fields that might be pointed to: is this correct? *) @@ -354,9 +353,9 @@ struct if Queries.TS.exists f ts then old_access None end; - on_lvals ad + on_ad ad (* | _ -> - old_access None None *) + old_access None None *) (* TODO: what about this case? *) end; ctx.local | _ -> diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index e5839741b2..2b8ebffc61 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -20,8 +20,8 @@ struct let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) - let lock ctx rw may_fail nonzero_return_when_aquired a lv arg = - match lv with + let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = + match lv_opt with | None -> Queries.AD.iter (fun e -> ctx.split () [Events.Lock (e, rw)] diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 49bd338538..5f2905ffb1 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -15,11 +15,11 @@ struct let context _ _ = () - let check_lval tainted ((v, offset): Queries.LS.elt) = + let check_mval tainted ((v, offset): Queries.LS.elt) = if not v.vglob && VS.mem v tainted then M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v - let rem_lval tainted ((v, offset): Queries.LS.elt) = match offset with + let rem_mval tainted ((v, offset): Queries.LS.elt) = match offset with | `NoOffset -> VS.remove v tainted | _ -> tainted (* If there is an offset, it is a bit harder to remove, as we don't know where the indeterminate value is *) @@ -38,11 +38,11 @@ struct ) ctx.local ) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter ctx (_:lval option) (_:fundec) (args:exp list) : (D.t * D.t) list = if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ls e -> Queries.AD.join ls (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else @@ -82,31 +82,31 @@ struct () ) longjmp_nodes; D.join modified_locals ctx.local - | Access {lvals; kind = Read; _} -> + | Access {ad; kind = Read; _} -> (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top lvals then ( - if not (VS.is_empty octx.local) then + begin match ad with + | ad when Queries.AD.is_top ad && not (VS.is_empty octx.local) -> M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" - ) - else ( - Queries.AD.iter (function - (* Use original access state instead of current with removed written vars. *) - | Queries.AD.Addr.Addr (v,o) -> check_lval octx.local (v, ValueDomain.Offs.to_exp o) - | _ -> () - ) lvals - ); + | ad -> + Queries.AD.iter (function + (* Use original access state instead of current with removed written vars. *) + | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (v, ValueDomain.Offs.to_exp o) + | _ -> () + ) ad + end; ctx.local - | Access {lvals; kind = Write; _} -> + | Access {ad; kind = Write; _} -> (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top lvals then - ctx.local - else ( - Queries.AD.fold (fun addr acc -> - match addr with - | Queries.AD.Addr.Addr (v,o) -> rem_lval acc (v, ValueDomain.Offs.to_exp o) - | _ -> acc - ) lvals ctx.local - ) + begin match ad with + | ad when Queries.AD.is_top ad -> + ctx.local + | ad -> + Queries.AD.fold (fun addr vs -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) + | _ -> vs + ) ad ctx.local + end | _ -> ctx.local end diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index d911dafd91..f3301a4659 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -131,7 +131,7 @@ struct let event ctx e octx = match e with - | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + | Events.Access {exp=e; ad; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in let module AD = Queries.AD in @@ -151,7 +151,7 @@ struct let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls -- this is the common case if we have a sound points-to set. *) - let on_lvals ad includes_uk = + let on_ad ad includes_uk = let conf = if reach then conf - 20 else conf in let conf = if includes_uk then conf - 10 else conf in let f addr = @@ -164,10 +164,10 @@ struct in AD.iter f ad in - begin match lvals with + begin match ad with | ad when not (AD.is_top ad) -> (* the case where the points-to set is non top and does not contain unknown values *) - on_lvals ad false + on_ad ad false | ad -> (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in @@ -185,9 +185,9 @@ struct in Queries.TS.iter f ts end; - on_lvals ad !includes_uk + on_ad ad !includes_uk (* | _ -> - add_access (conf - 60) None *) + add_access (conf - 60) None *) (* TODO: what about this case? *) end; ctx.local | _ -> diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 568482631c..45a6d99d38 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -203,14 +203,13 @@ struct match q with | _ -> Queries.Result.top q - let query_lv ask exp = + let query_addrs ask exp = match ask (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> - Queries.AD.elements l + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let eval_fv ask exp: varinfo option = - match query_lv ask exp with + match query_addrs ask exp with | [addr] -> Queries.AD.Addr.to_var_may addr | _ -> None diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 3e2b41d903..4e7ec37c8a 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -27,30 +27,30 @@ struct let reachable (ask: Queries.ask) e: D.t = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let to_extra addr set = match addr with | Queries.AD.Addr.Addr (v,_) -> D.add v set | _ -> set in - Queries.AD.fold to_extra a (D.empty ()) + Queries.AD.fold to_extra ad (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | a -> - if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty a; + | ad -> + if M.tracing then M.tracel "escape" "reachable %a: %a\n" d_exp e Queries.AD.pretty ad; D.empty () let mpt (ask: Queries.ask) e: D.t = match ask.f (Queries.MayPointToA e) with - | a when not (AD.is_top a) -> - let to_extra addr set = + | ad when not (AD.is_top ad) -> + let to_extra addr set = match addr with | AD.Addr.Addr (v,_) -> D.add v set | _ -> set in - AD.fold to_extra (AD.remove UnknownPtr a) (D.empty ()) + AD.fold to_extra (AD.remove UnknownPtr ad) (D.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | a -> - if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty a; + | ad -> + if M.tracing then M.tracel "escape" "mpt %a: %a\n" d_exp e AD.pretty ad; D.empty () let thread_id ctx = diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 1f04964d58..20f5667f46 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -196,13 +196,13 @@ struct let reachable = let do_exp e = match ask.f (Queries.ReachableFromA e) with - | a when not (Queries.AD.is_top a) -> - let to_extra addr xs = - match addr with - | Queries.AD.Addr.Addr addr -> AD.of_mval addr :: xs - | _ -> xs + | ad when not (Queries.AD.is_top ad) -> + let to_extra ad ads = + match ad with + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | _ -> ads in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> [] in diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 4e28c49771..f1fcdd8a7b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -83,17 +83,17 @@ struct | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in match ctx.ask (Queries.MayPointToA lval_to_query) with - | a when not (Queries.AD.is_top a) -> - let warn_for_heap_var var = - if D.mem var state then - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" var.vname transfer_fn_name + | ad when not (Queries.AD.is_top ad) -> + let warn_for_heap_var v = + if D.mem v state then + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "lval (%s) in \"%s\" points to a maybe freed memory region" v.vname transfer_fn_name in let pointed_to_heap_vars = - Queries.AD.fold (fun addr l -> + Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> var :: l - | _ -> l - ) a [] + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | _ -> vars + ) ad [] in List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for all heap vars that the lval possibly points to *) (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) @@ -156,15 +156,15 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun acc arg -> Queries.AD.join acc (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else let reachable_vars = - let get_vars addr vs = + let get_vars addr vars = match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vs - | _ -> vs + | Queries.AD.Addr.Addr (v,_) -> v :: vars + | _ -> vars in Queries.AD.fold get_vars reachable_from_args [] in @@ -188,13 +188,13 @@ struct match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointToA ptr) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let pointed_to_heap_vars = - Queries.AD.fold (fun addr s -> + Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var, _) when ctx.ask (Queries.IsHeapVar var) -> D.add var s - | _ -> s - ) a (D.empty ()) + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsHeapVar var) -> D.add var state + | _ -> state + ) ad (D.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx); diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index edec2f40d4..628faa2ac1 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -176,7 +176,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) let pt e = ask.f (Queries.MayPointToA e) in - let bls = pt b in + let bad = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with | TPtr (t,_) -> t @@ -247,7 +247,7 @@ struct | CastE (t,e) -> addrOfExp e | _ -> None in - let lval_is_not_disjoint (v,o) als = + let lval_is_not_disjoint (v,o) aad = let rec oleq o s = match o, s with | `NoOffset, _ -> true @@ -255,21 +255,21 @@ struct | `Index (i1,o), `Index (i2,s) when exp_equal i1 i2 -> oleq o s | _ -> false in - if Queries.AD.is_top als + if Queries.AD.is_top aad then false else Queries.AD.exists (function | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) | _ -> false - ) als + ) aad in - let (als, test) = + let (aad, test) = match addrOfExp a with | None -> (Queries.AD.bot (), false) | Some e -> - let als = pt e in - (als, lval_is_not_disjoint bl als) + let aad = pt e in + (aad, lval_is_not_disjoint bl aad) in - if Queries.AD.is_top als + if Queries.AD.is_top aad then type_may_change_apt a else test || match a with @@ -295,12 +295,12 @@ struct in let r = if Cil.isConstant b then false - else if Queries.AD.is_top bls + else if Queries.AD.is_top bad then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.AD.exists (function | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) | _ -> false - ) bls + ) bad in (* if r then (Messages.warn ~category:Analyzer ~msg:("Kill " ^sprint 80 (Exp.pretty () a)^" because of "^sprint 80 (Exp.pretty () b)) (); r) @@ -345,11 +345,11 @@ struct Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> begin match ask.f (Queries.MayPointToA e) with - | ls when not (Queries.AD.is_top ls) -> + | ad when not (Queries.AD.is_top ad) -> Some (Queries.AD.exists (function - | Addr (v, _) -> is_global_var ask (Lval (var v)) = Some true + | Addr (v,_) -> is_global_var ask (Lval (var v)) = Some true | _ -> false - ) ls) + ) ad) | _ -> Some true end | CastE (t,e) -> is_global_var ask e @@ -390,8 +390,8 @@ struct let reachables ~deep (ask: Queries.ask) es = let reachable e st = let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in - let vs = ask.f q in - Queries.AD.join vs st + let ad = ask.f q in + Queries.AD.join ad st in List.fold_right reachable es (Queries.AD.empty ()) diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 78357a6d73..28b49de1a5 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,17 +281,17 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_lv (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with - | l when not (Queries.AD.is_top l) -> Queries.AD.elements l + let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] in let exp = AddrOf lval in - let xs = query_lv ask exp in (* MayPointTo -> LValSet *) - let keys = List.fold (fun l addr -> + let addrs = query_addrs ask exp in (* MayPointTo -> LValSet *) + let keys = List.fold (fun vs addr -> match addr with - | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: l - | _ -> l - ) [] xs + | Queries.AD.Addr.Addr (v,o) -> (v, ValueDomain.Offs.to_exp o) :: vs + | _ -> vs + ) [] addrs in let pretty_key k = Pretty.text (string_of_key k) in Messages.debug ~category:Analyzer "MayPointTo %a = [%a]" d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) keys; diff --git a/src/domains/events.ml b/src/domains/events.ml index 41e745ed8a..06561bddbe 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -10,7 +10,7 @@ type t = | EnterMultiThreaded | SplitBranch of exp * bool (** Used to simulate old branch-based split. *) | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) - | Access of {exp: CilType.Exp.t; lvals: Queries.AD.t; kind: AccessKind.t; reach: bool} + | Access of {exp: CilType.Exp.t; ad: Queries.AD.t; kind: AccessKind.t; reach: bool} | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp @@ -41,7 +41,7 @@ let pretty () = function | EnterMultiThreaded -> text "EnterMultiThreaded" | SplitBranch (exp, tv) -> dprintf "SplitBranch (%a, %B)" d_exp exp tv | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid - | Access {exp; lvals; kind; reach} -> dprintf "Access {exp=%a; lvals=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty lvals AccessKind.pretty kind reach + | Access {exp; ad; kind; reach} -> dprintf "Access {exp=%a; ad=%a; kind=%a; reach=%B}" CilType.Exp.pretty exp Queries.AD.pretty ad AccessKind.pretty kind reach | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp From f3c3d185c4a275e389f17a237cb117a707ebde87 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 17:01:55 +0300 Subject: [PATCH 172/238] Use MayPointToA and ReachableFromA in taindPartialContexts --- src/analyses/taintPartialContexts.ml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 76f4af8f9e..453e09b3af 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -6,12 +6,22 @@ open GoblintCil open Analyses +module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) + +let to_mvals ad = + (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) + Queries.AD.fold (fun addr mvals -> + match addr with + | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals + | _ -> mvals + ) ad (D.empty ()) + module Spec = struct include Analyses.IdentitySpec let name () = "taintPartialContexts" - module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) + module D = D module C = Lattice.Unit (* Add Lval or any Lval which it may point to to the set *) @@ -19,7 +29,7 @@ struct let d = ctx.local in (match lval with | (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d - | (Mem e, _) -> D.union (ctx.ask (Queries.MayPointTo e)) d + | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointToA e))) d ) (* this analysis is context insensitive*) @@ -84,9 +94,9 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.MayPointTo addr))) d shallow_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointToA addr)))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs in d From 22da3df00617a328b5c14b8b82456444caf8d602 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 18:58:56 +0300 Subject: [PATCH 173/238] Use MayPointToA for may_point_to in queries Co-authored-by: Simmo Saan --- src/cdomains/valueDomain.ml | 8 ++++---- src/domains/queries.ml | 2 +- src/domains/valueDomainQueries.ml | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 20c4f3bf21..83c79a542e 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -10,7 +10,7 @@ module M = Messages module BI = IntOps.BigIntOps module MutexAttr = MutexAttrDomain module VDQ = ValueDomainQueries -module LS = VDQ.LS +module AD = VDQ.AD module AddrSetDomain = SetDomain.ToppedSet(Addr)(struct let topname = "All" end) module ArrIdxDomain = IndexDomain @@ -756,9 +756,9 @@ struct match exp, start_of_array_lval with | BinOp(IndexPI, Lval lval, add, _), (Var arr_start_var, NoOffset) when not (contains_pointer add) -> begin match ask.may_point_to (Lval lval) with - | v when LS.cardinal v = 1 && not (LS.is_top v) -> - begin match LS.choose v with - | (var,`Index (i,`NoOffset)) when Cil.isZero (Cil.constFold true i) && CilType.Varinfo.equal var arr_start_var -> + | v when AD.cardinal v = 1 && not (AD.is_top v) -> + begin match AD.choose v with + | AD.Addr.Addr (var,`Index (i,`NoOffset)) when ID.equal_to Z.zero i = `Eq && CilType.Varinfo.equal var arr_start_var -> (* The idea here is that if a must(!) point to arr and we do sth like a[i] we don't want arr to be partitioned according to (arr+i)-&a but according to i instead *) add | _ -> BinOp(MinusPP, exp, StartOf start_of_array_lval, !ptrdiffType) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 42be039d96..10e9c996da 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -474,7 +474,7 @@ end let to_value_domain_ask (ask: ask) = let eval_int e = ask.f (EvalInt e) in - let may_point_to e = ask.f (MayPointTo e) in + let may_point_to e = ask.f (MayPointToA e) in let is_multiple v = ask.f (IsMultiple v) in { VDQ.eval_int; may_point_to; is_multiple } diff --git a/src/domains/valueDomainQueries.ml b/src/domains/valueDomainQueries.ml index d366e6dda3..8266582ac2 100644 --- a/src/domains/valueDomainQueries.ml +++ b/src/domains/valueDomainQueries.ml @@ -4,6 +4,7 @@ open GoblintCil open BoolDomain module LS = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end) +module AD = PreValueDomain.AD module ID = struct @@ -44,7 +45,7 @@ struct end type eval_int = exp -> ID.t -type may_point_to = exp -> LS.t +type may_point_to = exp -> AD.t type is_multiple = varinfo -> bool (** Subset of queries used by the valuedomain, using a simpler representation. *) From 5cf96d1cf863b21bc258008d5728ff495b7c12d5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:02:44 +0300 Subject: [PATCH 174/238] Remove MayPointTo and ReachableFrom queries with LS --- src/analyses/base.ml | 24 ------------------------ src/domains/queries.ml | 18 ++---------------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a4a1262589..407a3b5965 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1279,16 +1279,6 @@ struct ) | _ -> Queries.Result.top q end - | Q.MayPointTo e -> begin - match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with - | Address a -> - let s = addrToLvalSet a in - if AD.mem Addr.UnknownPtr a - then Q.LS.add (dummyFunDec.svar, `NoOffset) s - else s - | Bot -> Queries.Result.bot q (* TODO: remove *) - | _ -> Queries.Result.top q - end | Q.MayPointToA e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address a -> a @@ -1303,20 +1293,6 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end - | Q.ReachableFrom e -> begin - match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with - | Top -> Queries.Result.top q - | Bot -> Queries.Result.bot q (* TODO: remove *) - | Address a -> - let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe *) - let xs = List.map addrToLvalSet (reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local) in - let addrs = List.fold_left (Q.LS.join) (Q.LS.empty ()) xs in - if AD.mem Addr.UnknownPtr a then - Q.LS.add (dummyFunDec.svar, `NoOffset) addrs (* add unknown back *) - else - addrs - | _ -> Q.LS.empty () - end | Q.ReachableFromA e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 10e9c996da..6f5838c19b 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -72,9 +72,7 @@ type invariant_context = Invariant.context = { (** GADT for queries with specific result type. *) type _ t = | EqualSet: exp -> ES.t t - | MayPointTo: exp -> LS.t t | MayPointToA: exp -> AD.t t - | ReachableFrom: exp -> LS.t t | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -143,9 +141,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> (module ES) | CondVars _ -> (module ES) - | MayPointTo _ -> (module LS) | MayPointToA _ -> (module AD) - | ReachableFrom _ -> (module LS) | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -209,9 +205,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> ES.top () | CondVars _ -> ES.top () - | MayPointTo _ -> LS.top () | MayPointToA _ -> AD.top () - | ReachableFrom _ -> LS.top () | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -271,10 +265,8 @@ struct (* deriving ord doesn't work for GADTs (t and any_query) so this must be done manually... *) let order = function | Any (EqualSet _) -> 0 - | Any (MayPointTo _) -> 1 - | Any (MayPointToA _) -> 999 - | Any (ReachableFrom _) -> 2 - | Any (ReachableFromA _) -> 666 + | Any (MayPointToA _) -> 1 + | Any (ReachableFromA _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -329,9 +321,7 @@ struct else match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 - | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 - | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -376,9 +366,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e - | Any (MayPointTo e) -> CilType.Exp.hash e | Any (MayPointToA e) -> CilType.Exp.hash e - | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -420,9 +408,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e - | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e - | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e From 9291c8af396cdd6176a5710e3dd7de4810b5588d Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:06:39 +0300 Subject: [PATCH 175/238] Rename MayPointToA -> MayPointTo --- src/analyses/accessAnalysis.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 6 +++--- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/fileUse.ml | 6 +++--- src/analyses/mallocFresh.ml | 2 +- src/analyses/malloc_null.ml | 6 +++--- src/analyses/memLeak.ml | 2 +- src/analyses/mutexEventsAnalysis.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 2 +- src/analyses/pthreadSignals.ml | 2 +- src/analyses/spec.ml | 2 +- src/analyses/taintPartialContexts.ml | 4 ++-- src/analyses/threadEscape.ml | 2 +- src/analyses/uninit.ml | 4 ++-- src/analyses/useAfterFree.ml | 4 ++-- src/analyses/varEq.ml | 8 ++++---- src/cdomains/mvalMapDomain.ml | 2 +- src/domains/queries.ml | 16 ++++++++-------- 20 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index cf024afb10..b13b7bf3ea 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointToA e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointTo e in let ad = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; ad; kind; reach}) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c096ba8e6c..ef035d3cca 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -158,7 +158,7 @@ struct {st' with rel = rel''} ) | (Mem v, NoOffset) -> - begin match ask.f (Queries.MayPointToA v) with + begin match ask.f (Queries.MayPointTo v) with | ad when Queries.AD.is_top ad -> st | ad -> let mvals = Queries.AD.to_mval ad in @@ -216,7 +216,7 @@ struct | CastE (t,e) -> CastE (t, inner e) | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> - begin match ask (Queries.MayPointToA e) with + begin match ask (Queries.MayPointTo e) with | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval @@ -534,7 +534,7 @@ struct | None -> st) | _, _ -> let lvallist e = - match ask.f (Queries.MayPointToA e) with + match ask.f (Queries.MayPointTo e) with | ad when Queries.AD.is_top ad -> [] | ad -> Queries.AD.to_mval ad diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 407a3b5965..63a9de1113 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1279,7 +1279,7 @@ struct ) | _ -> Queries.Result.top q end - | Q.MayPointToA e -> begin + | Q.MayPointTo e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address a -> a | Bot -> Queries.Result.bot q (* TODO: remove *) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 5f50d3968d..9fdc142f9a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,7 +64,7 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointToA exp) with + match ctx.ask (Queries.MayPointTo exp) with | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> let a' = if Queries.AD.mem UnknownPtr a then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 9c08269058..a4961b8dc9 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,7 +877,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let ad = ctx.ask (Queries.MayPointToA exp) in + let ad = ctx.ask (Queries.MayPointTo exp) in if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then if Queries.AD.mem UnknownPtr ad then (* UNSOUND *) diff --git a/src/analyses/fileUse.ml b/src/analyses/fileUse.ml index d4635722b9..a9088a4bb2 100644 --- a/src/analyses/fileUse.ml +++ b/src/analyses/fileUse.ml @@ -24,11 +24,11 @@ struct (* queries *) let query ctx (type a) (q: a Queries.t) = match q with - | Queries.MayPointToA exp -> if M.tracing then M.tracel "file" "query MayPointToA: %a" d_plainexp exp; Queries.Result.top q + | Queries.MayPointTo exp -> if M.tracing then M.tracel "file" "query MayPointTo: %a" d_plainexp exp; Queries.Result.top q | _ -> Queries.Result.top q let query_ad (ask: Queries.ask) exp = - match ask.f (Queries.MayPointToA exp) with + match ask.f (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] let print_query_lv ?msg:(msg="") ask exp = @@ -36,7 +36,7 @@ struct let pretty_key = function | Queries.AD.Addr.Addr (v,o) -> Pretty.text (D.string_of_key (v, ValueDomain.Addr.Offs.to_exp o)) | _ -> Pretty.text "" in - if M.tracing then M.tracel "file" "%s MayPointToA %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs + if M.tracing then M.tracel "file" "%s MayPointTo %a = [%a]" msg d_exp exp (Pretty.docList ~sep:(Pretty.text ", ") pretty_key) addrs let eval_fv ask exp: varinfo option = match query_ad ask exp with diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 3bea7b8c73..2c2b99a075 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -18,7 +18,7 @@ struct let exitstate _ = D.empty () let assign_lval (ask: Queries.ask) lval local = - match ask.f (MayPointToA (AddrOf lval)) with + match ask.f (MayPointTo (AddrOf lval)) with | ad when Queries.AD.is_top ad -> D.empty () | ad when Queries.AD.exists (function | Queries.AD.Addr.Addr (v,_) -> not (D.mem v local) && (v.vglob || ThreadEscape.has_escaped ask v) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index feb1d97a51..e211fad98e 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -37,7 +37,7 @@ struct with SetDomain.Unsupported _ -> () end;*) match e with | Lval (Var v, offs) -> - begin match a.f (Queries.MayPointToA (mkAddrOf (Var v,offs))) with + begin match a.f (Queries.MayPointTo (mkAddrOf (Var v,offs))) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.iter (function | Queries.AD.Addr.Addr mval -> warn_lval st mval @@ -118,7 +118,7 @@ struct else D.filter (fun x -> AD.mem x vars) st let get_concrete_lval (ask: Queries.ask) (lval:lval) = - match ask.f (Queries.MayPointToA (mkAddrOf lval)) with + match ask.f (Queries.MayPointTo (mkAddrOf lval)) with | ad when Queries.AD.cardinal ad = 1 && not (Queries.AD.mem UnknownPtr ad) -> Queries.AD.Addr.to_mval (Queries.AD.choose ad) | _ -> None @@ -130,7 +130,7 @@ struct | _ -> None let might_be_null (ask: Queries.ask) lv gl st = - match ask.f (Queries.MayPointToA (mkAddrOf lv)) with + match ask.f (Queries.MayPointTo (mkAddrOf lv)) with | ad when not (Queries.AD.is_top ad) -> let one_addr_might = function | Queries.AD.Addr.Addr mval -> diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 660d9ba591..0bc2196aff 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -49,7 +49,7 @@ struct | _ -> state end | Free ptr -> - begin match ctx.ask (Queries.MayPointToA ptr) with + begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) let unique_pointed_to_heap_vars = diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 2b8ebffc61..162527b32b 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -18,7 +18,7 @@ struct include UnitAnalysis.Spec let name () = "mutexEvents" - let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointToA exp) + let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointTo exp) let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = match lv_opt with diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 815b3fdb67..4d9de5f9c4 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -54,7 +54,7 @@ struct match desc.special arglist with | MutexInit {mutex = mutex; attr = attr} -> let attr = ctx.ask (Queries.EvalMutexAttr attr) in - let mutexes = ctx.ask (Queries.MayPointToA mutex) in + let mutexes = ctx.ask (Queries.MayPointTo mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) Queries.AD.iter (function addr -> match addr with diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index f5d0e2c9d0..d515820514 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,7 +17,7 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointToA exp)) + let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointTo exp)) let possible_vinfos a cv_arg = List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) diff --git a/src/analyses/spec.ml b/src/analyses/spec.ml index 45a6d99d38..54ffcd2697 100644 --- a/src/analyses/spec.ml +++ b/src/analyses/spec.ml @@ -204,7 +204,7 @@ struct | _ -> Queries.Result.top q let query_addrs ask exp = - match ask (Queries.MayPointToA exp) with + match ask (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 453e09b3af..df0c87f34a 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -29,7 +29,7 @@ struct let d = ctx.local in (match lval with | (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d - | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointToA e))) d + | (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointTo e))) d ) (* this analysis is context insensitive*) @@ -94,7 +94,7 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointToA addr)))) d shallow_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs in let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs in diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 4e7ec37c8a..738a2b66ae 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -40,7 +40,7 @@ struct D.empty () let mpt (ask: Queries.ask) e: D.t = - match ask.f (Queries.MayPointToA e) with + match ask.f (Queries.MayPointTo e) with | ad when not (AD.is_top ad) -> let to_extra addr set = match addr with diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 20f5667f46..3ad3de6afa 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -31,7 +31,7 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = - match ask.f (Queries.MayPointToA (AddrOf lv)) with + match ask.f (Queries.MayPointTo (AddrOf lv)) with | a when not (Queries.AD.is_top a) -> let to_extra addr xs = match addr with @@ -168,7 +168,7 @@ struct let init_vo (v: varinfo) (ofs: lval_offs) : D.t = List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in - match a.f (Queries.MayPointToA (AddrOf lv)) with + match a.f (Queries.MayPointTo (AddrOf lv)) with | a when Queries.AD.cardinal a = 1 -> begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with | Some (var, ofs) -> init_vo var ofs diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f1fcdd8a7b..fc8b8aba1b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,7 +82,7 @@ struct | Var _ -> Lval lval | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - match ctx.ask (Queries.MayPointToA lval_to_query) with + match ctx.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = if D.mem v state then @@ -187,7 +187,7 @@ struct let desc = LibraryFunctions.find f in match desc.special arglist with | Free ptr -> - begin match ctx.ask (Queries.MayPointToA ptr) with + begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) -> let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 628faa2ac1..038ad345d9 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -137,7 +137,7 @@ struct (* TODO: why unused? how different from below? *) let may_change_pt ask (b:exp) (a:exp) : bool = - let pt e = ask (Queries.MayPointToA e) in + let pt e = ask (Queries.MayPointTo e) in let rec lval_may_change_pt a bl : bool = let rec may_change_pt_offset o = match o with @@ -175,7 +175,7 @@ struct let may_change (ask: Queries.ask) (b:exp) (a:exp) : bool = (*b should be an address of something that changes*) - let pt e = ask.f (Queries.MayPointToA e) in + let pt e = ask.f (Queries.MayPointTo e) in let bad = pt b in let bt = match unrollTypeDeep (Cilfacade.typeOf b) with @@ -344,7 +344,7 @@ struct | Lval (Var v,_) -> Some (v.vglob || (ask.f (Queries.IsMultiple v) || BaseUtil.is_global ask v)) | Lval (Mem e, _) -> - begin match ask.f (Queries.MayPointToA e) with + begin match ask.f (Queries.MayPointTo e) with | ad when not (Queries.AD.is_top ad) -> Some (Queries.AD.exists (function | Addr (v,_) -> is_global_var ask (Lval (var v)) = Some true @@ -389,7 +389,7 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - let q = if deep then Queries.ReachableFromA e else Queries.MayPointToA e in + let q = if deep then Queries.ReachableFromA e else Queries.MayPointTo e in let ad = ask.f q in Queries.AD.join ad st in diff --git a/src/cdomains/mvalMapDomain.ml b/src/cdomains/mvalMapDomain.ml index 28b49de1a5..d0d2f8da85 100644 --- a/src/cdomains/mvalMapDomain.ml +++ b/src/cdomains/mvalMapDomain.ml @@ -281,7 +281,7 @@ struct let keys_from_lval lval (ask: Queries.ask) = (* use MayPointTo query to get all possible pointees of &lval *) (* print_query_lv ctx.ask (AddrOf lval); *) - let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointToA exp) with + let query_addrs (ask: Queries.ask) exp = match ask.f (Queries.MayPointTo exp) with | ad when not (Queries.AD.is_top ad) -> Queries.AD.elements ad | _ -> [] in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 6f5838c19b..3586e304df 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -72,7 +72,7 @@ type invariant_context = Invariant.context = { (** GADT for queries with specific result type. *) type _ t = | EqualSet: exp -> ES.t t - | MayPointToA: exp -> AD.t t + | MayPointTo: exp -> AD.t t | ReachableFromA: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t @@ -141,7 +141,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> (module ES) | CondVars _ -> (module ES) - | MayPointToA _ -> (module AD) + | MayPointTo _ -> (module AD) | ReachableFromA _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) @@ -205,7 +205,7 @@ struct (* Cannot group these GADTs... *) | EqualSet _ -> ES.top () | CondVars _ -> ES.top () - | MayPointToA _ -> AD.top () + | MayPointTo _ -> AD.top () | ReachableFromA _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () @@ -265,7 +265,7 @@ struct (* deriving ord doesn't work for GADTs (t and any_query) so this must be done manually... *) let order = function | Any (EqualSet _) -> 0 - | Any (MayPointToA _) -> 1 + | Any (MayPointTo _) -> 1 | Any (ReachableFromA _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 @@ -321,7 +321,7 @@ struct else match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 - | Any (MayPointToA e1), Any (MayPointToA e2) -> CilType.Exp.compare e1 e2 + | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 @@ -366,7 +366,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e - | Any (MayPointToA e) -> CilType.Exp.hash e + | Any (MayPointTo e) -> CilType.Exp.hash e | Any (ReachableFromA e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e @@ -408,7 +408,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e - | Any (MayPointToA e) -> Pretty.dprintf "MayPointToA %a" CilType.Exp.pretty e + | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e @@ -460,7 +460,7 @@ end let to_value_domain_ask (ask: ask) = let eval_int e = ask.f (EvalInt e) in - let may_point_to e = ask.f (MayPointToA e) in + let may_point_to e = ask.f (MayPointTo e) in let is_multiple v = ask.f (IsMultiple v) in { VDQ.eval_int; may_point_to; is_multiple } From 43223d09d59f5ae89a1f2f0fde8b009f3e29b9f4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:09:28 +0300 Subject: [PATCH 176/238] Rename ReachableFromA -> ReachableFrom --- src/analyses/accessAnalysis.ml | 2 +- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- src/analyses/base.ml | 2 +- src/analyses/extractPthread.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/poisonVariables.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/threadEscape.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/useAfterFree.ml | 2 +- src/analyses/varEq.ml | 2 +- src/domains/queries.ml | 14 +++++++------- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index b13b7bf3ea..e99aefa0e5 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -33,7 +33,7 @@ struct let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B\n" d_exp e AccessKind.pretty kind reach; - let reach_or_mpt: _ Queries.t = if reach then ReachableFromA e else MayPointTo e in + let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in let ad = ctx.ask reach_or_mpt in ctx.emit (Access {exp=e; ad; kind; reach}) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ef035d3cca..31d697c881 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -275,7 +275,7 @@ struct let reachable_from_args ctx args = let to_vs e = - ctx.ask (ReachableFromA e) + ctx.ask (ReachableFrom e) |> LockDomain.MayLocksetNoRW.to_var_may |> VS.of_list in @@ -453,7 +453,7 @@ struct match st with | None -> None | Some st -> - let ad = ask.f (Queries.ReachableFromA e) in + let ad = ask.f (Queries.ReachableFrom e) in if Queries.AD.is_top ad then None else diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 63a9de1113..e9a0580098 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1293,7 +1293,7 @@ struct | Bot -> Queries.Result.bot q (* TODO: remove *) | _ -> Queries.Result.top q end - | Q.ReachableFromA e -> begin + | Q.ReachableFrom e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Top -> Queries.Result.top q | Bot -> Queries.Result.bot q (* TODO: remove *) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index a4961b8dc9..ce8471c662 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -1117,7 +1117,7 @@ module Spec : Analyses.MCPSpec = struct match (LibraryFunctions.find f).special arglist', f.vname, arglist with | ThreadCreate { thread; start_routine = func; _ }, _, _ -> let funs_ad = - let ad = ctx.ask (Queries.ReachableFromA func) in + let ad = ctx.ask (Queries.ReachableFrom func) in Queries.AD.filter (function | Queries.AD.Addr.Addr mval -> diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index e211fad98e..dca158c973 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -95,7 +95,7 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra addr ads = match addr with diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index 5f2905ffb1..f69e5b2fbc 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -42,7 +42,7 @@ struct if VS.is_empty ctx.local then [ctx.local,ctx.local] else ( - let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFromA e))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFrom e))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then [ctx.local, ctx.local] else diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index df0c87f34a..d4ea17d2e0 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -96,7 +96,7 @@ struct in let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFromA addr)))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFrom addr)))) d deep_addrs in d diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 738a2b66ae..9ed62e7422 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -26,7 +26,7 @@ struct module G = ThreadIdSet let reachable (ask: Queries.ask) e: D.t = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra addr set = match addr with diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 3ad3de6afa..58fd5fc008 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -195,7 +195,7 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = let do_exp e = - match ask.f (Queries.ReachableFromA e) with + match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> let to_extra ad ads = match ad with diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index fc8b8aba1b..f828e17e2b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,7 +156,7 @@ struct if D.is_empty caller_state then [caller_state, caller_state] else ( - let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFromA arg))) (Queries.AD.empty ()) args in + let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 038ad345d9..77823d99d7 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -389,7 +389,7 @@ struct (* Give the set of reachables from argument. *) let reachables ~deep (ask: Queries.ask) es = let reachable e st = - let q = if deep then Queries.ReachableFromA e else Queries.MayPointTo e in + let q = if deep then Queries.ReachableFrom e else Queries.MayPointTo e in let ad = ask.f q in Queries.AD.join ad st in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 3586e304df..0388ce2995 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -73,7 +73,7 @@ type invariant_context = Invariant.context = { type _ t = | EqualSet: exp -> ES.t t | MayPointTo: exp -> AD.t t - | ReachableFromA: exp -> AD.t t + | ReachableFrom: exp -> AD.t t | ReachableUkTypes: exp -> TS.t t | Regions: exp -> LS.t t | MayEscape: varinfo -> MayBool.t t @@ -142,7 +142,7 @@ struct | EqualSet _ -> (module ES) | CondVars _ -> (module ES) | MayPointTo _ -> (module AD) - | ReachableFromA _ -> (module AD) + | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) | EvalFunvar _ -> (module LS) @@ -206,7 +206,7 @@ struct | EqualSet _ -> ES.top () | CondVars _ -> ES.top () | MayPointTo _ -> AD.top () - | ReachableFromA _ -> AD.top () + | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () | EvalFunvar _ -> LS.top () @@ -266,7 +266,7 @@ struct let order = function | Any (EqualSet _) -> 0 | Any (MayPointTo _) -> 1 - | Any (ReachableFromA _) -> 2 + | Any (ReachableFrom _) -> 2 | Any (ReachableUkTypes _) -> 3 | Any (Regions _) -> 4 | Any (MayEscape _) -> 5 @@ -322,7 +322,7 @@ struct match a, b with | Any (EqualSet e1), Any (EqualSet e2) -> CilType.Exp.compare e1 e2 | Any (MayPointTo e1), Any (MayPointTo e2) -> CilType.Exp.compare e1 e2 - | Any (ReachableFromA e1), Any (ReachableFromA e2) -> CilType.Exp.compare e1 e2 + | Any (ReachableFrom e1), Any (ReachableFrom e2) -> CilType.Exp.compare e1 e2 | Any (ReachableUkTypes e1), Any (ReachableUkTypes e2) -> CilType.Exp.compare e1 e2 | Any (Regions e1), Any (Regions e2) -> CilType.Exp.compare e1 e2 | Any (MayEscape vi1), Any (MayEscape vi2) -> CilType.Varinfo.compare vi1 vi2 @@ -367,7 +367,7 @@ struct let rec hash_arg = function | Any (EqualSet e) -> CilType.Exp.hash e | Any (MayPointTo e) -> CilType.Exp.hash e - | Any (ReachableFromA e) -> CilType.Exp.hash e + | Any (ReachableFrom e) -> CilType.Exp.hash e | Any (ReachableUkTypes e) -> CilType.Exp.hash e | Any (Regions e) -> CilType.Exp.hash e | Any (MayEscape vi) -> CilType.Varinfo.hash vi @@ -409,7 +409,7 @@ struct let rec pretty () = function | Any (EqualSet e) -> Pretty.dprintf "EqualSet %a" CilType.Exp.pretty e | Any (MayPointTo e) -> Pretty.dprintf "MayPointTo %a" CilType.Exp.pretty e - | Any (ReachableFromA e) -> Pretty.dprintf "ReachableFromA %a" CilType.Exp.pretty e + | Any (ReachableFrom e) -> Pretty.dprintf "ReachableFrom %a" CilType.Exp.pretty e | Any (ReachableUkTypes e) -> Pretty.dprintf "ReachableUkTypes %a" CilType.Exp.pretty e | Any (Regions e) -> Pretty.dprintf "Regions %a" CilType.Exp.pretty e | Any (MayEscape vi) -> Pretty.dprintf "MayEscape %a" CilType.Varinfo.pretty vi From 866f5172aa486db87986815dd009bef6f5185e70 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:35:42 +0300 Subject: [PATCH 177/238] Rename a -> ad, where missed --- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- src/analyses/condVars.ml | 8 ++++---- src/analyses/uninit.ml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 31d697c881..ca60e5dc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -217,8 +217,8 @@ struct | Lval (Var v, off) -> Lval (Var v, off) | Lval (Mem e, NoOffset) -> begin match ask (Queries.MayPointTo e) with - | a when not (Queries.AD.is_top a) && (Queries.AD.cardinal a) = 1 -> - begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | ad when not (Queries.AD.is_top ad) && (Queries.AD.cardinal ad) = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose ad) with | Some mval -> ValueDomain.Addr.Mval.to_cil_exp mval | None -> Lval (Mem e, NoOffset) end diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 9fdc142f9a..55627941ba 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -65,11 +65,11 @@ struct let mayPointTo ctx exp = match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a > 0 -> - let a' = if Queries.AD.mem UnknownPtr a then ( + | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad > 0 -> + let a' = if Queries.AD.mem UnknownPtr ad then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.AD.remove UnknownPtr a - ) else a + Queries.AD.remove UnknownPtr ad + ) else ad in List.filter_map (function | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 58fd5fc008..c14b1034a0 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -32,13 +32,13 @@ struct (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = match ask.f (Queries.MayPointTo (AddrOf lv)) with - | a when not (Queries.AD.is_top a) -> + | ad when not (Queries.AD.is_top ad) -> let to_extra addr xs = match addr with | Queries.AD.Addr.Addr (v,o) -> (v, o, write) :: xs | _ -> xs in - Queries.AD.fold to_extra a [] + Queries.AD.fold to_extra ad [] | _ -> M.info ~category:Unsound "Access to unknown address could be global"; [] @@ -169,8 +169,8 @@ struct List.fold_right remove_if_prefix (get_pfx v `NoOffset ofs v.vtype v.vtype) st in match a.f (Queries.MayPointTo (AddrOf lv)) with - | a when Queries.AD.cardinal a = 1 -> - begin match Queries.AD.Addr.to_mval (Queries.AD.choose a) with + | ad when Queries.AD.cardinal ad = 1 -> + begin match Queries.AD.Addr.to_mval (Queries.AD.choose ad) with | Some (var, ofs) -> init_vo var ofs | None -> st end From 57549d0ee291fd88a54b3315bc3f5078e2426e72 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 19:37:55 +0300 Subject: [PATCH 178/238] Simplify cardinal > 0 to not is_empty --- src/analyses/condVars.ml | 2 +- src/analyses/extractPthread.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 55627941ba..04246bf28a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -65,7 +65,7 @@ struct let mayPointTo ctx exp = match ctx.ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad > 0 -> + | ad when not (Queries.AD.is_top ad) && not (Queries.AD.is_empty ad) -> let a' = if Queries.AD.mem UnknownPtr ad then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) Queries.AD.remove UnknownPtr ad diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index ce8471c662..211b88fd6e 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -878,7 +878,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = let ad = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad > 0 then + if (not (Queries.AD.is_top ad)) && not (Queries.AD.is_empty ad) then if Queries.AD.mem UnknownPtr ad then (* UNSOUND *) Queries.AD.remove UnknownPtr ad From 3cbc8e7db66d5373249eaac2b64b2a9f10a8aef3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 13:43:58 +0300 Subject: [PATCH 179/238] Remove done TODO from uninit --- src/analyses/uninit.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index c14b1034a0..9bd8db2fe5 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -29,7 +29,6 @@ struct let threadspawn ctx lval f args fctx = ctx.local let exitstate v : D.t = D.empty () - (* TODO: Use AddressDomain for queries *) let access_address (ask: Queries.ask) write lv = match ask.f (Queries.MayPointTo (AddrOf lv)) with | ad when not (Queries.AD.is_top ad) -> From 6bfb97b845fe544d3f6c0aebec1ea8da4aecadf9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 14:42:32 +0300 Subject: [PATCH 180/238] Use AddressDomain for queries in base --- src/analyses/base.ml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e9a0580098..50729de29e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -611,16 +611,6 @@ struct %> 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 - (* TODO: Use AddressDomain for queries *) - let convertToQueryLval = function - | ValueDomain.AD.Addr.Addr (v,o) -> [v, Addr.Offs.to_exp o] - | _ -> [] - - let addrToLvalSet a = - let add x y = Q.LS.add y x in - try - AD.fold (fun e c -> List.fold_left add c (convertToQueryLval e)) a (Q.LS.empty ()) - with SetDomain.Unsupported _ -> Q.LS.top () let reachable_top_pointers_types ctx (ps: AD.t) : Queries.TS.t = let module TS = Queries.TS in @@ -1261,14 +1251,15 @@ struct (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> - let s = addrToLvalSet a in let has_offset = function | `NoOffset -> false | `Field _ | `Index _ -> true in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o) s then + if Q.AD.exists (function + | Q.AD.Addr.Addr (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o + | _ -> false) a then Queries.Result.bot q else ( let r = get ~full:true (Analyses.ask_of_ctx ctx) ctx.global ctx.local a None in @@ -1328,7 +1319,8 @@ struct (* ignore @@ printf "EvalStr Address: %a -> %s (must %i, may %i)\n" d_plainexp e (VD.short 80 (Address a)) (List.length @@ AD.to_var_must a) (List.length @@ AD.to_var_may a); *) begin match unrollType (Cilfacade.typeOf e) with | TPtr(TInt(IChar, _), _) -> - let lval = Mval.Exp.to_cil @@ Q.LS.choose @@ addrToLvalSet a in + let (v,o) = List.hd (AD.to_mval a) in + let lval = Mval.Exp.to_cil @@ (v, Addr.Offs.to_exp o) in (try `Lifted (Bytes.to_string (Hashtbl.find char_array lval)) with Not_found -> Queries.Result.top q) | _ -> (* what about ISChar and IUChar? *) @@ -2006,12 +1998,15 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = + let has_non_heap_var = Q.AD.exists (function + | Q.AD.Addr.Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + | _ -> false) + in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - let points_to_set = addrToLvalSet a in - if Q.LS.is_top points_to_set then + if Q.AD.is_top a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then + else if has_non_heap_var a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 593822588ee466c72ae93aee485bf52074878b4f Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 15:02:08 +0300 Subject: [PATCH 181/238] Remove unneccessary module calls before AD and Addr in base --- src/analyses/base.ml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 50729de29e..4183261ddb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -108,7 +108,7 @@ struct | (info,(value:VD.t))::xs -> match value with | Address t when hasAttribute "goblint_array_domain" info.vattr -> - let possibleVars = List.to_seq (PreValueDomain.AD.to_var_may t) in + let possibleVars = List.to_seq (AD.to_var_may t) in Seq.fold_left (fun map arr -> VarMap.add arr (info.vattr) map) (pointedArrayMap xs) @@ Seq.filter (fun info -> isArrayType info.vtype) possibleVars | _ -> pointedArrayMap xs in @@ -345,7 +345,7 @@ struct if AD.is_definite x && AD.is_definite y then let ax = AD.choose x in let ay = AD.choose y in - let handle_address_is_multiple addr = begin match AD.Addr.to_var addr with + let handle_address_is_multiple addr = begin match Addr.to_var addr with | Some v when a.f (Q.IsMultiple v) -> if M.tracing then M.tracel "addr" "IsMultiple %a\n" CilType.Varinfo.pretty v; None @@ -353,7 +353,7 @@ struct Some true end in - match AD.Addr.semantic_equal ax ay with + match Addr.semantic_equal ax ay with | Some true -> if M.tracing then M.tracel "addr" "semantic_equal %a %a\n" AD.pretty x AD.pretty y; handle_address_is_multiple ax @@ -591,7 +591,7 @@ struct | Struct n -> Struct (ValueDomain.Structs.map replace_val n) | Union (f,v) -> Union (f,replace_val v) | Blob (n,s,o) -> Blob (replace_val n,s,o) - | Address x -> Address (ValueDomain.AD.map ValueDomain.Addr.top_indices x) + | Address x -> Address (AD.map ValueDomain.Addr.top_indices x) | x -> x in CPA.map replace_val st @@ -1257,8 +1257,8 @@ struct | `Index _ -> true in (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) - if Q.AD.exists (function - | Q.AD.Addr.Addr (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o + if AD.exists (function + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || has_offset o | _ -> false) a then Queries.Result.bot q else ( @@ -1291,8 +1291,8 @@ struct | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) let addrs = reachable_vars (Analyses.ask_of_ctx ctx) [a'] ctx.global ctx.local in - List.fold_left (Q.AD.join) (Q.AD.empty ()) addrs - | _ -> Q.AD.empty () + List.fold_left (AD.join) (AD.empty ()) addrs + | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with @@ -1998,13 +1998,13 @@ struct invalidate ~deep:true ~ctx (Analyses.ask_of_ctx ctx) gs st' deep_addrs let check_free_of_non_heap_mem ctx special_fn ptr = - let has_non_heap_var = Q.AD.exists (function - | Q.AD.Addr.Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + let has_non_heap_var = AD.exists (function + | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) | _ -> false) in match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local ptr with | Address a -> - if Q.AD.is_top a then + if AD.is_top a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname else if has_non_heap_var a then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr From b55afa6a6fdafab6f9a9b3c4502d3d2e2d15e9d9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Sep 2023 13:05:30 +0300 Subject: [PATCH 182/238] Fix semgrep 1.38 compatibility --- .github/workflows/semgrep.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index acd696e597..b46713d510 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v3 - name: Run semgrep - run: semgrep scan --sarif --output=semgrep.sarif + run: semgrep scan --config .semgrep/ --sarif > semgrep.sarif - name: Upload SARIF file to GitHub Advanced Security Dashboard uses: github/codeql-action/upload-sarif@v2 From fc67853d42ce48745443adc9768e3d4fe14f76d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:41:25 +0000 Subject: [PATCH 183/238] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/coverage.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/indentation.yml | 2 +- .github/workflows/locked.yml | 6 +++--- .github/workflows/metadata.yml | 4 ++-- .github/workflows/options.yml | 2 +- .github/workflows/semgrep.yml | 2 +- .github/workflows/unlocked.yml | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7472cbc820..5635ebbeea 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 02c5f07d90..1ef104db29 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aa69baf958..e1648904c3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check for undocumented modules run: python scripts/goblint-lib-modules.py diff --git a/.github/workflows/indentation.yml b/.github/workflows/indentation.yml index 14db288d60..e22e674301 100644 --- a/.github/workflows/indentation.yml +++ b/.github/workflows/indentation.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 358682a2f3..007ea34619 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: @@ -101,7 +101,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: @@ -141,7 +141,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} env: diff --git a/.github/workflows/metadata.yml b/.github/workflows/metadata.yml index da20c6b675..1092606bc6 100644 --- a/.github/workflows/metadata.yml +++ b/.github/workflows/metadata.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Validate CITATION.cff uses: docker://citationcff/cffconvert:latest @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/.github/workflows/options.yml b/.github/workflows/options.yml index b8522c03bb..b5f690a700 100644 --- a/.github/workflows/options.yml +++ b/.github/workflows/options.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index b46713d510..bd2dfd285c 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run semgrep run: semgrep scan --config .semgrep/ --sarif > semgrep.sarif diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 2bec6b72fb..15cd9138d8 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -45,7 +45,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 @@ -131,7 +131,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 @@ -208,7 +208,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action @@ -246,7 +246,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v2 From d97504bf08c8f79e04539665432ad05ffd843523 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Sep 2023 16:13:29 +0300 Subject: [PATCH 184/238] Fix YAML witness unassume indentation (PR #1124) --- src/analyses/base.ml | 6 +++-- src/witness/witness.ml | 58 +++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4a49b9eed0..b74555b7ad 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2297,8 +2297,10 @@ struct let sizeval = eval_int (Analyses.ask_of_ctx ctx) gs st size in let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in if ID.to_int countval = Some Z.one then ( - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)))] + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ + add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false); + eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)) + ] ) else ( let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in diff --git a/src/witness/witness.ml b/src/witness/witness.ml index f73a2755c8..baa465a1b3 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -306,35 +306,35 @@ struct let determine_result entrystates (module Task:Task): (module WitnessTaskResult) = let module Arg: BiArgInvariant = (val if GobConfig.get_bool "witness.enabled" then ( - let module Arg = (val ArgTool.create entrystates) in - let module Arg = - struct - include Arg - - let find_invariant (n, c, i) = - let context = {Invariant.default_context with path = Some i} in - ask_local (n, c) (Invariant context) - end - in - (module Arg: BiArgInvariant) - ) - else ( - let module Arg = - struct - module Node = ArgTool.Node - module Edge = MyARG.InlineEdge - let next _ = [] - let prev _ = [] - let find_invariant _ = Invariant.none - let main_entry = - let lvar = WitnessUtil.find_main_entry entrystates in - (fst lvar, snd lvar, -1) - let iter_nodes f = f main_entry - let query _ q = Queries.Result.top q - end - in - (module Arg: BiArgInvariant) - ) + let module Arg = (val ArgTool.create entrystates) in + let module Arg = + struct + include Arg + + let find_invariant (n, c, i) = + let context = {Invariant.default_context with path = Some i} in + ask_local (n, c) (Invariant context) + end + in + (module Arg: BiArgInvariant) + ) + else ( + let module Arg = + struct + module Node = ArgTool.Node + module Edge = MyARG.InlineEdge + let next _ = [] + let prev _ = [] + let find_invariant _ = Invariant.none + let main_entry = + let lvar = WitnessUtil.find_main_entry entrystates in + (fst lvar, snd lvar, -1) + let iter_nodes f = f main_entry + let query _ q = Queries.Result.top q + end + in + (module Arg: BiArgInvariant) + ) ) in From 0193efad541bb0ba9d3ee79b3b8cf1dabd9aff05 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 10 Sep 2023 12:59:02 +0200 Subject: [PATCH 185/238] handle `bitand` in congruences in restricted cases --- src/cdomains/intDomain.ml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 5417598360..c78e66e2bf 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -514,7 +514,7 @@ module Size = struct (* size in bits as int, range as int64 *) let cast t x = (* TODO: overflow is implementation-dependent! *) if t = IBool then - (* C11 6.3.1.2 Boolean type *) + (* C11 6.3.1.2 Boolean type *) if Z.equal x Z.zero then Z.zero else Z.one else let a,b = range t in @@ -1978,7 +1978,7 @@ struct let top_of ik = `Excluded (S.empty (), size ik) let cast_to ?torg ?no_ov ik = function | `Excluded (s,r) -> - let r' = size ik in + let r' = size ik in if R.leq r r' then (* upcast -> no change *) `Excluded (s, r) else if ik = IBool then (* downcast to bool *) @@ -1986,7 +1986,7 @@ struct `Definite (BI.one) else `Excluded (S.empty(), r') - else + else (* downcast: may overflow *) (* let s' = S.map (BigInt.cast_to ik) s in *) (* We want to filter out all i in s' where (t)x with x in r could be i. *) @@ -3134,7 +3134,7 @@ struct (** The implementation of the bit operations could be improved based on the master’s thesis 'Abstract Interpretation and Abstract Domains' written by Stefan Bygde. - see: https://www.dsi.unive.it/~avp/domains.pdf *) + see: http://www.es.mdh.se/pdf_publications/948.pdf *) let bit2 f ik x y = match x, y with | None, None -> None | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) @@ -3144,7 +3144,19 @@ struct let bitor ik x y = bit2 Ints_t.bitor ik x y - let bitand ik x y = bit2 Ints_t.bitand ik x y + let bitand ik x y = match x, y with + | None, None -> None + | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) + | Some (c, m), Some (c', m') -> + if (m =: Ints_t.zero && m' =: Ints_t.zero) then + (* both arguments constant *) + Some (Ints_t.bitand c c', Ints_t.zero) + else if m' =: Ints_t.zero && c' =: Ints_t.one && Ints_t.rem m (Ints_t.of_int 2) =: Ints_t.zero then + (* x & 1 and x == c (mod 2*z) *) + (* Value is equal to LSB of c *) + Some (Ints_t.bitand c c', Ints_t.zero) + else + top () let bitxor ik x y = bit2 Ints_t.bitxor ik x y From 4d18300af5f6ae1b04bdbf29b29775af07fbc96c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 10 Sep 2023 13:53:37 +0200 Subject: [PATCH 186/238] Handle `bitand` in intervals and def_exc; Add test --- src/analyses/baseInvariant.ml | 26 ++++++++++---- src/cdomains/intDomain.ml | 36 +++++++++++++++++-- tests/regression/37-congruence/13-bitand.c | 40 ++++++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 tests/regression/37-congruence/13-bitand.c diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 096d50b89b..70c6ed9101 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -410,6 +410,18 @@ struct meet_bin c c else a, b + | BAnd as op -> + (* we only attempt to refine a here *) + let a = + match ID.to_int b with + | Some x when BI.equal x BI.one -> + (match ID.to_bool c with + | Some true -> ID.meet a (ID.of_congruence ikind (Z.one, Z.of_int 2)) + | Some false -> ID.meet a (ID.of_congruence ikind (Z.zero, Z.of_int 2)) + | None -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a 1 = %a\n" d_binop op ID.pretty c; a) + | _ -> if M.tracing then M.tracel "inv" "Unhandled case for operator x %a %a = %a\n" d_binop op ID.pretty b ID.pretty c; a + in + a, b | op -> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; a, b @@ -545,8 +557,8 @@ struct in let eval e st = eval_rv a gs st e in let eval_bool e st = match eval e st with Int i -> ID.to_bool i | _ -> None in - let unroll_fk_of_exp e = - match unrollType (Cilfacade.typeOf e) with + let unroll_fk_of_exp e = + match unrollType (Cilfacade.typeOf e) with | TFloat (fk, _) -> fk | _ -> failwith "value which was expected to be a float is of different type?!" in @@ -700,8 +712,8 @@ struct begin match t with | TInt (ik, _) -> begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); let tv_opt = ID.to_bool c in begin match tv_opt with | Some tv -> @@ -735,12 +747,12 @@ struct begin match t with | TFloat (fk, _) -> begin match x with - | ((Var v), offs) -> - if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); + | ((Var v), offs) -> + if M.tracing then M.trace "invSpecial" "qry Result: %a\n" Queries.ML.pretty (ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs))); begin match ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st | `Lifted (Floor (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_floor (FD.cast_to ret_fk c))) xFloat st - | `Lifted (Fabs (ret_fk, xFloat)) -> + | `Lifted (Fabs (ret_fk, xFloat)) -> let inv = FD.inv_fabs (FD.cast_to ret_fk c) in if FD.is_bot inv then raise Analyses.Deadcode diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index c78e66e2bf..312f5d7540 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -826,7 +826,18 @@ struct | _ -> (top_of ik,{underflow=true; overflow=true}) let bitxor = bit (fun _ik -> Ints_t.bitxor) - let bitand = bit (fun _ik -> Ints_t.bitand) + + let bitand ik i1 i2 = + match is_bot i1, is_bot i2 with + | true, true -> bot_of ik + | 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 of_int ik (Ints_t.bitand x y) |> fst with Division_by_zero -> top_of ik) + | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst + | _ -> top_of ik + let bitor = bit (fun _ik -> Ints_t.bitor) let bit1 f ik i1 = @@ -2282,7 +2293,28 @@ struct let ge ik x y = le ik y x let bitnot = lift1 BigInt.bitnot - let bitand = lift2 BigInt.bitand + + let bitand ik x y = norm ik (match x,y with + (* We don't bother with exclusion sets: *) + | `Excluded _, `Definite i -> + (* Except in two special cases *) + if BigInt.equal i BigInt.zero then + `Definite BigInt.zero + else if BigInt.equal i BigInt.one then + of_interval IBool (BigInt.zero, BigInt.one) + else + top () + | `Definite _, `Excluded _ + | `Excluded _, `Excluded _ -> top () + (* The good case: *) + | `Definite x, `Definite y -> + (try `Definite (BigInt.bitand 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)))) + + let bitor = lift2 BigInt.bitor let bitxor = lift2 BigInt.bitxor diff --git a/tests/regression/37-congruence/13-bitand.c b/tests/regression/37-congruence/13-bitand.c new file mode 100644 index 0000000000..c785e722c1 --- /dev/null +++ b/tests/regression/37-congruence/13-bitand.c @@ -0,0 +1,40 @@ +// PARAM: --enable ana.int.congruence --set sem.int.signed_overflow assume_none +#include + +int main() +{ + // Assuming modulo expression + + long x; + __goblint_assume(x % 2 == 1); + __goblint_check(x % 2 == 1); + __goblint_check(x & 1); + + long y; + __goblint_assume(y % 2 == 0); + __goblint_check(y % 2 == 0); + __goblint_check(y & 1); //FAIL + + long z; + __goblint_check(z & 1); //UNKNOWN! + __goblint_assume(z % 8 == 1); + __goblint_check(z & 1); + + long xz; + __goblint_assume(xz % 3 == 1); + __goblint_check(xz & 1); //UNKNOWN! + __goblint_assume(xz % 6 == 1); + __goblint_check(xz & 1); + + // Assuming bitwise expression + + long a; + __goblint_assume(a & 1); + __goblint_check(a % 2 == 1); + __goblint_check(a & 1); + + int b; + __goblint_assume(b & 1); + __goblint_check(b % 2 == 1); + __goblint_check(b & 1); +} From d3ec6172b6f9f8060e0dadcbee439f4b29d00a9b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 11:49:16 +0300 Subject: [PATCH 187/238] Add missing thread-unsafe functions to LibraryFunctions (closes #723) --- src/analyses/libraryFunctions.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 5dc311a587..c4378f6e14 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -111,7 +111,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); ("wcscat", unknown [drop "dest" [r; w]; drop "src" [r]]); + ("wctomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []]); ("wcrtomb", unknown ~attrs:[ThreadUnsafe] [drop "s" [w]; drop "wc" []; drop "ps" [r_deep; w_deep]]); + ("wcstombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r]; drop "size" []]); + ("wcsrtombs", unknown ~attrs:[ThreadUnsafe] [drop "dst" [w]; drop "src" [r_deep; w]; drop "size" []; drop "ps" [r_deep; w_deep]]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); @@ -142,9 +145,11 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("dlerror", unknown ~attrs:[ThreadUnsafe] []); ("drand48", unknown ~attrs:[ThreadUnsafe] []); ("encrypt", unknown ~attrs:[ThreadUnsafe] [drop "block" [r; w]; drop "edflag" []]); + ("setkey", unknown ~attrs:[ThreadUnsafe] [drop "key" [r]]); ("endgrent", unknown ~attrs:[ThreadUnsafe] []); ("endpwent", unknown ~attrs:[ThreadUnsafe] []); ("fcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); + ("ecvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigits" []; drop "decpt" [w]; drop "sign" [w]]); ("gcvt", unknown ~attrs:[ThreadUnsafe] [drop "number" []; drop "ndigit" []; drop "buf" [w]]); ("getdate", unknown ~attrs:[ThreadUnsafe] [drop "string" [r]]); ("getenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); @@ -279,6 +284,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); (* has two underscores *) ("sigsetjmp", special [__ "env" [w]; drop "savesigs" []] @@ fun env -> Setjmp { env }); ("siglongjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); + ("ftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []]); (* TODO: use Call instead of Spawn *) + ("nftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []; drop "flags" []]); (* TODO: use Call instead of Spawn *) ] (** Pthread functions. *) From e40255bdaca9bebe56ccde7f77ded60c25af9023 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 11 Sep 2023 12:52:13 +0200 Subject: [PATCH 188/238] Add special case for &0 in the interval domain --- src/cdomains/intDomain.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 312f5d7540..f1ed546e95 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -835,7 +835,8 @@ struct | _ -> match to_int i1, to_int i2 with | Some x, Some y -> (try of_int ik (Ints_t.bitand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst + | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst + | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst | _ -> top_of ik let bitor = bit (fun _ik -> Ints_t.bitor) From f2b002d664cab336f91b4032658de237168092ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 14:31:01 +0300 Subject: [PATCH 189/238] Add CHANGELOG for v2.2.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9531a5766..a126514852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## v2.2.0 +* TODO OCaml 5 (#1003, #1137). +* TODO Library #1138 +* TODO refactor race #1136 + +* Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). +* Refactor race analysis to lazy distribution (#1084, #1089, #1016). +* Add thread-unsafe library function call analysis (#723, #1082). +* Add mutex type analysis and mutex API analysis (#800, #839, #1073). +* Add interval set domain and string literals domain (#901, #966, #994, #1048). +* Add affine equalities analysis (#592). +* Add use-after-free analysis (#1050, #1114). +* Add dead code elimination transformation (#850, #979). +* Add taint analysis for partial contexts (#553, #952). +* Add YAML witness validation via unassume (#796, #977, #1044, #1045, #1124). +* Add incremental analysis rename detection (#774, #777). +* Fix address sets unsoundness (#822, #967, #564, #1032, #998, #1031). +* Fix thread escape analysis unsoundness (#939, #984, #1074, #1078). +* Fix many incremental analysis issues (#627, #836, #835, #841, #932, #678, #942, #949, #950, #957, #955, #954, #960, #959, #1004, #558, #1010, #1091). +* Fix server mode for abstract debugging (#983, #990, #997, #1000, #1001, #1013, #1018, #1017, #1026, #1027). +* Add documentation for configuration JSON schema and OCaml API (#999, #1054, #1055, #1053). +* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135). +* Add OCaml 5.0 support (#945). + ## v2.1.0 Functionally equivalent to Goblint in SV-COMP 2023. From 1128aba94f87b95031bef9929dea7294c78b880f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 15:30:40 +0300 Subject: [PATCH 190/238] Fix deprecated File.exists? in update_suite.rb --- scripts/update_suite.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index aeac526987..ca408a513a 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -41,7 +41,7 @@ def clearline $goblint = File.join(Dir.getwd,"goblint") goblintbyte = File.join(Dir.getwd,"goblint.byte") -if File.exists?(goblintbyte) then +if File.exist?(goblintbyte) then puts "Running the byte-code version! Continue? (y/n)" exit unless $stdin.gets()[0] == 'y' $goblint = goblintbyte @@ -50,11 +50,11 @@ def clearline end $vrsn = `#{$goblint} --version` -if not File.exists? "linux-headers" then +if not File.exist? "linux-headers" then puts "Missing linux-headers, will download now!" `make headers` end -has_linux_headers = File.exists? "linux-headers" # skip kernel tests if make headers failed (e.g. on opam-repository opam-ci where network is forbidden) +has_linux_headers = File.exist? "linux-headers" # skip kernel tests if make headers failed (e.g. on opam-repository opam-ci where network is forbidden) #Command line parameters #Either only run a single test, or From fd092e36fe2927adc284af3f37d900aa3ab1f763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= Date: Mon, 11 Sep 2023 15:23:23 +0100 Subject: [PATCH 191/238] goblint: fix lower bound on yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See build failure at https://opam.ci.ocaml.org/github/ocaml/opam-repository/commit/b49e6de20d0a04ef393c2e153d87d6e00f63c455 'opam-ci' would otherwise try 'yaml.1.0.0' which doesn't work: ``` <><> Error report <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> +- The following actions failed | - build goblint 2.1.0 +- ``` Signed-off-by: Edwin Török --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index 1da7492d7d..54ed811ddf 100644 --- a/dune-project +++ b/dune-project @@ -42,7 +42,7 @@ (sha (>= 1.12)) cpu arg-complete - yaml + (yaml (>= 3.0.0)) uuidm catapult catapult-file diff --git a/goblint.opam b/goblint.opam index ce7df4a3b0..04e4b27f9d 100644 --- a/goblint.opam +++ b/goblint.opam @@ -39,7 +39,7 @@ depends: [ "sha" {>= "1.12"} "cpu" "arg-complete" - "yaml" + "yaml" {>= "3.0.0"} "uuidm" "catapult" "catapult-file" From c8cc6c47cde0cefc4f78459e3a4a42f0d10b8311 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:24:13 +0300 Subject: [PATCH 192/238] Add TODOs about AddressDomain queries usage --- src/analyses/condVars.ml | 2 +- src/analyses/malloc_null.ml | 2 +- src/analyses/memLeak.ml | 2 +- src/analyses/mutexTypeAnalysis.ml | 4 ++-- src/analyses/poisonVariables.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/uninit.ml | 2 +- src/analyses/varEq.ml | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 04246bf28a..3d07520576 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -72,7 +72,7 @@ struct ) else ad in List.filter_map (function - | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) | _ -> None ) (Queries.AD.elements a') | _ -> [] diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index dca158c973..55d6a49bcb 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -99,7 +99,7 @@ struct | ad when not (Queries.AD.is_top ad) -> let to_extra addr ads = match addr with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) | _ -> ads in Queries.AD.fold to_extra ad [] diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 0bc2196aff..4f27cfea69 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -57,7 +57,7 @@ struct | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v | _ -> D.empty () in - D.diff state unique_pointed_to_heap_vars + D.diff state unique_pointed_to_heap_vars (* TODO: use D.remove instead of diffing singleton *) | _ -> state end | Abort -> diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 4d9de5f9c4..806c35f464 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -18,7 +18,7 @@ struct module O = Offset.Unit module V = struct - include Printable.Prod(CilType.Varinfo)(O) + include Printable.Prod(CilType.Varinfo)(O) (* TODO: use Mval.Unit *) let is_write_only _ = false end @@ -56,7 +56,7 @@ struct let attr = ctx.ask (Queries.EvalMutexAttr attr) in let mutexes = ctx.ask (Queries.MayPointTo mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) - Queries.AD.iter (function addr -> + Queries.AD.iter (function addr -> match addr with | Queries.AD.Addr.Addr (v,o) -> ctx.sideg (v,O.of_offs o) attr | _ -> () diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index f69e5b2fbc..1bd4b6d544 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -103,7 +103,7 @@ struct | ad -> Queries.AD.fold (fun addr vs -> match addr with - | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) + | Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) | _ -> vs ) ad ctx.local end diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index d4ea17d2e0..d053cd103b 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -12,7 +12,7 @@ let to_mvals ad = (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) Queries.AD.fold (fun addr mvals -> match addr with - | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals + | Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals (* TODO: use unconverted addrs in domain? *) | _ -> mvals ) ad (D.empty ()) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 9bd8db2fe5..887035663d 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -198,7 +198,7 @@ struct | ad when not (Queries.AD.is_top ad) -> let to_extra ad ads = match ad with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads + | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) | _ -> ads in Queries.AD.fold to_extra ad [] diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 77823d99d7..bd7d23ab8b 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -258,7 +258,7 @@ struct if Queries.AD.is_top aad then false else Queries.AD.exists (function - | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) + | Addr (u,s) -> CilType.Varinfo.equal v u && oleq o (Addr.Offs.to_exp s) (* TODO: avoid conversion? *) | _ -> false ) aad in @@ -298,7 +298,7 @@ struct else if Queries.AD.is_top bad then ((*Messages.warn ~category:Analyzer "No PT-set: switching to types ";*) type_may_change_apt a ) else Queries.AD.exists (function - | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) + | Addr (v,o) -> lval_may_change_pt a (v, Addr.Offs.to_exp o) (* TODO: avoid conversion? *) | _ -> false ) bad in From 7edb312254bdbe221f72c57c16ca8a7eacac8263 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:29:23 +0300 Subject: [PATCH 193/238] Simplify some AddressDomain query result manipulation --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 4 ++-- src/analyses/condVars.ml | 22 +++++++++----------- src/analyses/extractPthread.ml | 21 +++++-------------- src/analyses/pthreadSignals.ml | 6 ++---- src/analyses/useAfterFree.ml | 11 ++-------- 6 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ca60e5dc30..46c620f390 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -276,7 +276,7 @@ struct let reachable_from_args ctx args = let to_vs e = ctx.ask (ReachableFrom e) - |> LockDomain.MayLocksetNoRW.to_var_may + |> Queries.AD.to_var_may |> VS.of_list in List.fold (fun vs e -> VS.join vs (to_vs e)) (VS.empty ()) args diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..e59d8a2fc6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1319,8 +1319,8 @@ struct (* ignore @@ printf "EvalStr Address: %a -> %s (must %i, may %i)\n" d_plainexp e (VD.short 80 (Address a)) (List.length @@ AD.to_var_must a) (List.length @@ AD.to_var_may a); *) begin match unrollType (Cilfacade.typeOf e) with | TPtr(TInt(IChar, _), _) -> - let (v,o) = List.hd (AD.to_mval a) in - let lval = Mval.Exp.to_cil @@ (v, Addr.Offs.to_exp o) in + let mval = List.hd (AD.to_mval a) in + let lval = Addr.Mval.to_cil mval in (try `Lifted (Bytes.to_string (Hashtbl.find char_array lval)) with Not_found -> Queries.Result.top q) | _ -> (* what about ISChar and IUChar? *) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 3d07520576..04b148dd02 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -64,18 +64,16 @@ struct let (>?) = Option.bind let mayPointTo ctx exp = - match ctx.ask (Queries.MayPointTo exp) with - | ad when not (Queries.AD.is_top ad) && not (Queries.AD.is_empty ad) -> - let a' = if Queries.AD.mem UnknownPtr ad then ( - M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) - Queries.AD.remove UnknownPtr ad - ) else ad - in - List.filter_map (function - | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) - | _ -> None - ) (Queries.AD.elements a') - | _ -> [] + let ad = ctx.ask (Queries.MayPointTo exp) in + let a' = if Queries.AD.mem UnknownPtr ad then ( + M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) + Queries.AD.remove UnknownPtr ad + ) else ad + in + List.filter_map (function + | ValueDomain.Addr.Addr (v,o) -> Some (v, ValueDomain.Addr.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *) + | _ -> None + ) (Queries.AD.elements a') let mustPointTo ctx exp = (* this is just to get Mval.Exp *) match mayPointTo ctx exp with diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 211b88fd6e..74657c7468 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -877,16 +877,9 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = - let ad = ctx.ask (Queries.MayPointTo exp) in - if (not (Queries.AD.is_top ad)) && not (Queries.AD.is_empty ad) then - if Queries.AD.mem UnknownPtr ad - then (* UNSOUND *) - Queries.AD.remove UnknownPtr ad - |> Queries.AD.to_var_may - else - Queries.AD.to_var_may ad - else - [] + ctx.ask (Queries.MayPointTo exp) + |> Queries.AD.remove UnknownPtr + |> Queries.AD.to_var_may let eval_var ctx exp = match exp with @@ -1126,11 +1119,7 @@ module Spec : Analyses.MCPSpec = struct ad in let thread_fun = - Queries.AD.fold (fun addr vars -> - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vars - | _ -> vars - ) funs_ad [] + Queries.AD.to_var_may funs_ad |> List.unique ~eq:(fun a b -> a.vid = b.vid) |> List.hd in @@ -1255,7 +1244,7 @@ module Spec : Analyses.MCPSpec = struct (* TODO: optimize finding *) let tasks_f = let var_in_ad ad f = Queries.AD.exists (function - | Queries.AD.Addr.Addr (ls_f,_) -> ls_f = f + | Queries.AD.Addr.Addr (ls_f,_) -> CilType.Varinfo.equal ls_f f | _ -> false ) ad in diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index d515820514..0b776282e8 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -17,10 +17,8 @@ struct module C = MustSignals module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end) - let eval_exp_addr (a: Queries.ask) exp = Queries.AD.elements (a.f (Queries.MayPointTo exp)) - - let possible_vinfos a cv_arg = - List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg) + let possible_vinfos (a: Queries.ask) cv_arg = + Queries.AD.to_var_may (a.f (Queries.MayPointTo cv_arg)) (* transfer functions *) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f828e17e2b..0aafbd1ad4 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -160,15 +160,8 @@ struct if Queries.AD.is_top reachable_from_args || D.is_top caller_state then [caller_state, caller_state] else - let reachable_vars = - let get_vars addr vars = - match addr with - | Queries.AD.Addr.Addr (v,_) -> v :: vars - | _ -> vars - in - Queries.AD.fold get_vars reachable_from_args [] - in - let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in + let reachable_vars = Queries.AD.to_var_may reachable_from_args in + let callee_state = D.filter (fun var -> List.mem var reachable_vars) caller_state in (* TODO: use AD.mem directly *) [caller_state, callee_state] ) From 3e01426ccd87dd02b6519db783eff107028368f7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 11 Sep 2023 17:29:53 +0300 Subject: [PATCH 194/238] Cherry-pick AddressDomain.filter from 224615f63b07448ba45afb79f42c2d8284d4b968 Required to fix Promela extraction tests. --- src/cdomains/addressDomain.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cdomains/addressDomain.ml b/src/cdomains/addressDomain.ml index 9f6ee56cbf..5981caf9ea 100644 --- a/src/cdomains/addressDomain.ml +++ b/src/cdomains/addressDomain.ml @@ -440,4 +440,6 @@ struct let r = narrow x y in if M.tracing then M.traceu "ad" "-> %a\n" pretty r; r + + let filter f ad = fold (fun addr ad -> if f addr then add addr ad else ad) ad (empty ()) end From 70cd4e879945ad637827953e593010e93a954e50 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 10:36:22 +0300 Subject: [PATCH 195/238] Fix Calloc indentation in base --- src/analyses/base.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b74555b7ad..6ab4015460 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2298,15 +2298,17 @@ struct let countval = eval_int (Analyses.ask_of_ctx ctx) gs st n in if ID.to_int countval = Some Z.one then ( set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ - add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false); - eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var)) + (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) ] ) else ( let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); - (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset)))))] + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [ + (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.one) (Blob (VD.bot (), blobsize, false)))); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) BI.zero, `NoOffset))))) + ] ) | _ -> st end From dce70e2953189412c2248a67d147937e56216fd9 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 10:59:27 +0300 Subject: [PATCH 196/238] Add comment about unsoundness back to extractPthread --- src/analyses/extractPthread.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index 74657c7468..60e389fedf 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -878,7 +878,7 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct let eval_ptr ctx exp = ctx.ask (Queries.MayPointTo exp) - |> Queries.AD.remove UnknownPtr + |> Queries.AD.remove UnknownPtr (* UNSOUND *) |> Queries.AD.to_var_may let eval_var ctx exp = From 0568edd8bdc6218354b6793d0b90c76d9014c882 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 11:56:44 +0300 Subject: [PATCH 197/238] Simplify do_exp in uninit and malloc_null --- src/analyses/malloc_null.ml | 12 ++++++------ src/analyses/uninit.ml | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 55d6a49bcb..354429cad6 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -97,16 +97,16 @@ struct let do_exp e = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ads = + let to_extra addr ad = match addr with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) - | _ -> ads + | Queries.AD.Addr.Addr _ -> AD.add addr ad + | _ -> ad in - Queries.AD.fold to_extra ad [] + Queries.AD.fold to_extra ad (AD.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | _ -> [] + | _ -> AD.empty () in - List.concat_map do_exp args + List.map do_exp args in let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = let vars = AD.to_var_may one in diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 887035663d..56a1c7d843 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -196,16 +196,16 @@ struct let do_exp e = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra ad ads = - match ad with - | Queries.AD.Addr.Addr mval -> AD.of_mval mval :: ads (* TODO: why list of singleton AD-s? *) - | _ -> ads + let to_extra addr ad = + match addr with + | Queries.AD.Addr.Addr _ -> AD.add addr ad + | _ -> ad in - Queries.AD.fold to_extra ad [] + Queries.AD.fold to_extra ad (AD.empty ()) (* Ignore soundness warnings, as invalidation proper will raise them. *) - | _ -> [] + | _ -> AD.empty () in - List.concat_map do_exp args + List.map do_exp args in let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = let vars = AD.to_var_may one in From f643bb5ad0ae3c7e4f012d91be88e1557de7bd1a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 13:37:25 +0300 Subject: [PATCH 198/238] Add external benchmarking guide --- docs/user-guide/benchmarking.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/benchmarking.md b/docs/user-guide/benchmarking.md index 44811b61a5..5417375bdb 100644 --- a/docs/user-guide/benchmarking.md +++ b/docs/user-guide/benchmarking.md @@ -1,6 +1,31 @@ # Benchmarking +The following best practices should be followed when benchmarking Goblint. +This is to ensure valid, reproducible and representative benchmarking results. -To achieve reproducible builds and the best performance for benchmarking, it is recommended to compile Goblint using the `release` option: +# External benchmarking +External users should choose the version of Goblint to evaluate or benchmark as follows: + +1. Use the newest version release. + + The version from git `master` branch or any other intermediate git commit come without any guarantees. + They are bleeding-edge and haven't gone through validation like the version releases. + + SV-COMP releases are highly preferable since they've gone through rigorous validation in SV-COMP. + +2. Download the corresponding version from a Zenodo artifact or by checking out the respective git tag. **Do not install directly from opam repository!** + + Goblint pins optimized versions of some dependencies which cannot be done on the opam repository releases. + Thus, using the latter would yield unrepresentative results. + + Zenodo artifacts come with DOIs, which make them ideal for citation. + +3. Use OCaml 4.14. **Do not use OCaml 5!** + + OCaml 5 has significant performance regressions, which yield unrepresentative benchmarking results. + Goblint's `make setup` installs the correct OCaml version into a new opam switch. + +# Release build +To achieve the best performance for benchmarking, Goblint should be compiled using the `release` option: ```sh make release From a3eaf9aece32da7a67d65dc17bffa57dbc9b8530 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 13:39:42 +0300 Subject: [PATCH 199/238] Refer to benchmarking guide in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b03b7bbe36..4d97baa842 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Documentation can be browsed on [Read the Docs](https://goblint.readthedocs.io/e ## Installing Both for using an up-to-date version of Goblint or developing it, the best way is to install from source by cloning this repository. +For benchmarking Goblint, please follow the [Benchmarking guide on Read the Docs](https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/). ### Linux 1. Install [opam](https://opam.ocaml.org/doc/Install.html). From cd45df53d63174ad77f05d93d34472f4cdbd639a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:02:11 +0300 Subject: [PATCH 200/238] Add opam post-message about OCaml 5 benchmarking --- goblint.opam | 3 +++ goblint.opam.locked | 3 +++ goblint.opam.template | 3 +++ 3 files changed, 9 insertions(+) diff --git a/goblint.opam b/goblint.opam index a20df919d0..59f8a59c09 100644 --- a/goblint.opam +++ b/goblint.opam @@ -81,3 +81,6 @@ pin-depends: [ # TODO: add back after release, only pinned for CI stability [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] diff --git a/goblint.opam.locked b/goblint.opam.locked index acb49a7b14..fe5bb65a00 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -140,3 +140,6 @@ pin-depends: [ "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] diff --git a/goblint.opam.template b/goblint.opam.template index b7f5a7abff..5c13293196 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -8,3 +8,6 @@ pin-depends: [ # TODO: add back after release, only pinned for CI stability [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] +post-messages: [ + "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} +] From ee31392babcb1107763f5b5658215716c7d90dc3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 17:49:48 +0300 Subject: [PATCH 201/238] Use opam 2.0 compatible switch create (closes #1133) --- make.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make.sh b/make.sh index 788289c5ed..af1411a8d3 100755 --- a/make.sh +++ b/make.sh @@ -8,7 +8,7 @@ opam_setup() { set -x opam init -y -a --bare $SANDBOXING # sandboxing is disabled in travis and docker opam update - opam switch -y create . --deps-only ocaml-variants.4.14.0+options ocaml-option-flambda --locked + opam switch -y create . --deps-only --packages=ocaml-variants.4.14.0+options,ocaml-option-flambda --locked } rule() { From b25cb45cca2d755d26c86dfe9529ce15b761a161 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:23:02 +0300 Subject: [PATCH 202/238] Use OCaml 5.0 compatible Apron --- goblint.opam | 2 -- goblint.opam.locked | 12 ++++-------- goblint.opam.template | 2 -- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/goblint.opam b/goblint.opam index 59f8a59c09..794b43e770 100644 --- a/goblint.opam +++ b/goblint.opam @@ -78,8 +78,6 @@ pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - # TODO: add back after release, only pinned for CI stability - [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} diff --git a/goblint.opam.locked b/goblint.opam.locked index fe5bb65a00..5f81506183 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -21,7 +21,7 @@ doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "angstrom" {= "0.15.0"} - "apron" {= "v0.9.13"} + "apron" {= "v0.9.14~beta.2"} "arg-complete" {= "0.1.0"} "astring" {= "0.8.5"} "base-bigarray" {= "base"} @@ -56,22 +56,22 @@ depends: [ "dune-private-libs" {= "3.6.1"} "dune-site" {= "3.6.1"} "dyn" {= "3.6.1"} + "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} "goblint-cil" {= "2.0.1"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} - "fileutils" {= "0.6.4"} "logs" {= "0.7.0"} - "mlgmpidl" {= "1.2.14"} + "mlgmpidl" {= "1.2.15"} "num" {= "1.4"} "ocaml" {= "4.14.0"} - "ocaml-variants" {= "4.14.0+options"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} + "ocaml-variants" {= "4.14.0+options"} "ocamlbuild" {= "0.14.2"} "ocamlfind" {= "1.9.5"} "odoc" {= "2.2.0" & with-doc} @@ -131,10 +131,6 @@ pin-depends: [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] - [ - "apron.v0.9.13" - "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8" - ] [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" diff --git a/goblint.opam.template b/goblint.opam.template index 5c13293196..ff1364fc20 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -5,8 +5,6 @@ pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - # TODO: add back after release, only pinned for CI stability - [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8"] ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} From 13062dfc37b5b1108bf44081c0c0b8363ce81083 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:24:26 +0300 Subject: [PATCH 203/238] Add OCaml 5.0 to unlocked workflow --- .github/workflows/unlocked.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..65d85f0b3d 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -18,6 +18,7 @@ jobs: - ubuntu-latest - macos-latest ocaml-compiler: + - 5.0.x - ocaml-variants.4.14.0+options,ocaml-option-flambda - 4.14.x - 4.13.x From 3ee9af873e2075824833b42f4e3bd84e197e1f35 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 12 Sep 2023 15:45:51 +0300 Subject: [PATCH 204/238] Simplify remove_unreachable in uninit and malloc_null --- src/analyses/malloc_null.ml | 24 ++++++++++++------------ src/analyses/uninit.ml | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 354429cad6..4d5871cb80 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -94,25 +94,25 @@ struct (* Remove null values from state that are unreachable from exp.*) let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = - let do_exp e = + let do_exp e a = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ad = - match addr with - | Queries.AD.Addr.Addr _ -> AD.add addr ad - | _ -> ad - in - Queries.AD.fold to_extra ad (AD.empty ()) + ad + |> Queries.AD.filter (function + | Queries.AD.Addr.Addr _ -> true + | _ -> false) + |> Queries.AD.join a (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> AD.empty () in - List.map do_exp args + List.fold_right do_exp args (AD.empty ()) in - let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = - let vars = AD.to_var_may one in - List.fold_right AD.add (List.concat_map to_addrs vars) many + let vars = + reachable + |> AD.to_var_may + |> List.concat_map to_addrs + |> AD.of_list in - let vars = List.fold_right add_exploded_struct reachable (AD.empty ()) in if D.is_top st then D.top () else D.filter (fun x -> AD.mem x vars) st diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 56a1c7d843..f8759d9134 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -193,25 +193,25 @@ struct let remove_unreachable (ask: Queries.ask) (args: exp list) (st: D.t) : D.t = let reachable = - let do_exp e = + let do_exp e a = match ask.f (Queries.ReachableFrom e) with | ad when not (Queries.AD.is_top ad) -> - let to_extra addr ad = - match addr with - | Queries.AD.Addr.Addr _ -> AD.add addr ad - | _ -> ad - in - Queries.AD.fold to_extra ad (AD.empty ()) + ad + |> Queries.AD.filter (function + | Queries.AD.Addr.Addr _ -> true + | _ -> false) + |> Queries.AD.join a (* Ignore soundness warnings, as invalidation proper will raise them. *) | _ -> AD.empty () in - List.map do_exp args + List.fold_right do_exp args (AD.empty ()) in - let add_exploded_struct (one: AD.t) (many: AD.t) : AD.t = - let vars = AD.to_var_may one in - List.fold_right AD.add (List.concat_map to_addrs vars) many + let vars = + reachable + |> AD.to_var_may + |> List.concat_map to_addrs + |> AD.of_list in - let vars = List.fold_right add_exploded_struct reachable (AD.empty ()) in if D.is_top st then D.top () else D.filter (fun x -> AD.mem x vars) st From 94db94deb38ffa5b880214d36c17defbf53683df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 12 Sep 2023 14:32:45 +0300 Subject: [PATCH 205/238] Update Gobview to OCaml 5 compatible --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index c3dcfaba97..70f76a1692 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit c3dcfaba97a1df72f027e5dad317e2c201ce5e4b +Subproject commit 70f76a16927b14fca0ccfd3ab032076381bdc834 From bff915968251f99b5ba14ac4dfa1021f0b2c371b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 12 Sep 2023 16:03:00 +0200 Subject: [PATCH 206/238] Congruences: Make % sound by restricting cases where we return a constant --- src/cdomains/intDomain.ml | 4 ++-- tests/regression/37-congruence/06-refinements.c | 5 +++-- tests/regression/37-congruence/07-refinements-o.c | 5 +++-- .../regression/37-congruence/11-overflow-signed.c | 6 +++--- tests/regression/37-congruence/13-bitand.c | 12 +++++++++--- tests/regression/37-congruence/14-negative.c | 15 +++++++++++++++ 6 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 tests/regression/37-congruence/14-negative.c diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index f1ed546e95..748df62300 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3199,8 +3199,8 @@ struct | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) | Some (c1, m1), Some(c2, m2) -> if m2 =: Ints_t.zero then - if (c2 |: m1) then - Some(c1 %: c2,Ints_t.zero) + if (c2 |: m1) && (c1 %: c2 =: Ints_t.zero || m1 =: Ints_t.zero || not (Cil.isSigned ik)) then + Some(c1 %: c2, Ints_t.zero) else normalize ik (Some(c1, (Ints_t.gcd m1 c2))) else diff --git a/tests/regression/37-congruence/06-refinements.c b/tests/regression/37-congruence/06-refinements.c index c0b7b0564c..38bf9458cc 100644 --- a/tests/regression/37-congruence/06-refinements.c +++ b/tests/regression/37-congruence/06-refinements.c @@ -5,14 +5,15 @@ int main() { int top; int i = 0; if(top % 17 == 3) { - __goblint_check(top%17 ==3); + __goblint_check(top%17 ==3); //TODO (Refine top to be positive above, and reuse information in %) if(top %17 != 3) { i = 12; } else { } } - __goblint_check(i ==0); + __goblint_check(i ==0); // TODO + i = 0; if(top % 17 == 0) { __goblint_check(top%17 == 0); diff --git a/tests/regression/37-congruence/07-refinements-o.c b/tests/regression/37-congruence/07-refinements-o.c index 44f21b7c8c..49148d6683 100644 --- a/tests/regression/37-congruence/07-refinements-o.c +++ b/tests/regression/37-congruence/07-refinements-o.c @@ -32,15 +32,16 @@ int main() { int top; int i = 0; if(top % 17 == 3) { - __goblint_check(top%17 ==3); + __goblint_check(top%17 ==3); //TODO (Refine top to be positive above, and reuse information in %) if(top %17 != 3) { i = 12; } else { } } - __goblint_check(i ==0); + __goblint_check(i ==0); //TODO + i = 0; if(top % 17 == 0) { __goblint_check(top%17 == 0); if(top %17 != 0) { diff --git a/tests/regression/37-congruence/11-overflow-signed.c b/tests/regression/37-congruence/11-overflow-signed.c index 29599fe246..031d88ce45 100644 --- a/tests/regression/37-congruence/11-overflow-signed.c +++ b/tests/regression/37-congruence/11-overflow-signed.c @@ -12,8 +12,8 @@ int basic(){ { if (b % two_pow_16 == 5) { - __goblint_check(a % two_pow_16 == 3); - __goblint_check(b % two_pow_16 == 5); + __goblint_check(a % two_pow_16 == 3); //TODO (Refine a to be positive above, and reuse information in %) + __goblint_check(b % two_pow_16 == 5); //TODO (Refine a to be positive above, and reuse information in %) unsigned int e = a * b; __goblint_check(e % two_pow_16 == 15); // UNKNOWN! @@ -35,7 +35,7 @@ int special(){ if (a % two_pow_18 == two_pow_17) { - __goblint_check(a % two_pow_18 == two_pow_17); + __goblint_check(a % two_pow_18 == two_pow_17); //TODO (Refine a to be positive above, and reuse information in %) unsigned int e = a * a; __goblint_check(e == 0); //UNKNOWN! diff --git a/tests/regression/37-congruence/13-bitand.c b/tests/regression/37-congruence/13-bitand.c index c785e722c1..500fb9d1cc 100644 --- a/tests/regression/37-congruence/13-bitand.c +++ b/tests/regression/37-congruence/13-bitand.c @@ -5,11 +5,16 @@ int main() { // Assuming modulo expression - long x; + unsigned long x; __goblint_assume(x % 2 == 1); __goblint_check(x % 2 == 1); __goblint_check(x & 1); + long xx; + __goblint_assume(xx % 2 == 1); + __goblint_check(xx % 2 == 1); //TODO (Refine xx to be positive above, and reuse information in %) + __goblint_check(xx & 1); + long y; __goblint_assume(y % 2 == 0); __goblint_check(y % 2 == 0); @@ -27,14 +32,15 @@ int main() __goblint_check(xz & 1); // Assuming bitwise expression + // Does NOT actually lead to modulo information, as negative values may also have their last bit set! long a; __goblint_assume(a & 1); - __goblint_check(a % 2 == 1); + __goblint_check(a % 2 == 1); //UNKNOWN! __goblint_check(a & 1); int b; __goblint_assume(b & 1); - __goblint_check(b % 2 == 1); + __goblint_check(b % 2 == 1); //UNKNOWN! __goblint_check(b & 1); } diff --git a/tests/regression/37-congruence/14-negative.c b/tests/regression/37-congruence/14-negative.c new file mode 100644 index 0000000000..eae8307ab1 --- /dev/null +++ b/tests/regression/37-congruence/14-negative.c @@ -0,0 +1,15 @@ +// PARAM: --enable ana.int.congruence --set sem.int.signed_overflow assume_none +#include + +int main() +{ + int top; + + int c = -5; + if (top) + { + c = -7; + } + __goblint_check(c % 2 == 1); //UNKNOWN! (Does not hold at runtime) + __goblint_check(c % 2 == -1); //TODO (Track information that c is negative) +} From c6e43c552ec54324e793954aa616b0b21b1d717f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:09 +0000 Subject: [PATCH 207/238] Bump docker/build-push-action from 4 to 5 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 4 ++-- .github/workflows/unlocked.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..b0f7b30f01 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -59,7 +59,7 @@ jobs: - name: Build Docker image id: build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . load: true # load into docker instead of immediately pushing @@ -72,7 +72,7 @@ jobs: run: docker run --rm -v $(pwd):/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} /data/tests/regression/04-mutex/01-simple_rc.c # run image by version in case multiple tags - name: Push Docker image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..f5a5ef0138 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -215,7 +215,7 @@ jobs: - name: Build dev Docker image id: build - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . target: dev From 72550593ba3b4157a0ed4ea6bde1f1974e31c1c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:12 +0000 Subject: [PATCH 208/238] Bump docker/metadata-action from 4 to 5 Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..40647f1335 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | From 25ce122603ca04eea4323b2448bb290cb0bc2b93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:16 +0000 Subject: [PATCH 209/238] Bump docker/setup-buildx-action from 2 to 3 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- .github/workflows/unlocked.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..716ad314bf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action + uses: docker/setup-buildx-action@v3 # needed for GitHub Actions Cache in build-push-action - name: Log in to the Container registry uses: docker/login-action@v2 diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 15cd9138d8..15179fda03 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -211,7 +211,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action + uses: docker/setup-buildx-action@v3 # needed for GitHub Actions Cache in build-push-action - name: Build dev Docker image id: build From 02c839f198c48b3256aa815ade4590c3a159bf0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:07:20 +0000 Subject: [PATCH 210/238] Bump docker/login-action from 2 to 3 Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1ef104db29..410aa05ea1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -41,7 +41,7 @@ jobs: uses: docker/setup-buildx-action@v2 # needed for GitHub Actions Cache in build-push-action - name: Log in to the Container registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From bef3c54e0f6c5c13aca320774107cc626cb79368 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 10:43:00 +0300 Subject: [PATCH 211/238] Replace goblint-cil pin with published 2.0.2 --- dune-project | 2 +- goblint.opam | 5 +++-- goblint.opam.locked | 6 +----- goblint.opam.template | 3 ++- gobview | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/dune-project b/dune-project index b8b8481c97..c5dc943c5d 100644 --- a/dune-project +++ b/dune-project @@ -24,7 +24,7 @@ (synopsis "Static analysis framework for C") (depends (ocaml (>= 4.10)) - (goblint-cil (>= 2.0.1)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.4.0)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) diff --git a/goblint.opam b/goblint.opam index 59f8a59c09..3b1afdfff8 100644 --- a/goblint.opam +++ b/goblint.opam @@ -21,7 +21,7 @@ bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.6"} "ocaml" {>= "4.10"} - "goblint-cil" {>= "2.0.1"} + "goblint-cil" {>= "2.0.2"} "batteries" {>= "3.4.0"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} @@ -75,7 +75,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/goblint.opam.locked b/goblint.opam.locked index fe5bb65a00..d0323713ad 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -58,7 +58,7 @@ depends: [ "dyn" {= "3.6.1"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.1"} + "goblint-cil" {= "2.0.2"} "integers" {= "0.7.0"} "json-data-encoding" {= "0.12.1"} "jsonrpc" {= "1.15.0~5.0preview1"} @@ -127,10 +127,6 @@ conflicts: [ ] # TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ - [ - "goblint-cil.2.0.1" - "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" - ] [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8" diff --git a/goblint.opam.template b/goblint.opam.template index 5c13293196..eb795f29d2 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ - [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/gobview b/gobview index c3dcfaba97..b373d06174 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit c3dcfaba97a1df72f027e5dad317e2c201ce5e4b +Subproject commit b373d06174667537b671f3122daf4ebd4b195aea From 8440ff01c6f63e9480b578e79bb7dc28893ed5e1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:08:32 +0300 Subject: [PATCH 212/238] Update releasing documentation about Zenodo webhook --- docs/developer-guide/releasing.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index f6bfbb459e..69ffcb2461 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -45,13 +45,20 @@ 10. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. 11. Exit Docker container. -12. Create a GitHub release with the git tag: `DUNE_RELEASE_DELEGATE=github-dune-release-delegate dune-release publish distrib`. +12. Temporarily enable Zenodo GitHub webhook. + + This is because we only want numbered version releases to automatically add a new version to our Zenodo artifact. + Other tags (like SV-COMP or paper artifacts) have manually created Zenodo artifacts anyway and thus shouldn't add new versions to the main Zenodo artifact. + +13. Create a GitHub release with the git tag: `DUNE_RELEASE_DELEGATE=github-dune-release-delegate dune-release publish distrib`. Explicitly specify `distrib` because we don't want to publish OCaml API docs. Environment variable workaround for the package having a Read the Docs `doc` URL (see ). -13. Create an opam package: `dune-release opam pkg`. -14. Submit the opam package to opam-repository: `dune-release opam submit`. +14. Re-disable Zenodo GitHub webhook. + +15. Create an opam package: `dune-release opam pkg`. +16. Submit the opam package to opam-repository: `dune-release opam submit`. ## SV-COMP @@ -104,15 +111,9 @@ ### After all preruns 1. Push git tag from last prerun: `git push origin svcompXY`. -2. Temporarily disable Zenodo webhook. - - This is because we don't want a new out-of-place version of Goblint in our Zenodo artifact. - A separate Zenodo artifact for the SV-COMP version can be created later if tool paper is submitted. - -3. Create GitHub release from the git tag and attach latest submitted archive as a download. -4. Manually run `docker` workflow on `svcompXY` git tag and targeting `svcompXY` Docker tag. +2. Create GitHub release from the git tag and attach latest submitted archive as a download. +3. Manually run `docker` workflow on `svcompXY` git tag and targeting `svcompXY` Docker tag. This is because the usual `docker` workflow only handles semver releases. -5. Re-enable Zenodo webhook. -6. Release new semver version on opam. See above. +4. Release new semver version on opam. See above. From 336c5ffaa050cdd8e9760b0fad7565df5351892f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:26:57 +0300 Subject: [PATCH 213/238] Finalize v2.2.0 CHANGELOG --- CHANGELOG.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a126514852..045e06815a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,6 @@ ## v2.2.0 -* TODO OCaml 5 (#1003, #1137). -* TODO Library #1138 -* TODO refactor race #1136 - * Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). -* Refactor race analysis to lazy distribution (#1084, #1089, #1016). +* Refactor race analysis to lazy distribution (#1084, #1089, #1136, #1016). * Add thread-unsafe library function call analysis (#723, #1082). * Add mutex type analysis and mutex API analysis (#800, #839, #1073). * Add interval set domain and string literals domain (#901, #966, #994, #1048). @@ -19,8 +15,8 @@ * Fix many incremental analysis issues (#627, #836, #835, #841, #932, #678, #942, #949, #950, #957, #955, #954, #960, #959, #1004, #558, #1010, #1091). * Fix server mode for abstract debugging (#983, #990, #997, #1000, #1001, #1013, #1018, #1017, #1026, #1027). * Add documentation for configuration JSON schema and OCaml API (#999, #1054, #1055, #1053). -* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135). -* Add OCaml 5.0 support (#945). +* Add many library function specifications (#962, #996, #1028, #1079, #1121, #1135, #1138). +* Add OCaml 5.0 support (#1003, #945, #1162). ## v2.1.0 Functionally equivalent to Goblint in SV-COMP 2023. From b46aeda9eaecd3ec42ec14c977d1550faad0de23 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 11:29:16 +0300 Subject: [PATCH 214/238] Disable pins for v2.2.0 release --- goblint.opam | 6 +++--- goblint.opam.locked | 7 ------- goblint.opam.template | 6 +++--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/goblint.opam b/goblint.opam index e42f4f7f47..efdd0aaa00 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,12 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index b97faad221..bb59c41dd1 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -125,13 +125,6 @@ available: os-distribution != "alpine" & arch != "arm64" conflicts: [ "result" {< "1.5"} ] -# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 -pin-depends: [ - [ - "ppx_deriving.5.2.1" - "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" - ] -] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] diff --git a/goblint.opam.template b/goblint.opam.template index 59160bf171..6259c4d498 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-distribution != "alpine" & arch != "arm64" -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.2 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#98598d94f796a63751e5a9d39c6b3a9fe1f32330" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] -] + # [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] +# ] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From f18d8085ff4ba1af670356abee38fcfb820c2c6b Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 13 Sep 2023 12:32:41 +0300 Subject: [PATCH 215/238] D.remove instead of diffing singleton in memLeak --- src/analyses/memLeak.ml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 4f27cfea69..1bff7611a4 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -52,12 +52,10 @@ struct begin match ctx.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) - let unique_pointed_to_heap_vars = - match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.singleton v - | _ -> D.empty () - in - D.diff state unique_pointed_to_heap_vars (* TODO: use D.remove instead of diffing singleton *) + begin match Queries.AD.choose ad with + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | _ -> state + end | _ -> state end | Abort -> From 246c999ad775c7f4bb8db7989fc975ab5d774444 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 14:45:29 +0300 Subject: [PATCH 216/238] Bump batteries lower bound to 3.5.0 Required for Hashtbl.exists. --- dune-project | 2 +- goblint.opam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dune-project b/dune-project index c5dc943c5d..4a9cd8e3c1 100644 --- a/dune-project +++ b/dune-project @@ -25,7 +25,7 @@ (depends (ocaml (>= 4.10)) (goblint-cil (>= 2.0.2)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. - (batteries (>= 3.4.0)) + (batteries (>= 3.5.0)) (zarith (>= 1.8)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) diff --git a/goblint.opam b/goblint.opam index efdd0aaa00..661222805b 100644 --- a/goblint.opam +++ b/goblint.opam @@ -22,7 +22,7 @@ depends: [ "dune" {>= "3.6"} "ocaml" {>= "4.10"} "goblint-cil" {>= "2.0.2"} - "batteries" {>= "3.4.0"} + "batteries" {>= "3.5.0"} "zarith" {>= "1.8"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} From 1a731fd3eb9bed59843d0e5a9e96694d6592a1a5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:10:54 +0300 Subject: [PATCH 217/238] Fix deprecated tail syntax in 70-transform/02-deadcode.t --- tests/regression/70-transform/02-deadcode.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/70-transform/02-deadcode.t b/tests/regression/70-transform/02-deadcode.t index ed3cd80e17..8bc4f9bf13 100644 --- a/tests/regression/70-transform/02-deadcode.t +++ b/tests/regression/70-transform/02-deadcode.t @@ -210,7 +210,7 @@ Transformation still works with 'exp.mincfg', but can not find all dead code; test against the diff. Macintosh's diff(1) adds whitespace after the function names, strip with sed. - $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail +3 + $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 @@ -13,0 +14,3 @@ int basic1(int n ) + if (n < 0) { + return (0); From 833d90d41b5d14934edbf283cb90fdafbf382798 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:16:01 +0300 Subject: [PATCH 218/238] Remove -p in 70-transform/02-deadcode.t due to OSX On opam-repository CI, OSX has different -p output. --- tests/regression/70-transform/02-deadcode.t | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/regression/70-transform/02-deadcode.t b/tests/regression/70-transform/02-deadcode.t index 8bc4f9bf13..03a46b891e 100644 --- a/tests/regression/70-transform/02-deadcode.t +++ b/tests/regression/70-transform/02-deadcode.t @@ -210,15 +210,15 @@ Transformation still works with 'exp.mincfg', but can not find all dead code; test against the diff. Macintosh's diff(1) adds whitespace after the function names, strip with sed. - $ diff -p -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 - @@ -13,0 +14,3 @@ int basic1(int n ) + $ diff -U0 "$(./transform.sh --file $args 02-deadcode.c)" "$(./transform.sh --file $args --enable exp.mincfg 02-deadcode.c)" | sed 's/[[:blank:]]*$//' | tail -n +3 + @@ -13,0 +14,3 @@ + if (n < 0) { + return (0); + } - @@ -54,0 +58,2 @@ int one_branch_dead(int x ) + @@ -54,0 +58,2 @@ + } else { + return (7 - x); - @@ -65,0 +71,8 @@ int uncalled_but_referenced_function(int + @@ -65,0 +71,8 @@ +int uncalled1(void) +{ + @@ -227,17 +227,17 @@ Macintosh's diff(1) adds whitespace after the function names, strip with sed. + +} +} - @@ -79,0 +93,5 @@ int conditional_call_in_loop(int x ) + @@ -79,0 +93,5 @@ + if (i > 7) { + { + uncalled1(); + } + } - @@ -151,0 +170,4 @@ int loop_dead_on_break(int z ) + @@ -151,0 +170,4 @@ + { + s += s; + i ++; + } - @@ -203,0 +226,2 @@ int main(void) + @@ -203,0 +226,2 @@ + uncalled1(); + uncalled_but_referenced_function(3); From 166a9b619b87456059de8f1839fb810621302efb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Sep 2023 15:20:48 +0300 Subject: [PATCH 219/238] Add CHANGELOG for v2.2.1 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 045e06815a..97cc399133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v2.2.1 +* Bump batteries lower bound to 3.5.0. +* Fix flaky dead code elimination transformation test. + ## v2.2.0 * Add `setjmp`/`longjmp` analysis (#887, #970, #1015, #1019). * Refactor race analysis to lazy distribution (#1084, #1089, #1136, #1016). From a36572925450fc31fe95f448a75118f004af537a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 12:23:21 +0300 Subject: [PATCH 220/238] Add zlib and liblzma functions used in the silver searcher --- src/analyses/libraryFunctions.ml | 18 ++++++++++++++++++ src/util/options.schema.json | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index e8fabe7dc1..2eb68dd437 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -102,6 +102,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vprintf", unknown [drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vasprintf", unknown [drop "strp" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsprintf", unknown [drop "str" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -288,6 +290,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []]); (* TODO: use Call instead of Spawn *) ("nftw", unknown ~attrs:[ThreadUnsafe] [drop "dirpath" [r]; drop "fn" [s]; drop "nopenfd" []; drop "flags" []]); (* TODO: use Call instead of Spawn *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); + ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); ] (** Pthread functions. *) @@ -824,6 +827,19 @@ let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pcre_version", unknown []); ] +let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("inflate", unknown [drop "strm" [r_deep]; drop "flush" []]); + ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); + ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); + ("inflateEnd", unknown [drop "strm" [f_deep]]); + ] + +let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ + ("lzma_code", unknown [drop "strm" [r_deep; w_deep]; drop "action" []]); + ("lzma_auto_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); + ("lzma_end", unknown [drop "strm" [r_deep; w_deep]]); + ] + let libraries = Hashtbl.of_list [ ("c", c_descs_list @ math_descs_list); ("posix", posix_descs_list); @@ -837,6 +853,8 @@ let libraries = Hashtbl.of_list [ ("ncurses", ncurses_descs_list); ("zstd", zstd_descs_list); ("pcre", pcre_descs_list); + ("zlib", zlib_descs_list); + ("liblzma", liblzma_descs_list); ] let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t = diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 1ef73378fb..1b9c7d3fd5 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1285,7 +1285,9 @@ "sv-comp", "ncurses", "zstd", - "pcre" + "pcre", + "zlib", + "liblzma" ] }, "default": [ From a1464e1ed471aa8fc55b78eae130902da97f1791 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 21:46:00 +0300 Subject: [PATCH 221/238] Use record as Access.A type instead of a quintuple --- src/analyses/raceAnalysis.ml | 33 +++++++++++++++++---------------- src/domains/access.ml | 32 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 74a98af6be..a89ef9206a 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -225,9 +225,9 @@ struct | _ -> () - let side_access ctx (conf, w, loc, e, a) ((memoroot, offset) as memo) = + let side_access ctx Access.A.{conf; kind; node; exp; acc} ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton (conf, w, loc, e, a))))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton {conf; kind; node; exp; acc})))); side_vars ctx memo (** Side-effect empty access set for prefix-type_suffix race checking. *) @@ -302,24 +302,24 @@ struct ) (G.vars (ctx.global (V.vars g))) | _ -> Queries.Result.top q - let event ctx e octx = - match e with - | Events.Access {exp=e; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) + let event ctx exp octx = + match exp with + | Events.Access {exp; lvals; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) (* must use original (pre-assign, etc) ctx queries *) let conf = 110 in let module LS = Queries.LS in let part_access (vo:varinfo option): MCPAccess.A.t = (*partitions & locks*) - Obj.obj (octx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind}))) + Obj.obj (octx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in - let loc = Option.get !Node.current_node in + let node = Option.get !Node.current_node in let add_access conf voffs = - let a = part_access (Option.map fst voffs) in - Access.add ~side:(side_access octx (conf, kind, loc, e, a)) ~side_empty:(side_access_empty octx) e voffs; + let acc = part_access (Option.map fst voffs) in + Access.add ~side:(side_access octx {conf; kind; node; exp; acc}) ~side_empty:(side_access_empty octx) exp voffs; in let add_access_struct conf ci = - let a = part_access None in - Access.add_one ~side:(side_access octx (conf, kind, loc, e, a)) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) + let acc = part_access None in + Access.add_one ~side:(side_access octx {conf; kind; node; exp; acc}) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in let has_escaped g = octx.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls @@ -345,7 +345,7 @@ struct (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in (* now we need to access all fields that might be pointed to: is this correct? *) - begin match octx.ask (ReachableUkTypes e) with + begin match octx.ask (ReachableUkTypes exp) with | ts when Queries.TS.is_top ts -> includes_uk := true | ts -> @@ -370,12 +370,13 @@ struct (* perform shallow and deep invalidate according to Library descriptors *) let desc = LibraryFunctions.find f in if List.mem LibraryDesc.ThreadUnsafe desc.attrs then ( - let e = Lval (Var f, NoOffset) in + let exp = Lval (Var f, NoOffset) in let conf = 110 in - let loc = Option.get !Node.current_node in + let kind = AccessKind.Call in + let node = Option.get !Node.current_node in let vo = Some f in - let a = Obj.obj (ctx.ask (PartAccess (Memory {exp=e; var_opt=vo; kind=Call}))) in - side_access ctx (conf, Call, loc, e, a) ((`Var f), `NoOffset) ; + let acc = Obj.obj (ctx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in + side_access ctx {conf; kind; node; exp; acc} ((`Var f), `NoOffset) ; ); ctx.local diff --git a/src/domains/access.ml b/src/domains/access.ml index 675d1c2e72..05308b5ed9 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -329,12 +329,18 @@ and distribute_access_type f = function module A = struct include Printable.Std - type t = int * AccessKind.t * Node.t * CilType.Exp.t * MCPAccess.A.t [@@deriving eq, ord, hash] + type t = { + conf : int; + kind : AccessKind.t; + node : Node.t; + exp : CilType.Exp.t; + acc : MCPAccess.A.t; + } [@@deriving eq, ord, hash] let name () = "access" - let pretty () (conf, kind, node, e, lp) = - Pretty.dprintf "%d, %a, %a, %a, %a" conf AccessKind.pretty kind CilType.Location.pretty (Node.location node) CilType.Exp.pretty e MCPAccess.A.pretty lp + let pretty () {conf; kind; node; exp; acc} = + Pretty.dprintf "%d, %a, %a, %a, %a" conf AccessKind.pretty kind CilType.Location.pretty (Node.location node) CilType.Exp.pretty exp MCPAccess.A.pretty acc include Printable.SimplePretty ( struct @@ -343,10 +349,8 @@ struct end ) - let conf (conf, _, _, _, _) = conf - - let relift (conf, kind, node, e, a) = - (conf, kind, node, e, MCPAccess.A.relift a) + let relift {conf; kind; node; exp; acc} = + {conf; kind; node; exp; acc = MCPAccess.A.relift acc} end module AS = @@ -354,17 +358,17 @@ struct include SetDomain.Make (A) let max_conf accs = - accs |> elements |> List.map A.conf |> (List.max ~cmp:Int.compare) + accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (List.max ~cmp:Int.compare) end (* Check if two accesses may race and if yes with which confidence *) -let may_race (conf,(kind: AccessKind.t),loc,e,a) (conf2,(kind2: AccessKind.t),loc2,e2,a2) = +let may_race A.{kind; acc; _} A.{kind=kind2; acc=acc2; _} = if kind = Read && kind2 = Read then false (* two read/read accesses do not race *) else if not (get_bool "ana.race.free") && (kind = Free || kind2 = Free) then false - else if not (MCPAccess.A.may_race a a2) then + else if not (MCPAccess.A.may_race acc acc2) then false (* analysis-specific information excludes race *) else true @@ -487,7 +491,7 @@ let race_conf accs = if AS.cardinal accs = 1 then ( (* singleton component *) let acc = AS.choose accs in if may_race acc acc then (* self-race *) - Some (A.conf acc) + Some (acc.conf) else None ) @@ -516,9 +520,9 @@ let print_accesses memo grouped_accs = let allglobs = get_bool "allglobs" in let race_threshold = get_int "warn.race-threshold" in let msgs race_accs = - let h (conf,kind,node,e,a) = - let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty a conf in - let doc = dprintf "%t (exp: %a)" d_msg d_exp e in + let h A.{conf; kind; node; exp; acc} = + let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty acc conf in + let doc = dprintf "%t (exp: %a)" d_msg d_exp exp in (doc, Some (Messages.Location.Node node)) in AS.elements race_accs From 929b658cf26be63fcae41ce3be71499be60a9ebb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 14 Sep 2023 21:57:53 +0300 Subject: [PATCH 222/238] Inline d_msg () to doc in access --- src/domains/access.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 05308b5ed9..83903ba355 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -521,8 +521,7 @@ let print_accesses memo grouped_accs = let race_threshold = get_int "warn.race-threshold" in let msgs race_accs = let h A.{conf; kind; node; exp; acc} = - let d_msg () = dprintf "%a with %a (conf. %d)" AccessKind.pretty kind MCPAccess.A.pretty acc conf in - let doc = dprintf "%t (exp: %a)" d_msg d_exp exp in + let doc = dprintf "%a with %a (conf. %d) (exp: %a)" AccessKind.pretty kind MCPAccess.A.pretty acc conf d_exp exp in (doc, Some (Messages.Location.Node node)) in AS.elements race_accs From 60952d933071b651630f6657b133e7625dc5a034 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 15 Sep 2023 10:34:34 +0300 Subject: [PATCH 223/238] Pass access as a variable instead of destructing the record --- src/analyses/raceAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index a89ef9206a..d1d608ae06 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -225,9 +225,9 @@ struct | _ -> () - let side_access ctx Access.A.{conf; kind; node; exp; acc} ((memoroot, offset) as memo) = + let side_access ctx acc ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton {conf; kind; node; exp; acc})))); + ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton acc)))); side_vars ctx memo (** Side-effect empty access set for prefix-type_suffix race checking. *) From fb7159c0fe7aa1e0ed887f807db7feb3c9699114 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 16 Sep 2023 16:33:40 +0200 Subject: [PATCH 224/238] Add some setjump/longjump test for race detection --- tests/regression/68-longjmp/52-races.c | 36 +++++++++++++ tests/regression/68-longjmp/53-races-no.c | 37 ++++++++++++++ .../regression/68-longjmp/54-races-actually.c | 51 +++++++++++++++++++ .../68-longjmp/55-races-no-return.c | 51 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 tests/regression/68-longjmp/52-races.c create mode 100644 tests/regression/68-longjmp/53-races-no.c create mode 100644 tests/regression/68-longjmp/54-races-actually.c create mode 100644 tests/regression/68-longjmp/55-races-no-return.c diff --git a/tests/regression/68-longjmp/52-races.c b/tests/regression/68-longjmp/52-races.c new file mode 100644 index 0000000000..5c7ac6daa6 --- /dev/null +++ b/tests/regression/68-longjmp/52-races.c @@ -0,0 +1,36 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + longjmp(env_buffer, 2); + pthread_mutex_unlock(&mutex1); + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + if(!setjmp( env_buffer )) { + bar(); + } + + global = 5; // NORACE + pthread_mutex_unlock(&mutex1); +} diff --git a/tests/regression/68-longjmp/53-races-no.c b/tests/regression/68-longjmp/53-races-no.c new file mode 100644 index 0000000000..7247f14941 --- /dev/null +++ b/tests/regression/68-longjmp/53-races-no.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global ==3) { + longjmp(env_buffer, 2); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + if(!setjmp( env_buffer )) { + bar(); + } + + global = 5; // NORACE + pthread_mutex_unlock(&mutex1); +} diff --git a/tests/regression/68-longjmp/54-races-actually.c b/tests/regression/68-longjmp/54-races-actually.c new file mode 100644 index 0000000000..12f1f791c5 --- /dev/null +++ b/tests/regression/68-longjmp/54-races-actually.c @@ -0,0 +1,51 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; // RACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global == 3) { + longjmp(env_buffer, 2); + } else { + longjmp(env_buffer, 4); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int n = 0; + + switch(setjmp( env_buffer )) { + case 0: + bar(); + break; + case 2: + n=1; + pthread_mutex_unlock(&mutex1); + break; + default: + break; + } + + global = 5; //RACE + + if(n == 0) { + pthread_mutex_unlock(&mutex1); + } +} diff --git a/tests/regression/68-longjmp/55-races-no-return.c b/tests/regression/68-longjmp/55-races-no-return.c new file mode 100644 index 0000000000..ad0e5d4707 --- /dev/null +++ b/tests/regression/68-longjmp/55-races-no-return.c @@ -0,0 +1,51 @@ +// PARAM: --enable ana.int.interval +#include +#include +#include +#include +#include + +jmp_buf env_buffer; +int global = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&mutex1); + global = 3; //NORACE + pthread_mutex_unlock(&mutex1); + return NULL; +} + +int bar() { + pthread_mutex_lock(&mutex1); + if(global == 7) { + longjmp(env_buffer, 2); + } else { + longjmp(env_buffer, 4); + } + return 8; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int n = 0; + + switch(setjmp( env_buffer )) { + case 0: + bar(); + break; + case 2: + n=1; + pthread_mutex_unlock(&mutex1); + break; + default: + break; + } + + global = 5; //NORACE + + if(n == 0) { + pthread_mutex_unlock(&mutex1); + } +} From 18fe6d1b55fb54f193e1b5ce8506c937ce2d571d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 16 Sep 2023 16:47:51 +0200 Subject: [PATCH 225/238] 68/[52-55] Don't include `goblint.h`, it is unused --- tests/regression/68-longjmp/52-races.c | 1 - tests/regression/68-longjmp/53-races-no.c | 1 - tests/regression/68-longjmp/54-races-actually.c | 3 +-- tests/regression/68-longjmp/55-races-no-return.c | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/regression/68-longjmp/52-races.c b/tests/regression/68-longjmp/52-races.c index 5c7ac6daa6..4cde97d954 100644 --- a/tests/regression/68-longjmp/52-races.c +++ b/tests/regression/68-longjmp/52-races.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; diff --git a/tests/regression/68-longjmp/53-races-no.c b/tests/regression/68-longjmp/53-races-no.c index 7247f14941..4692f6ca18 100644 --- a/tests/regression/68-longjmp/53-races-no.c +++ b/tests/regression/68-longjmp/53-races-no.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; diff --git a/tests/regression/68-longjmp/54-races-actually.c b/tests/regression/68-longjmp/54-races-actually.c index 12f1f791c5..62423cd884 100644 --- a/tests/regression/68-longjmp/54-races-actually.c +++ b/tests/regression/68-longjmp/54-races-actually.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; @@ -44,7 +43,7 @@ int main() { } global = 5; //RACE - + if(n == 0) { pthread_mutex_unlock(&mutex1); } diff --git a/tests/regression/68-longjmp/55-races-no-return.c b/tests/regression/68-longjmp/55-races-no-return.c index ad0e5d4707..850fc54fa5 100644 --- a/tests/regression/68-longjmp/55-races-no-return.c +++ b/tests/regression/68-longjmp/55-races-no-return.c @@ -2,7 +2,6 @@ #include #include #include -#include #include jmp_buf env_buffer; From 1feb75e2aa06ccacec63b39e52cfc0661b5d9a80 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:31:51 +0300 Subject: [PATCH 226/238] Fix Cilfacade.split_anoncomp_name for empty names (closes #1171) --- src/domains/access.ml | 4 ++-- src/util/cilfacade.ml | 9 +++++---- unittest/mainTest.ml | 1 + unittest/util/cilfacadeTest.ml | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 unittest/util/cilfacadeTest.ml diff --git a/src/domains/access.ml b/src/domains/access.ml index 83903ba355..fa6446df16 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -16,8 +16,8 @@ let is_ignorable_type (t: typ): bool = | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with - | (true, ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) - | (false, ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) + | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) + | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) | _ -> false end | TComp ({ cname = "lock_class_key"; _ }, _) -> true diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index c77d2cd738..f0ac3202ce 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -342,22 +342,23 @@ let makeBinOp binop e1 e2 = let (_, e) = Cabs2cil.doBinOp binop e1 t1 e2 t2 in e -let anoncomp_name_regexp = Str.regexp {|^__anon\(struct\|union\)_\(.+\)_\([0-9]+\)$|} +let anoncomp_name_regexp = Str.regexp {|^__anon\(struct\|union\)\(_\(.+\)\)?_\([0-9]+\)$|} let split_anoncomp_name name = (* __anonunion_pthread_mutexattr_t_488594144 *) + (* __anonunion_50 *) if Str.string_match anoncomp_name_regexp name 0 then ( let struct_ = match Str.matched_group 1 name with | "struct" -> true | "union" -> false | _ -> assert false in - let name' = Str.matched_group 2 name in - let id = int_of_string (Str.matched_group 3 name) in + let name' = try Some (Str.matched_group 3 name) with Not_found -> None in + let id = int_of_string (Str.matched_group 4 name) in (struct_, name', id) ) else - invalid_arg "Cilfacade.split_anoncomp_name" + invalid_arg ("Cilfacade.split_anoncomp_name: " ^ name) (** Pretty-print typsig like typ, because {!d_typsig} prints with CIL constructors. *) diff --git a/unittest/mainTest.ml b/unittest/mainTest.ml index df67340309..642e495d50 100644 --- a/unittest/mainTest.ml +++ b/unittest/mainTest.ml @@ -8,6 +8,7 @@ let all_tests = ("" >::: LvalTest.test (); CompilationDatabaseTest.tests; LibraryDslTest.tests; + CilfacadeTest.tests; (* etc *) "domaintest" >::: QCheck_ounit.to_ounit2_test_list Maindomaintest.all_testsuite; IntOpsTest.tests; diff --git a/unittest/util/cilfacadeTest.ml b/unittest/util/cilfacadeTest.ml new file mode 100644 index 0000000000..482a502824 --- /dev/null +++ b/unittest/util/cilfacadeTest.ml @@ -0,0 +1,14 @@ +open Goblint_lib +open OUnit2 +open Cilfacade + +let test_split_anoncomp_name _ = + let assert_equal = assert_equal ~printer:[%show: bool * string option * int] in + assert_equal (false, Some "pthread_mutexattr_t", 488594144) (split_anoncomp_name "__anonunion_pthread_mutexattr_t_488594144"); + assert_equal (true, Some "__once_flag", 1234) (split_anoncomp_name "__anonstruct___once_flag_1234"); + assert_equal (false, None, 50) (split_anoncomp_name "__anonunion_50") + +let tests = + "cilfacadeTest" >::: [ + "split_anoncomp_name" >:: test_split_anoncomp_name; + ] From 444428467d44a3d188d034c4418d34117543b838 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 11:36:23 +0300 Subject: [PATCH 227/238] Remove duplicate vsprintf and add vsnprintf --- src/analyses/libraryFunctions.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 2eb68dd437..912867d319 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -103,7 +103,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("vfprintf", unknown [drop "stream" [r_deep; w_deep]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vsprintf", unknown [drop "buffer" [w]; drop "format" [r]; drop "vlist" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("vasprintf", unknown [drop "strp" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) - ("vsprintf", unknown [drop "str" [w]; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) + ("vsnprintf", unknown [drop "str" [w]; drop "size" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mktime", unknown [drop "tm" [r;w]]); ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); @@ -1089,7 +1089,6 @@ let invalidate_actions = [ "usleep", readsAll; "svc_run", writesAll;(*unsafe*) "dup", readsAll; (*safe*) - "vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf", writesAllButFirst 3 readsAll; (*drop 3*) "__builtin___vsnprintf_chk", writesAllButFirst 3 readsAll; (*drop 3*) "strcasecmp", readsAll; (*safe*) From 684cfa8c3f45afcefd95a1096e954fad27af8d91 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Mon, 18 Sep 2023 11:37:22 +0300 Subject: [PATCH 228/238] Apply suggestions from code review Co-authored-by: Simmo Saan --- src/analyses/libraryFunctions.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 912867d319..b883c7e354 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -828,7 +828,7 @@ let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ] let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ - ("inflate", unknown [drop "strm" [r_deep]; drop "flush" []]); + ("inflate", unknown [drop "strm" [r_deep; w_deep]; drop "flush" []]); ("inflateInit2", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []]); ("inflateInit2_", unknown [drop "strm" [r_deep; w_deep]; drop "windowBits" []; drop "version" [r]; drop "stream_size" []]); ("inflateEnd", unknown [drop "strm" [f_deep]]); @@ -837,7 +837,7 @@ let zlib_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ let liblzma_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("lzma_code", unknown [drop "strm" [r_deep; w_deep]; drop "action" []]); ("lzma_auto_decoder", unknown [drop "strm" [r_deep; w_deep]; drop "memlimit" []; drop "flags" []]); - ("lzma_end", unknown [drop "strm" [r_deep; w_deep]]); + ("lzma_end", unknown [drop "strm" [r_deep; w_deep; f_deep]]); ] let libraries = Hashtbl.of_list [ From e69d13c0cd4cf73a9bbda511627ee1844a39bbbc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:49:06 +0300 Subject: [PATCH 229/238] Add thread self creation non-terminating test from libaco --- tests/regression/10-synch/07-thread_self_create.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/regression/10-synch/07-thread_self_create.c diff --git a/tests/regression/10-synch/07-thread_self_create.c b/tests/regression/10-synch/07-thread_self_create.c new file mode 100644 index 0000000000..473a26a25b --- /dev/null +++ b/tests/regression/10-synch/07-thread_self_create.c @@ -0,0 +1,15 @@ +// PARAM: --set ana.activated[+] thread +// Checks termination of thread analysis with a thread who is its own single parent. +#include + +void *t_fun(void *arg) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + return 0; +} From 2486404315e4e7d44512134637d1eaff78def99e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 11:55:59 +0300 Subject: [PATCH 230/238] Fix 10-synch/07-thread_self_create --- src/analyses/threadAnalysis.ml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index d6a93744bc..1e679a4707 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -32,13 +32,21 @@ struct let rec is_not_unique ctx tid = let (rep, parents, _) = ctx.global tid in - let n = TS.cardinal parents in - (* A thread is not unique if it is - * a) repeatedly created, - * b) created in multiple threads, or - * c) created by a thread that is itself multiply created. - * Note that starting threads have empty ancestor sets! *) - rep || n > 1 || n > 0 && is_not_unique ctx (TS.choose parents) + if rep then + true (* repeatedly created *) + else ( + let n = TS.cardinal parents in + if n > 1 then + true (* created in multiple threads *) + else if n > 0 then ( + (* created by single thread *) + let parent = TS.choose parents in + (* created by itself thread-recursively or by a thread that is itself multiply created *) + T.equal tid parent || is_not_unique ctx parent (* equal check needed to avoid infinte self-recursion *) + ) + else + false (* no ancestors, starting thread *) + ) let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in From 0f59ac967b1fd37bbea7c6c4c2a785a136f36c03 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 13:50:10 +0300 Subject: [PATCH 231/238] Fix Cilfacade.pretty_typsig_like_typ forgetting pointers from name on base type --- src/util/cilfacade.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index f0ac3202ce..eb7330aa19 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -377,7 +377,7 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = | _ -> pa in match ts with - | TSBase t -> dn_type () t + | TSBase t -> defaultCilPrinter#pType nameOpt () t | TSComp (cstruct, cname, a) -> let su = if cstruct then "struct" else "union" in text (su ^ " " ^ cname ^ " ") From d0e9064924f4548d4ac5fc277d5644cbbddcfcf3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 19 Sep 2023 21:49:05 +0200 Subject: [PATCH 232/238] Fix `trace` calls outside of `if tracing` --- src/solvers/sLRphased.ml | 8 ++++---- src/solvers/sLRterm.ml | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/solvers/sLRphased.ml b/src/solvers/sLRphased.ml index f4c3389f1d..c120a7bc6c 100644 --- a/src/solvers/sLRphased.ml +++ b/src/solvers/sLRphased.ml @@ -73,7 +73,7 @@ module Make = let effects = ref Set.empty in let side y d = assert (not (S.Dom.is_bot d)); - trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -109,11 +109,11 @@ module Make = if wpx then if b then let nar = narrow old tmp in - trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar else let wid = S.Dom.widen old (S.Dom.join old tmp) in - trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; + if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a\n" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; wid else tmp @@ -163,7 +163,7 @@ module Make = and sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) - in trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + in if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v and eq x get set = eval_rhs_event x; match S.system x with diff --git a/src/solvers/sLRterm.ml b/src/solvers/sLRterm.ml index d4f4671c46..eb11447d11 100644 --- a/src/solvers/sLRterm.ml +++ b/src/solvers/sLRterm.ml @@ -64,14 +64,14 @@ module SLR3term = HM.replace rho x (S.Dom.bot ()); HM.replace infl x (VS.add x VS.empty); let c = if side then count_side else count in - trace "sol" "INIT: Var: %a with prio %d\n" S.Var.pretty_trace x !c; + if tracing then trace "sol" "INIT: Var: %a with prio %d\n" S.Var.pretty_trace x !c; HM.replace key x !c; decr c end in let sides x = let w = try HM.find set x with Not_found -> VS.empty in let v = Enum.fold (fun d z -> try S.Dom.join d (HPM.find rho' (z,x)) with Not_found -> d) (S.Dom.bot ()) (VS.enum w) in - trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v + if tracing then trace "sol" "SIDES: Var: %a\nVal: %a\n" S.Var.pretty_trace x S.Dom.pretty v; v in let rec iterate b_old prio = if H.size !q = 0 || min_key q > prio then () @@ -122,7 +122,7 @@ module SLR3term = ) *) (* if S.Dom.is_bot d then print_endline "BOT" else *) - trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; + if tracing then trace "sol" "SIDE: Var: %a\nVal: %a\n" S.Var.pretty_trace y S.Dom.pretty d; let first = not (Set.mem y !effects) in effects := Set.add y !effects; if first then ( @@ -156,17 +156,17 @@ module SLR3term = if wpx then if S.Dom.leq tmp old then ( let nar = narrow old tmp in - trace "sol" "NARROW1: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW1: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar, true ) else if b_old then ( let nar = narrow old tmp in - trace "sol" "NARROW2: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; + if tracing then trace "sol" "NARROW2: Var: %a\nOld: %a\nNew: %a\nNarrow: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty nar; nar, true ) else ( let wid = S.Dom.widen old (S.Dom.join old tmp) in - trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; + if tracing then trace "sol" "WIDEN: Var: %a\nOld: %a\nNew: %a\nWiden: %a" S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp S.Dom.pretty wid; wid, false ) else From 8336d28999086a3ae18b8c6cbd976ea916bde07d Mon Sep 17 00:00:00 2001 From: sallto <68823230+sallto@users.noreply.github.com> Date: Sat, 23 Sep 2023 16:38:26 +0200 Subject: [PATCH 233/238] fix: copying of files to GobView in projects with subdirectories (#1143) * fix: macos command line file * fix: special paths for macos and non-english languages special paths are os and language dependant. ex. -> for macos * fix: prevent recursive directory copies when using gobview * fix: correct counter for gobview files Counter for unique file names was broken by skipping directory entries * fix: remove unnecessary recursive copying --------- Co-authored-by: Michael Schwarz --- src/maingoblint.ml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 8944d87ea0..155faa0e76 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -417,10 +417,9 @@ let parse_preprocessed preprocessed = let goblint_cwd = GobFpath.cwd () in let get_ast_and_record_deps (preprocessed_file, task_opt) = - let transform_file (path_str, system_header) = match path_str with - | "" | "" -> + let transform_file (path_str, system_header) = if Str.string_match (Str.regexp "<.+>") path_str 0 then (path_str, system_header) (* ignore special "paths" *) - | _ -> + else let path = Fpath.v path_str in let path' = if get_bool "pre.transform-paths" then ( let cwd_opt = @@ -584,19 +583,19 @@ let do_gobview cilfile = let file_dir = Fpath.(run_dir / "files") in GobSys.mkdir_or_exists file_dir; let file_loc = Hashtbl.create 113 in - let counter = ref 0 in - let copy path = + let copy (path, i) = let name, ext = Fpath.split_ext (Fpath.base path) in - let unique_name = Fpath.add_ext ext (Fpath.add_ext (string_of_int !counter) name) in - counter := !counter + 1; + let unique_name = Fpath.add_ext ext (Fpath.add_ext (string_of_int i) name) in let dest = Fpath.(file_dir // unique_name) in let gobview_path = match Fpath.relativize ~root:run_dir dest with | Some p -> Fpath.to_string p | None -> failwith "The gobview directory should be a prefix of the paths of c files copied to the gobview directory" in Hashtbl.add file_loc (Fpath.to_string path) gobview_path; - FileUtil.cp [Fpath.to_string path] (Fpath.to_string dest) in + FileUtil.cp [Fpath.to_string path] (Fpath.to_string dest) + in let source_paths = Preprocessor.FpathH.to_list Preprocessor.dependencies |> List.concat_map (fun (_, m) -> Fpath.Map.fold (fun p _ acc -> p::acc) m []) in - List.iter copy source_paths; + let source_file_paths = List.filteri_map (fun i e -> if Fpath.is_file_path e then Some (e, i) else None) source_paths in + List.iter copy source_file_paths; Serialize.marshal file_loc (Fpath.(run_dir / "file_loc.marshalled")); (* marshal timing statistics *) let stats = Fpath.(run_dir / "stats.marshalled") in From 70f267b1bb50c66a0e0332982506de963c6f619e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 10:36:12 +0300 Subject: [PATCH 234/238] Add more categories to unsound/imprecise call messages --- src/analyses/base.ml | 6 +++--- src/framework/constraints.ml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..aac369fed3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1104,12 +1104,12 @@ struct if AD.mem Addr.UnknownPtr fp then begin let others = AD.to_var_may fp in if others = [] then raise OnlyUnknown; - M.warn ~category:Imprecise "Function pointer %a may contain unknown functions." d_exp fval; + M.warn ~category:Imprecise ~tags:[Category Call] "Function pointer %a may contain unknown functions." d_exp fval; dummyFunDec.svar :: others end else AD.to_var_may fp with SetDomain.Unsupported _ | OnlyUnknown -> - M.warn ~category:Unsound "Unknown call to function %a." d_exp fval; + M.warn ~category:Imprecise ~tags:[Category Call] "Unknown call to function %a." d_exp fval; [dummyFunDec.svar] (** Evaluate expression as address. @@ -1970,7 +1970,7 @@ struct end let special_unknown_invalidate ctx ask gs st f args = - (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn ~category:Imprecise "Unknown function ptr called"); + (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn ~category:Imprecise ~tags:[Category Call] "Unknown function ptr called"); let desc = LF.find f in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..288b7ad0ea 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -776,16 +776,16 @@ struct end else begin let geq = if var_arg then ">=" else "" in - M.warn ~tags:[CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; + M.warn ~category:Unsound ~tags:[Category Call; CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; None end | _ -> - M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; + M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; None in let funs = List.filter_map one_function functions in if [] = funs then begin - M.warn ~category:Unsound "No suitable function to be called at call site. Continuing with state before call."; + M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; d (* because LevelSliceLifter *) end else common_joins ctx funs !r !spawns From 16077e3b146d464072004eaf0cd1480a2a7b99d1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 13:27:33 +0300 Subject: [PATCH 235/238] Add region interprocedural fixpoint error test --- .../09-regions/40-zstd-thread-pool-region.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/regression/09-regions/40-zstd-thread-pool-region.c diff --git a/tests/regression/09-regions/40-zstd-thread-pool-region.c b/tests/regression/09-regions/40-zstd-thread-pool-region.c new file mode 100644 index 0000000000..13baf5ec3f --- /dev/null +++ b/tests/regression/09-regions/40-zstd-thread-pool-region.c @@ -0,0 +1,34 @@ +// SKIP PARAM: --set ana.activated[+] region +// FIXPOINT +#include +#include +#include + +typedef struct POOL_job_s { + void *opaque; +} POOL_job; + +typedef struct POOL_ctx_s { + POOL_job *queue; +} POOL_ctx; + +POOL_ctx* ctx_global; + +POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) +{ + POOL_ctx* ctx_create; + ctx_create = (POOL_ctx*)malloc(sizeof(POOL_ctx)); + ctx_create->queue = (POOL_job*)malloc(queueSize * sizeof(POOL_job)); + + int r; // rand + if (r) + ctx_global = ctx_create; // pretend escape + return ctx_create; +} + +int main() { + while (1) { + POOL_ctx *ctx_main; + ctx_main = POOL_create(20, 10); + } +} From 23a5d83ab8a1af6431a9c00934139ff056758642 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 16:30:25 +0300 Subject: [PATCH 236/238] Add eqd to TD3 tracing output --- src/solvers/td3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 29ad301292..07edc632c7 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -327,7 +327,7 @@ module Base = else box old eqd in - if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nNew value: %a\n" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty wpd; + if tracing then trace "sol" "Var: %a (wp: %b)\nOld value: %a\nEqd: %a\nNew value: %a\n" S.Var.pretty_trace x wp S.Dom.pretty old S.Dom.pretty eqd S.Dom.pretty wpd; if cache then ( if tracing then trace "cache" "cache size %d for %a\n" (HM.length l) S.Var.pretty_trace x; cache_sizes := HM.length l :: !cache_sizes; From 810fab57274821d600b5d841a8a1be977311a3b2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 17:36:12 +0300 Subject: [PATCH 237/238] Add zstd unsound both branches dead test --- .../03-practical/31-zstd-cctxpool-blobs.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/regression/03-practical/31-zstd-cctxpool-blobs.c diff --git a/tests/regression/03-practical/31-zstd-cctxpool-blobs.c b/tests/regression/03-practical/31-zstd-cctxpool-blobs.c new file mode 100644 index 0000000000..40e448eb22 --- /dev/null +++ b/tests/regression/03-practical/31-zstd-cctxpool-blobs.c @@ -0,0 +1,29 @@ +#include +#include + +struct ZSTD_CCtx_s { + int bmi2; +}; + +typedef struct ZSTD_CCtx_s ZSTD_CCtx; + +typedef struct { + ZSTD_CCtx* cctx[1]; +} ZSTDMT_CCtxPool; + +void *t_fun(void *arg) { + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); // enter multithreaded + + ZSTDMT_CCtxPool* const cctxPool = calloc(1, sizeof(ZSTDMT_CCtxPool)); + cctxPool->cctx[0] = malloc(sizeof(ZSTD_CCtx)); + if (!cctxPool->cctx[0]) // TODO NOWARN + __goblint_check(1); // TODO reachable + else + __goblint_check(1); // TODO reachable + return 0; +} From 5347c087b4eec4c7db77832b2c4f97c7d2841598 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 11:22:12 +0300 Subject: [PATCH 238/238] Add eval_offset tracing --- src/cdomains/valueDomain.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 9d894b6b34..cba4b04c18 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -824,6 +824,8 @@ struct (* Funny, this does not compile without the final type annotation! *) let rec eval_offset (ask: VDQ.t) f (x: t) (offs:offs) (exp:exp option) (v:lval option) (t:typ): t = let rec do_eval_offset (ask:VDQ.t) f (x:t) (offs:offs) (exp:exp option) (l:lval option) (o:offset option) (v:lval option) (t:typ): t = + if M.tracing then M.traceli "eval_offset" "do_eval_offset %a %a (%a)\n" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp; + let r = match x, offs with | Blob((va, _, orig) as c), `Index (_, ox) -> begin @@ -886,6 +888,9 @@ struct | Top -> M.info ~category:Imprecise "Trying to read an index, but the array is unknown"; top () | _ -> M.warn ~category:Imprecise ~tags:[Category Program] "Trying to read an index, but was not given an array (%a)" pretty x; top () end + in + if M.tracing then M.traceu "eval_offset" "do_eval_offset -> %a\n" pretty r; + r in let l, o = match exp with | Some(Lval (x,o)) -> Some ((x, NoOffset)), Some(o)