From 96c777d9325641d7d70248769d3ea4ec719a7daf Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 24 Jan 2023 08:45:41 +0100 Subject: [PATCH 001/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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/401] 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 7bb5697b51950921c51e638aaebefe4b80ce3a4c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 27 Jun 2023 16:23:52 +0200 Subject: [PATCH 037/401] Add first rough (semi-working) memory OOB analysis --- src/analyses/memOutOfBounds.ml | 194 +++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/analyses/memOutOfBounds.ml diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml new file mode 100644 index 0000000000..db4066d442 --- /dev/null +++ b/src/analyses/memOutOfBounds.ml @@ -0,0 +1,194 @@ +open GoblintCil +open Analyses + +module Spec = +struct + include Analyses.IdentitySpec + + module D = Lattice.Unit(*ValueDomain.AddrSetDomain*) + module C = Lattice.Unit + + (* TODO: Do this later *) + let context _ _ = () + + let name () = "memOutOfBounds" + + (* HELPER FUNCTIONS *) + + let exp_points_to_heap ctx (exp:exp) = + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + Queries.LS.elements a + |> List.map fst + |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) + | _ -> false + + let lval_points_to_heap ctx (lval:lval) = exp_points_to_heap ctx (mkAddrOf lval) + + let rec exp_contains_a_ptr (exp:exp) = + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> false + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | UnOp (_, e, _) + | CastE (_, e) -> exp_contains_a_ptr e + | BinOp (_, e1, e2, _) -> + exp_contains_a_ptr e1 || exp_contains_a_ptr e2 + | Question (e1, e2, e3, _) -> + exp_contains_a_ptr e1 || exp_contains_a_ptr e2 || exp_contains_a_ptr e3 + | Lval lval + | AddrOf lval + | StartOf lval -> lval_contains_a_ptr lval + + and lval_contains_a_ptr (lval:lval) = + let lval_is_of_ptr_type lval = + match typeOfLval lval with + | TPtr _ -> true + | _ -> false + in + let (host, offset) = lval in + let host_contains_a_ptr h = + match h with + | Var v -> isPointerType v.vtype + | Mem e -> exp_contains_a_ptr e + in + let rec offset_contains_a_ptr o = + match o with + | NoOffset -> false + | Index (e, o) -> exp_contains_a_ptr e || offset_contains_a_ptr o + | Field (f, o) -> isPointerType f.ftype || offset_contains_a_ptr o + in + lval_is_of_ptr_type lval || host_contains_a_ptr host || offset_contains_a_ptr offset + + let calc_lval_type_size (lval:lval) = + begin try + let t = typeOfLval lval in + match sizeOf t with + | Const (CInt(i, _, _)) -> Some (cilint_to_int i) + | _ -> None + with _ -> None + end + + let get_ptr_size_for_lval ctx (lval:lval) = + match lval_points_to_heap ctx lval with + | true -> (* We're dealing with a ptr that points to the heap *) + begin match ctx.ask (Queries.BlobSize (mkAddrOf lval)) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> Some (IntOps.BigIntOps.to_int i) + | None -> None + end + | _ -> None + end + (* Assumption here is that if it's not a heap ptr, then it's a stack ptr and the ptr size would be the lval type's size *) + | false -> calc_lval_type_size lval + + let rec get_offset_size ctx offset = + match offset with + | NoOffset -> Some 0 + | Index (e, o) -> + begin match ctx.ask (Queries.EvalInt e) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> + begin match get_offset_size ctx o with + | Some os -> Some (IntOps.BigIntOps.to_int i + os) + | None -> None + end + | None -> None + end + | _ -> None + end + | Field (f, o) -> + let f_size = + begin match sizeOf f.ftype with + | Const (CInt (i, _, _)) -> Some (cilint_to_int i) + | _ -> None + end + in + begin match f_size, get_offset_size ctx o with + | Some fs, Some os -> Some (fs + os) + | _ -> None + end + + let check_lval_for_oob_access ctx (lval:lval) = + match lval_contains_a_ptr lval with + | false -> () + | true -> + let (_, offset) = lval in + begin match get_ptr_size_for_lval ctx lval, get_offset_size ctx offset with + | _, None -> M.warn "Offset unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval + | None, _ -> M.warn "Pointer size unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval + | Some pi, Some oi -> + if oi > pi then + M.warn "Must out-of-bounds memory access: Offset size (%d) is larger than pointer's memory size (%d) for lval %a" oi pi CilType.Lval.pretty lval + end + + let rec check_exp_for_oob_access ctx (exp:exp) = + match exp with + | Const _ + | SizeOf _ + | SizeOfStr _ + | AlignOf _ + | AddrOfLabel _ -> () + | Real e + | Imag e + | SizeOfE e + | AlignOfE e + | UnOp (_, e, _) + | CastE (_, e) -> check_exp_for_oob_access ctx e + | BinOp (_, e1, e2, _) -> + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2 + | Question (e1, e2, e3, _) -> + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2; + check_exp_for_oob_access ctx e3 + | Lval lval + | StartOf lval + | AddrOf lval -> check_lval_for_oob_access ctx lval + + + (* TRANSFER FUNCTIONS *) + + let assign ctx (lval:lval) (rval:exp) : D.t = + check_lval_for_oob_access ctx lval; + check_exp_for_oob_access ctx rval; + ctx.local + + let branch ctx (exp:exp) (tv:bool) : D.t = + check_exp_for_oob_access ctx exp; + ctx.local + + let return ctx (exp:exp option) (f:fundec) : D.t = + Option.iter (fun x -> check_exp_for_oob_access ctx x) exp; + ctx.local + + let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; + List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; + 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 = + ctx.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 = + Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; + List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; + ctx.local + + let startstate v = (*D.empty*) () + let exitstate v = (*D.empty*) () +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) \ No newline at end of file From a5c693efd417720c2baccfd19782cc8592f1f262 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 27 Jun 2023 16:24:21 +0200 Subject: [PATCH 038/401] Add 2 simple regression tests for memory OOB --- tests/regression/73-mem-oob/01-oob-heap-simple.c | 14 ++++++++++++++ tests/regression/73-mem-oob/02-oob-stack-simple.c | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/regression/73-mem-oob/01-oob-heap-simple.c create mode 100644 tests/regression/73-mem-oob/02-oob-stack-simple.c diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/73-mem-oob/01-oob-heap-simple.c new file mode 100644 index 0000000000..91d9c7605a --- /dev/null +++ b/tests/regression/73-mem-oob/01-oob-heap-simple.c @@ -0,0 +1,14 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + *ptr = 'a';//NOWARN + *(ptr + 1) = 'b';//NOWARN + *(ptr + 10) = 'c';//WARN + + free(ptr); + + return 0; +} diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/73-mem-oob/02-oob-stack-simple.c new file mode 100644 index 0000000000..deedd52781 --- /dev/null +++ b/tests/regression/73-mem-oob/02-oob-stack-simple.c @@ -0,0 +1,12 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include + +int main(int argc, char const *argv[]) { + int i = 42; + int *ptr = &i; + + *ptr = 5;//NOWARN + *(ptr + 10) = 55;//WARN + + return 0; +} From 511fd366c0181999023f57e0e8a592d5f745f319 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 16:32:03 +0200 Subject: [PATCH 039/401] 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 f3507455563fa0b9e23f25fe7dfae605d53c04ca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 1 Jul 2023 18:33:43 +0200 Subject: [PATCH 040/401] Stick with Lattice.Unit --- src/analyses/memOutOfBounds.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index db4066d442..9648f35610 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -5,7 +5,7 @@ module Spec = struct include Analyses.IdentitySpec - module D = Lattice.Unit(*ValueDomain.AddrSetDomain*) + module D = Lattice.Unit module C = Lattice.Unit (* TODO: Do this later *) @@ -186,8 +186,8 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; ctx.local - let startstate v = (*D.empty*) () - let exitstate v = (*D.empty*) () + let startstate v = () + let exitstate v = () end let _ = From 1bff8fb0222ffe6ac77616bfe1a713aa7ec70f6d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:05:40 +0200 Subject: [PATCH 041/401] 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 042/401] 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 043/401] 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 044/401] 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 045/401] 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 4d5e0dcabe77afa11102f07c50971b12f18b1037 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:26:07 +0200 Subject: [PATCH 046/401] Rewrite the majority of the logic and checks performed in memOutOfBounds --- src/analyses/memOutOfBounds.ml | 208 +++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 72 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9648f35610..d96c59e105 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -15,15 +15,12 @@ struct (* HELPER FUNCTIONS *) - let exp_points_to_heap ctx (exp:exp) = - match ctx.ask (Queries.MayPointTo exp) with - | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> - Queries.LS.elements a - |> List.map fst - |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) - | _ -> false - let lval_points_to_heap ctx (lval:lval) = exp_points_to_heap ctx (mkAddrOf lval) + (* A safe way to call [cilint_to_int] without having to worry about exceptions *) + let cilint_to_int_wrapper i = + try + Some (cilint_to_int i) + with _ -> None let rec exp_contains_a_ptr (exp:exp) = match exp with @@ -47,90 +44,131 @@ struct | StartOf lval -> lval_contains_a_ptr lval and lval_contains_a_ptr (lval:lval) = - let lval_is_of_ptr_type lval = - match typeOfLval lval with - | TPtr _ -> true - | _ -> false - in let (host, offset) = lval in - let host_contains_a_ptr h = - match h with + let host_contains_a_ptr = function | Var v -> isPointerType v.vtype | Mem e -> exp_contains_a_ptr e in - let rec offset_contains_a_ptr o = - match o with + let rec offset_contains_a_ptr = function | NoOffset -> false | Index (e, o) -> exp_contains_a_ptr e || offset_contains_a_ptr o | Field (f, o) -> isPointerType f.ftype || offset_contains_a_ptr o in - lval_is_of_ptr_type lval || host_contains_a_ptr host || offset_contains_a_ptr offset + host_contains_a_ptr host || offset_contains_a_ptr offset - let calc_lval_type_size (lval:lval) = - begin try - let t = typeOfLval lval in - match sizeOf t with - | Const (CInt(i, _, _)) -> Some (cilint_to_int i) - | _ -> None - with _ -> None - end - - let get_ptr_size_for_lval ctx (lval:lval) = - match lval_points_to_heap ctx lval with - | true -> (* We're dealing with a ptr that points to the heap *) - begin match ctx.ask (Queries.BlobSize (mkAddrOf lval)) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> Some (IntOps.BigIntOps.to_int i) - | None -> None - end - | _ -> None + let lval_is_ptr_var (lval:lval) = + let (host, _) = lval in + match host with + | Var v -> isPointerType v.vtype + (* Intuition: If the lval has a Mem host, then it's not a direct ptr which is what we're looking for here *) + | Mem e -> false + + let exp_points_to_heap ctx (exp:exp) = + match ctx.ask (Queries.MayPointTo exp) with + | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> + Queries.LS.elements a + |> List.map fst + |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) + | _ -> false (* TODO: Is this sound? Maybe not quite. *) + + let get_size_for_heap_ptr ctx (exp:exp) = + (* TODO: + BlobSize always seems to respond with top when it's passed an Lval exp of a ptr var which in turn contains mem allocated via malloc. + Am I doing smth wrong here? + *) + match ctx.ask (Queries.BlobSize exp) with + | a when not (Queries.ID.is_top a) -> + begin match Queries.ID.to_int a with + | Some i -> Some (IntOps.BigIntOps.to_int i) + | None -> None end - (* Assumption here is that if it's not a heap ptr, then it's a stack ptr and the ptr size would be the lval type's size *) - | false -> calc_lval_type_size lval + | _ -> None + + (* TODO: Here we assume that the given exp is a Lval exp *) + let get_size_for_stack_ptr ctx (exp:exp) = + match exp with + | Lval lval -> + if lval_is_ptr_var lval then + let (host, _) = lval in + begin match host with + | Var v -> + begin match sizeOf v.vtype with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i + | _ -> None + end + | _ -> None + end + else None + | _ -> None + + let get_ptr_size_for_exp ctx (exp:exp) = + match exp_points_to_heap ctx exp with + (* We're dealing with a ptr that points to the heap *) + | true -> get_size_for_heap_ptr ctx exp + (* Assumption here is that if it doesn't point to the heap, then it points to the stack *) + | false -> get_size_for_stack_ptr ctx exp - let rec get_offset_size ctx offset = - match offset with + (** + * If we get [None], then the offset's size/value is unknown + * In the case [NoOffset], [Some 0] indicates that this offset type simply has value 0 + *) + let rec get_offset_size = function | NoOffset -> Some 0 | Index (e, o) -> - begin match ctx.ask (Queries.EvalInt e) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> - begin match get_offset_size ctx o with - | Some os -> Some (IntOps.BigIntOps.to_int i + os) - | None -> None - end - | None -> None - end + let exp_val = begin match constFold true e with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i | _ -> None end - | Field (f, o) -> - let f_size = - begin match sizeOf f.ftype with - | Const (CInt (i, _, _)) -> Some (cilint_to_int i) - | _ -> None - end in - begin match f_size, get_offset_size ctx o with - | Some fs, Some os -> Some (fs + os) - | _ -> None + begin match exp_val, get_offset_size o with + | Some ei, Some oi -> Some (ei + oi) + | _, _ -> None + end + | Field (f, o) -> + begin match get_offset_size o, sizeOf f.ftype with + | Some oi, Const (CInt (i, _, _)) -> + begin match cilint_to_int_wrapper i with + | Some i -> Some (oi + i) + | None -> None + end + | _, _ -> None end - let check_lval_for_oob_access ctx (lval:lval) = + let rec check_lval_for_oob_access ctx (lval:lval) = match lval_contains_a_ptr lval with - | false -> () + | false -> () (* Nothing to do here *) | true -> - let (_, offset) = lval in - begin match get_ptr_size_for_lval ctx lval, get_offset_size ctx offset with - | _, None -> M.warn "Offset unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval - | None, _ -> M.warn "Pointer size unknown, potential out-of-bounds access for lval %a" CilType.Lval.pretty lval - | Some pi, Some oi -> - if oi > pi then - M.warn "Must out-of-bounds memory access: Offset size (%d) is larger than pointer's memory size (%d) for lval %a" oi pi CilType.Lval.pretty lval - end + let (host, offset) = lval in + match host, get_offset_size offset with + | _, None -> M.warn "Offset size for lval %a not known. May have a memory out-of-bounds access" CilType.Lval.pretty lval + | Var v, Some oi -> + begin match sizeOf v.vtype with + | Const (CInt (i, _, _)) -> + begin match cilint_to_int_wrapper i with + | Some i -> + if i < oi then + M.warn "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + end + | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + end + | Mem e, Some oi -> + check_exp_for_oob_access ctx e; + (* TODO: + * Not sure if we actually need these checks below. + * They never seem to apply + * I.e., for ptrs, it always seems to be the case that we have a binop which adds the offset to the ptr + instead of having the offset represented as an offset of the lval + * For this reason, the checks below are currently commented out + *) + (* begin match get_ptr_size_for_exp ctx e with + | Some ei -> + if ei < oi then + M.warn "Offset bigger than size of pointer memory, denoted by expression %a in lval %a" d_exp e CilType.Lval.pretty lval + | _ -> M.warn "Unknown size of pointer memory, denoted by exp %a for lval %a" d_exp e CilType.Lval.pretty lval + end *) - let rec check_exp_for_oob_access ctx (exp:exp) = + and check_exp_for_oob_access ctx (exp:exp) = match exp with | Const _ | SizeOf _ @@ -143,7 +181,8 @@ struct | AlignOfE e | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e - | BinOp (_, e1, e2, _) -> + | BinOp (bop, e1, e2, _) -> + check_binop_exp ctx bop e1 e2; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -154,10 +193,35 @@ struct | StartOf lval | AddrOf lval -> check_lval_for_oob_access ctx lval + and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = + match binop with + | PlusPI + | IndexPI + | MinusPI -> + let ptr_size = get_ptr_size_for_exp ctx e1 in + let offset_size = eval_ptr_offset_in_binop e2 in + begin match ptr_size, offset_size with + | Some pi, Some oi -> + if pi < oi then + M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known" d_exp e1 CilType.Binop.pretty binop d_exp e2 + end + | _ -> () + + and eval_ptr_offset_in_binop (exp:exp) = + match constFold true exp with + | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i + | _ -> None (* TODO: Maybe try to also Eval the exp via Queries and not rely only on constFold *) + (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = + let (host,_) = lval in + match host with + | Mem e -> ignore (Pretty.printf "Lval host is Mem e and e is %a\n" d_plainexp e); + | _ -> (); check_lval_for_oob_access ctx lval; check_exp_for_oob_access ctx rval; ctx.local From 098501b62a5f87d764eaf33a9468d576bbcb1435 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:28:18 +0200 Subject: [PATCH 047/401] Remove printf calls --- src/analyses/memOutOfBounds.ml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index d96c59e105..58da08c7bc 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -218,10 +218,6 @@ struct (* TRANSFER FUNCTIONS *) let assign ctx (lval:lval) (rval:exp) : D.t = - let (host,_) = lval in - match host with - | Mem e -> ignore (Pretty.printf "Lval host is Mem e and e is %a\n" d_plainexp e); - | _ -> (); check_lval_for_oob_access ctx lval; check_exp_for_oob_access ctx rval; ctx.local From 6496c61ea12a9c686f3b09c2609c2ea88e9b0c0d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:30:43 +0200 Subject: [PATCH 048/401] Improve warning messages a bit --- src/analyses/memOutOfBounds.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 58da08c7bc..d11d876f5b 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -140,7 +140,7 @@ struct | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn "Offset size for lval %a not known. May have a memory out-of-bounds access" CilType.Lval.pretty lval + | _, None -> M.warn "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> @@ -203,9 +203,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From a796a0b4963cfae32177d33af27f1e49935ab234 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:37:17 +0200 Subject: [PATCH 049/401] Add message category for memory OOB access --- src/analyses/memOutOfBounds.ml | 17 ++++++++++------- src/util/messageCategory.ml | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index d11d876f5b..7ea11e8ef1 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -1,5 +1,6 @@ open GoblintCil open Analyses +open MessageCategory module Spec = struct @@ -135,22 +136,23 @@ struct end let rec check_lval_for_oob_access ctx (lval:lval) = + let undefined_behavior = Undefined MemoryOutOfBoundsAccess in match lval_contains_a_ptr lval with | false -> () (* Nothing to do here *) | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval + | _, None -> M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - M.warn "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + M.warn ~category:(Behavior undefined_behavior) "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -194,6 +196,7 @@ struct | AddrOf lval -> check_lval_for_oob_access ctx lval and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = + let undefined_behavior = Undefined MemoryOutOfBoundsAccess in match binop with | PlusPI | IndexPI @@ -203,9 +206,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior undefined_behavior) "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn ~category:(Behavior undefined_behavior) "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn ~category:(Behavior undefined_behavior) "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () diff --git a/src/util/messageCategory.ml b/src/util/messageCategory.ml index ef8ee5d6a9..8190f79294 100644 --- a/src/util/messageCategory.ml +++ b/src/util/messageCategory.ml @@ -11,6 +11,7 @@ type undefined_behavior = | ArrayOutOfBounds of array_oob | NullPointerDereference | UseAfterFree + | MemoryOutOfBoundsAccess | Uninitialized | Other [@@deriving eq, ord, hash] @@ -62,6 +63,7 @@ struct let array_out_of_bounds e: category = create @@ ArrayOutOfBounds e let nullpointer_dereference: category = create @@ NullPointerDereference let use_after_free: category = create @@ UseAfterFree + let memory_out_of_bounds_access: category = create @@ MemoryOutOfBoundsAccess let uninitialized: category = create @@ Uninitialized let other: category = create @@ Other @@ -97,6 +99,7 @@ struct | "array_out_of_bounds" -> ArrayOutOfBounds.from_string_list t | "nullpointer_dereference" -> nullpointer_dereference | "use_after_free" -> use_after_free + | "memory_out_of_bounds_access" -> memory_out_of_bounds_access | "uninitialized" -> uninitialized | "other" -> other | _ -> Unknown @@ -106,6 +109,7 @@ struct | ArrayOutOfBounds e -> "ArrayOutOfBounds" :: ArrayOutOfBounds.path_show e | NullPointerDereference -> ["NullPointerDereference"] | UseAfterFree -> ["UseAfterFree"] + | MemoryOutOfBoundsAccess -> ["MemoryOutOfBoundsAccess"] | Uninitialized -> ["Uninitialized"] | Other -> ["Other"] end @@ -214,6 +218,7 @@ let behaviorName = function |Undefined u -> match u with |NullPointerDereference -> "NullPointerDereference" |UseAfterFree -> "UseAfterFree" + |MemoryOutOfBoundsAccess -> "MemoryOutOfBoundsAccess" |Uninitialized -> "Uninitialized" |Other -> "Other" | ArrayOutOfBounds aob -> match aob with From 84e80b1b7f97a0077dae1b63f974c8b0c62d8108 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:42:08 +0200 Subject: [PATCH 050/401] Add CWE number 823 for memory OOB access warnings --- src/analyses/memOutOfBounds.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 7ea11e8ef1..ac2b909d6a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -137,6 +137,7 @@ struct let rec check_lval_for_oob_access ctx (lval:lval) = let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in match lval_contains_a_ptr lval with | false -> () (* Nothing to do here *) | true -> @@ -149,10 +150,10 @@ struct begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - M.warn ~category:(Behavior undefined_behavior) "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn ~category:(Behavior undefined_behavior) "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -197,6 +198,7 @@ struct and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in match binop with | PlusPI | IndexPI @@ -206,9 +208,9 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - M.warn ~category:(Behavior undefined_behavior) "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn ~category:(Behavior undefined_behavior) "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn ~category:(Behavior undefined_behavior) "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From a07b6903ada4aed962efdba4619657d41273bfe8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 16:59:44 +0200 Subject: [PATCH 051/401] Add a global variable that indicates whether an invalid pointer deref happened --- src/analyses/memOutOfBounds.ml | 24 +++++++++++++++++++----- src/util/goblintutil.ml | 3 +++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index ac2b909d6a..415934b52d 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -2,6 +2,8 @@ open GoblintCil open Analyses open MessageCategory +module GU = Goblintutil + module Spec = struct include Analyses.IdentitySpec @@ -143,17 +145,24 @@ struct | true -> let (host, offset) = lval in match host, get_offset_size offset with - | _, None -> M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval + | _, None -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with | Const (CInt (i, _, _)) -> begin match cilint_to_int_wrapper i with | Some i -> if i < oi then + GU.may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end - | _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval + | _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> check_exp_for_oob_access ctx e; @@ -208,9 +217,14 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then + GU.may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + | None, _ -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 + | _, None -> + GU.may_invalid_deref := true; + M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () diff --git a/src/util/goblintutil.ml b/src/util/goblintutil.ml index 2c49395915..ecaa38f338 100644 --- a/src/util/goblintutil.ml +++ b/src/util/goblintutil.ml @@ -14,6 +14,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether an invalid pointer dereference happened *) +let may_invalid_deref = ref false + (** The file where everything is output *) let out = ref stdout From 4c093726b52ba1e3e0966ec5cded850d36bc87b6 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:46:50 +0200 Subject: [PATCH 052/401] Add OOB memory access regression case with a loop --- tests/regression/73-mem-oob/03-oob-loop.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/73-mem-oob/03-oob-loop.c diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/73-mem-oob/03-oob-loop.c new file mode 100644 index 0000000000..5e0e08c381 --- /dev/null +++ b/tests/regression/73-mem-oob/03-oob-loop.c @@ -0,0 +1,16 @@ +// PARAM: --set ana.activated[+] memOutOfBounds +#include +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + for (int i = 0; i < 10; i++) { + ptr++; + } + + printf("%s", *ptr); //WARN + free(ptr); //WARN + + return 0; +} From a57112b0316c9000c35ee639198a299e5059fb7c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 16:54:03 +0200 Subject: [PATCH 053/401] Add regression test case with a joined thread --- ...13-multi-threaded-uaf-with-joined-thread.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c new file mode 100644 index 0000000000..140e66eff8 --- /dev/null +++ b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c @@ -0,0 +1,33 @@ +//PARAM: --set ana.activated[+] useAfterFree +#include +#include +#include + +int* gptr; + +// Mutex to ensure we don't get race warnings, but the UAF warnings we actually care about +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + +void *t_use(void* p) { + pthread_mutex_lock(&mtx); + *gptr = 0; //NOWARN + pthread_mutex_unlock(&mtx); +} + +int main() { + gptr = malloc(sizeof(int)); + *gptr = 42; + + pthread_t using_thread; + pthread_create(&using_thread, NULL, t_use, NULL); + + // Join using_thread before freeing gptr in the main thread + pthread_join(using_thread, NULL); + + pthread_mutex_lock(&mtx); + *gptr = 43; //NOWARN + free(gptr); //WARN + pthread_mutex_unlock(&mtx); + + return 0; +} \ No newline at end of file From 0b3bfc99d1c261be0806de1f2e45e6fe7b5eea78 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 17:31:25 +0200 Subject: [PATCH 054/401] Add global variable for detection of UAF occurrences --- src/analyses/useAfterFree.ml | 14 +++++++++++--- src/framework/analysisState.ml | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c3aebc985e..591cbba448 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -50,17 +50,24 @@ struct match get_current_threadid ctx with | `Lifted current -> let possibly_started = ThreadIdSet.exists (possibly_started current) freeing_threads in - if possibly_started then + if possibly_started then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var + end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in - if not current_is_unique && any_equal_current freeing_threads then + if not current_is_unique && any_equal_current freeing_threads then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var - else if D.mem heap_var ctx.local then + end + else if D.mem heap_var ctx.local then begin + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var + end end | `Top -> + AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -86,6 +93,7 @@ struct | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = if D.mem var state then + AnalysisState.svcomp_may_use_after_free := true; 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 = diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 0f3a9f55bc..385fa26aef 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,6 +7,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether a Use-After-Free (UAF) happened *) +let svcomp_may_use_after_free = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From 77f7e89a6cd42ab2052cb5afab7b816918b8f1e3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 26 Jul 2023 17:31:49 +0200 Subject: [PATCH 055/401] Add SVComp result generation for memory safety Currently only considering UAF --- src/autoTune.ml | 4 ++++ src/witness/svcomp.ml | 1 + src/witness/svcompSpec.ml | 4 ++++ src/witness/witness.ml | 30 ++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index a267c3bf9b..bce12302cd 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -185,6 +185,7 @@ let enableAnalyses anas = (*does not consider dynamic calls!*) let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] +let memorySafetyAnalyses = ["useAfterFree"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true @@ -219,6 +220,9 @@ let focusOnSpecification () = | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true + | MemorySafety -> (* Enable the memory safety analyses *) + print_endline @@ "Specification: MemorySafety -> enabling memory safety analyses \"" ^ (String.concat ", " memorySafetyAnalyses) ^ "\""; + enableAnalyses memorySafetyAnalyses (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index a5a572d1c2..b762d2eb5d 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,6 +52,7 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) + | MemorySafety -> "memory-safety" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 464c170251..784155ab9b 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,6 +6,7 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow + | MemorySafety let of_string s = let s = String.strip s in @@ -16,6 +17,8 @@ let of_string s = NoDataRace else if global_not = "overflow" then NoOverflow + else if global_not = "memory-safety" then + MemorySafety else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -42,5 +45,6 @@ let to_string spec = | UnreachCall f -> "call(" ^ f ^ "())" | NoDataRace -> "data-race" | NoOverflow -> "overflow" + | MemorySafety -> "memory-safety" in "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" diff --git a/src/witness/witness.ml b/src/witness/witness.ml index aff9a01383..6dc0d04034 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -472,6 +472,36 @@ struct in (module TaskResult:WitnessTaskResult) ) + | MemorySafety -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_use_after_free then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) let write entrystates = From 91388efe40f0ea7f432068bfa34c91cce00a82b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jul 2023 12:42:59 +0300 Subject: [PATCH 056/401] 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 057/401] 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 058/401] 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 059/401] 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 060/401] 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 061/401] 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 cfef9bd7d3f08fb578bc44608bd6af549785d48e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 14:13:32 +0200 Subject: [PATCH 062/401] Fix multi-line if expression --- src/analyses/useAfterFree.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 591cbba448..3b189a6dea 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -92,9 +92,10 @@ struct 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) -> let warn_for_heap_var var = - if D.mem var state then + if D.mem var state then begin AnalysisState.svcomp_may_use_after_free := true; 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 + end in let pointed_to_heap_vars = Queries.LS.elements a From cbaffbb8de505036a1058d42376ded1630116965 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 30 Jul 2023 15:00:07 +0200 Subject: [PATCH 063/401] Side-effect a tuple of tids and sets of joined threads This aims to improve precision --- src/analyses/useAfterFree.ml | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 3b189a6dea..31203e505d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -5,7 +5,7 @@ open Analyses open MessageCategory module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) -module ThreadIdSet = SetDomain.Make(ThreadIdDomain.ThreadLifted) +module ThreadIdWithJoinedThreads = SetDomain.Make(Lattice.Prod(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet)) module Spec : Analyses.MCPSpec = struct @@ -15,7 +15,7 @@ struct module D = ToppedVarInfoSet module C = Lattice.Unit - module G = ThreadIdSet + module G = ThreadIdWithJoinedThreads module V = VarinfoV (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) @@ -27,36 +27,41 @@ struct let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId + let get_joined_threads ctx = + ctx.ask Queries.MustJoinedThreads + let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) - if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdSet.is_empty freeing_threads then () + if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () else begin let possibly_started current = function - | `Lifted tid -> - let threads = ctx.ask Queries.CreatedThreads in + | `Lifted tid, joined_threads -> + let created_threads = ctx.ask Queries.CreatedThreads in + (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) + let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in let not_started = MHP.definitely_not_started (current, threads) tid in let possibly_started = not not_started in possibly_started - | `Top -> true - | `Bot -> false + | `Top, _ -> true + | `Bot, _ -> false in let equal_current current = function - | `Lifted tid -> + | `Lifted tid, _ -> ThreadId.Thread.equal current tid - | `Top -> true - | `Bot -> false + | `Top, _ -> true + | `Bot, _ -> false in match get_current_threadid ctx with | `Lifted current -> - let possibly_started = ThreadIdSet.exists (possibly_started current) freeing_threads in + let possibly_started = ThreadIdWithJoinedThreads.exists (possibly_started current) freeing_threads in if possibly_started then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in - let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in + let any_equal_current threads = ThreadIdWithJoinedThreads.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var @@ -134,9 +139,25 @@ struct | StartOf lval | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval - let side_effect_mem_free ctx freed_heap_vars threadid = - let threadid = G.singleton threadid in - D.iter (fun var -> ctx.sideg var threadid) freed_heap_vars + let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = + let side_effect_globals_to_heap_var heap_var globals_to_add = + let (tid_to_add, joined_threads_to_add) = globals_to_add in + let current_globals = ctx.global heap_var in + (* + * Check if there are tuples with the same first component (i.e., tid) + * If no, side-effect the globals that we receive here and be done + * If yes, join all second components together and side-effect a single global with + the tid as first component and the joined second components as a single second component + *) + let globals_with_same_tid = G.filter (fun (tid, _) -> ThreadIdDomain.ThreadLifted.equal tid tid_to_add) current_globals in + if G.is_empty globals_with_same_tid then + ctx.sideg heap_var (G.singleton globals_to_add) + else + let globals_to_add = G.fold (fun (t, j) (t_acc, j_acc) -> (t_acc, ConcDomain.MustThreadSet.join j j_acc)) globals_with_same_tid (tid_to_add, joined_threads_to_add) in + ctx.sideg heap_var (G.singleton globals_to_add) + in + let globals_to_side_effect = (threadid, joined_threads) in + D.iter (fun var -> side_effect_globals_to_heap_var var globals_to_side_effect) freed_heap_vars (* TRANSFER FUNCTIONS *) @@ -196,8 +217,9 @@ struct |> D.of_list 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); - D.join state (pointed_to_heap_vars) (* Add all heap vars, which ptr points to, to the state *) + side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx) (get_joined_threads ctx); + (* Add all heap vars, which ptr points to, to the state *) + D.join state (pointed_to_heap_vars) | _ -> state end | _ -> state From c01585dac6ecc8d2efb42ff21c302ddcb675d705 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 31 Jul 2023 18:50:25 +0200 Subject: [PATCH 064/401] Warn if threadJoins is deactivated --- src/analyses/useAfterFree.ml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 31203e505d..9a6484f624 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -1,5 +1,6 @@ (** An analysis for the detection of use-after-free vulnerabilities ([useAfterFree]). *) +open GobConfig open GoblintCil open Analyses open MessageCategory @@ -30,7 +31,16 @@ struct let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads + let warn_about_deactivated_thread_joins () = + if not @@ List.mem "threadJoins" @@ get_string_list "ana.activated" then + M.warn "Running without thread joins analysis. Multi-threaded UAF detection might be imprecise!" + let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = + (* + * We need the [threadJoins] analysis for making the multi-threaded UAF detection more precise + * Warn the user in case [threadJoins] is disabled + *) + warn_about_deactivated_thread_joins (); let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () From 49a6d5440099df0f3e730a6c7949c680809a5f7e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 31 Jul 2023 18:54:20 +0200 Subject: [PATCH 065/401] Update joined thread test case annotations --- .../13-multi-threaded-uaf-with-joined-thread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c index 140e66eff8..2ce291f9d1 100644 --- a/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c +++ b/tests/regression/74-use_after_free/13-multi-threaded-uaf-with-joined-thread.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.activated[+] useAfterFree +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins #include #include #include @@ -26,7 +26,7 @@ int main() { pthread_mutex_lock(&mtx); *gptr = 43; //NOWARN - free(gptr); //WARN + free(gptr); //NOWARN pthread_mutex_unlock(&mtx); return 0; From 49fa590a1b481b90de416fc93cc4f13b8f86b489 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 20:02:28 +0200 Subject: [PATCH 066/401] 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 067/401] 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 068/401] 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 069/401] 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 b02d0a743f51fd0afd79a6ba86eb46556bfc1e1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 08:53:23 +0200 Subject: [PATCH 070/401] Remove threadJoins activation warning --- src/analyses/useAfterFree.ml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 9a6484f624..437f19264b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -31,16 +31,7 @@ struct let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads - let warn_about_deactivated_thread_joins () = - if not @@ List.mem "threadJoins" @@ get_string_list "ana.activated" then - M.warn "Running without thread joins analysis. Multi-threaded UAF detection might be imprecise!" - let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = - (* - * We need the [threadJoins] analysis for making the multi-threaded UAF detection more precise - * Warn the user in case [threadJoins] is disabled - *) - warn_about_deactivated_thread_joins (); let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () From 50f20b0e1db69285385c28fe54bfc30d758fc163 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 09:55:10 +0200 Subject: [PATCH 071/401] Use a map domain for the globals --- src/analyses/useAfterFree.ml | 53 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 437f19264b..c1f963b466 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -6,7 +6,7 @@ open Analyses open MessageCategory module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) -module ThreadIdWithJoinedThreads = SetDomain.Make(Lattice.Prod(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet)) +module ThreadIdToJoinedThreadsMap = MapDomain.MapBot(ThreadIdDomain.ThreadLifted)(ConcDomain.MustThreadSet) module Spec : Analyses.MCPSpec = struct @@ -16,7 +16,7 @@ struct module D = ToppedVarInfoSet module C = Lattice.Unit - module G = ThreadIdWithJoinedThreads + module G = ThreadIdToJoinedThreadsMap module V = VarinfoV (** TODO: Try out later in benchmarks to see how we perform with and without context-sensititivty *) @@ -34,35 +34,37 @@ struct let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) - if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || ThreadIdWithJoinedThreads.is_empty freeing_threads then () + if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () else begin - let possibly_started current = function - | `Lifted tid, joined_threads -> + let possibly_started current tid joined_threads = + match tid with + | `Lifted tid -> let created_threads = ctx.ask Queries.CreatedThreads in (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in let not_started = MHP.definitely_not_started (current, threads) tid in let possibly_started = not not_started in possibly_started - | `Top, _ -> true - | `Bot, _ -> false + | `Top -> true + | `Bot -> false in - let equal_current current = function - | `Lifted tid, _ -> + let equal_current current tid joined_threads = + match tid with + | `Lifted tid -> ThreadId.Thread.equal current tid - | `Top, _ -> true - | `Bot, _ -> false + | `Top -> true + | `Bot -> false in match get_current_threadid ctx with | `Lifted current -> - let possibly_started = ThreadIdWithJoinedThreads.exists (possibly_started current) freeing_threads in + let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in - let any_equal_current threads = ThreadIdWithJoinedThreads.exists (equal_current current) threads in + let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin AnalysisState.svcomp_may_use_after_free := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var @@ -141,24 +143,17 @@ struct | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = - let side_effect_globals_to_heap_var heap_var globals_to_add = - let (tid_to_add, joined_threads_to_add) = globals_to_add in + let side_effect_globals_to_heap_var heap_var = let current_globals = ctx.global heap_var in - (* - * Check if there are tuples with the same first component (i.e., tid) - * If no, side-effect the globals that we receive here and be done - * If yes, join all second components together and side-effect a single global with - the tid as first component and the joined second components as a single second component - *) - let globals_with_same_tid = G.filter (fun (tid, _) -> ThreadIdDomain.ThreadLifted.equal tid tid_to_add) current_globals in - if G.is_empty globals_with_same_tid then - ctx.sideg heap_var (G.singleton globals_to_add) - else - let globals_to_add = G.fold (fun (t, j) (t_acc, j_acc) -> (t_acc, ConcDomain.MustThreadSet.join j j_acc)) globals_with_same_tid (tid_to_add, joined_threads_to_add) in - ctx.sideg heap_var (G.singleton globals_to_add) + let joined_threads_to_add = match G.find_opt threadid current_globals with + | Some threads -> ConcDomain.ThreadSet.inter joined_threads threads + | None -> joined_threads + in + let globals_to_side_effect = G.add threadid joined_threads_to_add current_globals in + ctx.sideg heap_var globals_to_side_effect in - let globals_to_side_effect = (threadid, joined_threads) in - D.iter (fun var -> side_effect_globals_to_heap_var var globals_to_side_effect) freed_heap_vars + D.iter side_effect_globals_to_heap_var freed_heap_vars + (* TRANSFER FUNCTIONS *) From 027b9cda653b95e63f904b417256da1b476546bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Aug 2023 11:41:35 +0300 Subject: [PATCH 072/401] 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 073/401] 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 074/401] 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 075/401] 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 076/401] 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 077/401] 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 078/401] 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 079/401] 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 080/401] 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 081/401] 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 082/401] 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 083/401] 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 084/401] 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 085/401] 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 086/401] 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 087/401] 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 088/401] 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 089/401] 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 c90fb0f11a3070c9c92534232a06008b0c2beb32 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 14:27:40 +0200 Subject: [PATCH 090/401] Add global vars for all three memory safety SVComp properties --- src/framework/analysisState.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 385fa26aef..e987c414f2 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -10,6 +10,12 @@ let svcomp_may_overflow = ref false (** Whether a Use-After-Free (UAF) happened *) let svcomp_may_use_after_free = ref false +(** Whether an invalid pointer dereference happened *) +let svcomp_may_invalid_deref = ref false + +(** Whether an invalid memtrack happened *) +let svcomp_may_invalid_memtrack = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From e28f72b22f55fa0412d14b5877c8c90577c1c3eb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 2 Aug 2023 14:29:13 +0200 Subject: [PATCH 091/401] Improve SVComp result generation and support all 3 memory-safety props --- src/autoTune.ml | 11 ++++--- src/witness/svcomp.ml | 4 ++- src/witness/svcompSpec.ml | 41 +++++++++++++++++++------- src/witness/witness.ml | 62 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index bce12302cd..d532081799 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -185,7 +185,6 @@ let enableAnalyses anas = (*does not consider dynamic calls!*) let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"] -let memorySafetyAnalyses = ["useAfterFree"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true @@ -220,9 +219,13 @@ let focusOnSpecification () = | NoOverflow -> (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; set_bool "ana.int.interval" true - | MemorySafety -> (* Enable the memory safety analyses *) - print_endline @@ "Specification: MemorySafety -> enabling memory safety analyses \"" ^ (String.concat ", " memorySafetyAnalyses) ^ "\""; - enableAnalyses memorySafetyAnalyses + | ValidFree -> (* Enable the useAfterFree analysis *) + let uafAna = ["useAfterFree"] in + print_endline @@ "Specification: ValidFree -> enabling useAfterFree analysis \"" ^ (String.concat ", " uafAna) ^ "\""; + enableAnalyses uafAna + (* TODO: Finish these two below later *) + | ValidDeref + | ValidMemtrack -> () (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index b762d2eb5d..f1ee18ed72 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -52,7 +52,9 @@ struct | UnreachCall _ -> "unreach-call" | NoOverflow -> "no-overflow" | NoDataRace -> "no-data-race" (* not yet in SV-COMP/Benchexec *) - | MemorySafety -> "memory-safety" + | ValidFree -> "valid-free" + | ValidDeref -> "valid-deref" + | ValidMemtrack -> "valid-memtrack" in "false(" ^ result_spec ^ ")" | Unknown -> "unknown" diff --git a/src/witness/svcompSpec.ml b/src/witness/svcompSpec.ml index 784155ab9b..8dafb8873c 100644 --- a/src/witness/svcompSpec.ml +++ b/src/witness/svcompSpec.ml @@ -6,19 +6,20 @@ type t = | UnreachCall of string | NoDataRace | NoOverflow - | MemorySafety + | ValidFree + | ValidDeref + | ValidMemtrack let of_string s = let s = String.strip s in - let regexp = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in - if Str.string_match regexp s 0 then + let regexp = Str.regexp "CHECK( init(main()), LTL(G \\(.*\\)) )" in + let regexp_negated = Str.regexp "CHECK( init(main()), LTL(G ! \\(.*\\)) )" in + if Str.string_match regexp_negated s 0 then let global_not = Str.matched_group 1 s in if global_not = "data-race" then NoDataRace else if global_not = "overflow" then NoOverflow - else if global_not = "memory-safety" then - MemorySafety else let call_regex = Str.regexp "call(\\(.*\\)())" in if Str.string_match call_regex global_not 0 then @@ -26,6 +27,16 @@ let of_string s = UnreachCall f else failwith "Svcomp.Specification.of_string: unknown global not expression" + else if Str.string_match regexp s 0 then + let global = Str.matched_group 1 s in + if global = "valid-free" then + ValidFree + else if global = "valid-deref" then + ValidDeref + else if global = "valid-memtrack" then + ValidMemtrack + else + failwith "Svcomp.Specification.of_string: unknown global expression" else failwith "Svcomp.Specification.of_string: unknown expression" @@ -41,10 +52,18 @@ let of_option () = of_string s let to_string spec = - let global_not = match spec with - | UnreachCall f -> "call(" ^ f ^ "())" - | NoDataRace -> "data-race" - | NoOverflow -> "overflow" - | MemorySafety -> "memory-safety" + let print_output spec_str is_neg = + if is_neg then + Printf.sprintf "CHECK( init(main()), LTL(G ! %s) )" spec_str + else + Printf.sprintf "CHECK( init(main()), LTL(G %s) )" spec_str + in + let spec_str, is_neg = match spec with + | UnreachCall f -> "call(" ^ f ^ "())", true + | NoDataRace -> "data-race", true + | NoOverflow -> "overflow", true + | ValidFree -> "valid-free", false + | ValidDeref -> "valid-deref", false + | ValidMemtrack -> "valid-memtrack", false in - "CHECK( init(main()), LTL(G ! " ^ global_not ^ ") )" + print_output spec_str is_neg diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 6dc0d04034..797541c606 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -472,7 +472,7 @@ struct in (module TaskResult:WitnessTaskResult) ) - | MemorySafety -> + | ValidFree -> let module TrivialArg = struct include Arg @@ -502,6 +502,66 @@ struct in (module TaskResult:WitnessTaskResult) ) + | ValidDeref -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_deref then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) + | ValidMemtrack -> + let module TrivialArg = + struct + include Arg + let next _ = [] + end + in + if not !AnalysisState.svcomp_may_invalid_memtrack then + let module TaskResult = + struct + module Arg = Arg + let result = Result.True + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + else ( + let module TaskResult = + struct + module Arg = TrivialArg + let result = Result.Unknown + let invariant _ = Invariant.none + let is_violation _ = false + let is_sink _ = false + end + in + (module TaskResult:WitnessTaskResult) + ) let write entrystates = 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 092/401] 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 093/401] 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 094/401] 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 095/401] 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 096/401] 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 097/401] 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 098/401] 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 099/401] 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 100/401] 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 101/401] 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 102/401] 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 103/401] 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 104/401] 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 105/401] 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 106/401] 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 107/401] 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 108/401] 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 109/401] 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 110/401] 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 111/401] 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 112/401] 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 113/401] 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 114/401] 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 115/401] 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 116/401] 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 503fb2416b4669aedbf03f531e0eb405e85c2cd3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:11:06 +0200 Subject: [PATCH 117/401] Remove wrongly overtaken goblintutil.ml --- src/util/goblintutil.ml | 166 ---------------------------------------- 1 file changed, 166 deletions(-) delete mode 100644 src/util/goblintutil.ml diff --git a/src/util/goblintutil.ml b/src/util/goblintutil.ml deleted file mode 100644 index ecaa38f338..0000000000 --- a/src/util/goblintutil.ml +++ /dev/null @@ -1,166 +0,0 @@ -(** Globally accessible flags and utility functions. *) - -open GoblintCil -open GobConfig - - -(** Outputs information about what the goblin is doing *) -(* let verbose = ref false *) - -(** If this is true we output messages and collect accesses. - This is set to true in control.ml before we verify the result (or already before solving if warn = 'early') *) -let should_warn = ref false - -(** Whether signed overflow or underflow happened *) -let svcomp_may_overflow = ref false - -(** Whether an invalid pointer dereference happened *) -let may_invalid_deref = ref false - -(** The file where everything is output *) -let out = ref stdout - -(** Command for assigning an id to a varinfo. All varinfos directly created by Goblint should be modified by this method *) -let create_var (var: varinfo) = - (* TODO Hack: this offset should preempt conflicts with ids generated by CIL *) - let start_id = 10_000_000_000 in - let hash = Hashtbl.hash { var with vid = 0 } in - let hash = if hash < start_id then hash + start_id else hash in - { var with vid = hash } - -(* Type invariant variables. *) -let type_inv_tbl = Hashtbl.create 13 -let type_inv (c:compinfo) : varinfo = - try Hashtbl.find type_inv_tbl c.ckey - with Not_found -> - let i = create_var (makeGlobalVar ("{struct "^c.cname^"}") (TComp (c,[]))) in - Hashtbl.add type_inv_tbl c.ckey i; - i - -let is_blessed (t:typ): varinfo option = - let me_gusta x = List.mem x (get_string_list "exp.unique") in - match unrollType t with - | TComp (ci,_) when me_gusta ci.cname -> Some (type_inv ci) - | _ -> (None : varinfo option) - - -(** A hack to see if we are currently doing global inits *) -let global_initialization = ref false - -(** Another hack to see if earlyglobs is enabled *) -let earlyglobs = ref false - -(** Whether currently in postsolver evaluations (e.g. verify, warn) *) -let postsolving = ref false - -(* None if verification is disabled, Some true if verification succeeded, Some false if verification failed *) -let verified : bool option ref = ref None - -let escape = XmlUtil.escape (* TODO: inline everywhere *) - - -(** Creates a directory and returns the absolute path **) -let create_dir name = - let dirName = GobFpath.cwd_append name in - GobSys.mkdir_or_exists dirName; - dirName - -(** Remove directory and its content, as "rm -rf" would do. *) -let rm_rf path = - let rec f path = - let path_str = Fpath.to_string path in - if Sys.is_directory path_str then begin - let files = Array.map (Fpath.add_seg path) (Sys.readdir path_str) in - Array.iter f files; - Unix.rmdir path_str - end else - Sys.remove path_str - in - f path - - -exception Timeout - -let timeout = Timeout.timeout - -let seconds_of_duration_string = - let unit = function - | "" | "s" -> 1 - | "m" -> 60 - | "h" -> 60 * 60 - | s -> failwith ("Unkown duration unit " ^ s ^ ". Supported units are h, m, s.") - in - let int_rest f s = Scanf.sscanf s "%u%s" f in - let split s = BatString.(head s 1, tail s 1) in - let rec f i s = - let u, r = split s in (* unit, rest *) - i * (unit u) + if r = "" then 0 else int_rest f r - in - int_rest f - -let vars = ref 0 -let evals = ref 0 -let narrow_reuses = ref 0 - -(* print GC statistics; taken from Cil.Stats.print which also includes timing; there's also Gc.print_stat, but it's in words instead of MB and more info than we want (also slower than quick_stat since it goes through the heap) *) -let print_gc_quick_stat chn = - let gc = Gc.quick_stat () in - let printM (w: float) : string = - let coeff = float_of_int (Sys.word_size / 8) in - Printf.sprintf "%.2fMB" (w *. coeff /. 1000000.0) - in - Printf.fprintf chn - "Memory statistics: total=%s, max=%s, minor=%s, major=%s, promoted=%s\n minor collections=%d major collections=%d compactions=%d\n" - (printM (gc.Gc.minor_words +. gc.Gc.major_words - -. gc.Gc.promoted_words)) - (printM (float_of_int gc.Gc.top_heap_words)) - (printM gc.Gc.minor_words) - (printM gc.Gc.major_words) - (printM gc.Gc.promoted_words) - gc.Gc.minor_collections - gc.Gc.major_collections - gc.Gc.compactions; - gc - -let exe_dir = Fpath.(parent (v Sys.executable_name)) -let command_line = match Array.to_list Sys.argv with - | command :: arguments -> Filename.quote_command command arguments - | [] -> assert false - -(* https://ocaml.org/api/Sys.html#2_SignalnumbersforthestandardPOSIXsignals *) -(* https://ocaml.github.io/ocamlunix/signals.html *) -let signal_of_string = let open Sys in function - | "sigint" -> sigint (* Ctrl+C Interactive interrupt *) - | "sigtstp" -> sigtstp (* Ctrl+Z Interactive stop *) - | "sigquit" -> sigquit (* Ctrl+\ Interactive termination *) - | "sigalrm" -> sigalrm (* Timeout *) - | "sigkill" -> sigkill (* Termination (cannot be ignored) *) - | "sigsegv" -> sigsegv (* Invalid memory reference, https://github.com/goblint/analyzer/issues/206 *) - | "sigterm" -> sigterm (* Termination *) - | "sigusr1" -> sigusr1 (* Application-defined signal 1 *) - | "sigusr2" -> sigusr2 (* Application-defined signal 2 *) - | "sigstop" -> sigstop (* Stop *) - | "sigprof" -> sigprof (* Profiling interrupt *) - | "sigxcpu" -> sigxcpu (* Timeout in cpu time *) - | s -> failwith ("Unhandled signal " ^ s) - -let self_signal signal = Unix.kill (Unix.getpid ()) signal - -let rec for_all_in_range (a, b) f = - let module BI = IntOps.BigIntOps in - if BI.compare a b > 0 - then true - else f a && (for_all_in_range (BI.add a (BI.one), b) f) - -let dummy_obj = Obj.repr () - -let jobs () = - match get_int "jobs" with - | 0 -> Cpu.numcores () - | n -> n - -(** call [f], with [r] temporarily set to [x] *) -let with_ref r x = - let x0 = !r in - r := x; - Fun.protect ~finally:(fun () -> r := x0) From 0c53230cd3e0404f86fd2c3de0bc91863eeb7af5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:18:01 +0200 Subject: [PATCH 118/401] Move may_invalid_deref to analysisState --- src/framework/analysisState.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index 0f3a9f55bc..32b4e4d608 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,6 +7,9 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false +(** Whether an invalid pointer dereference happened *) +let svcomp_may_invalid_deref = ref false + (** A hack to see if we are currently doing global inits *) let global_initialization = ref false From c07d867dd75287f00afdbf1c1be1b8a02d463542 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 12 Aug 2023 08:19:23 +0200 Subject: [PATCH 119/401] Use AnalysisState in place of Goblintutil --- src/analyses/memOutOfBounds.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 415934b52d..85e8530de7 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -2,7 +2,7 @@ open GoblintCil open Analyses open MessageCategory -module GU = Goblintutil +module AS = AnalysisState module Spec = struct @@ -146,7 +146,7 @@ struct let (host, offset) = lval in match host, get_offset_size offset with | _, None -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval | Var v, Some oi -> begin match sizeOf v.vtype with @@ -154,14 +154,14 @@ struct begin match cilint_to_int_wrapper i with | Some i -> if i < oi then - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval | _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval end | Mem e, Some oi -> @@ -217,13 +217,13 @@ struct begin match ptr_size, offset_size with | Some pi, Some oi -> if pi < oi then - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 | None, _ -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 | _, None -> - GU.may_invalid_deref := true; + AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 end | _ -> () From b1b710ef597878488789a726c409c99107885b38 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 14 Aug 2023 10:57:10 +0300 Subject: [PATCH 120/401] =?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 121/401] 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 122/401] 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 123/401] 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 124/401] 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 125/401] 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 126/401] 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 127/401] 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 128/401] 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 129/401] 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 130/401] 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 131/401] 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 132/401] 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 133/401] 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 134/401] 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 135/401] 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 136/401] 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 137/401] 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 138/401] 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 139/401] 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 140/401] 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 141/401] 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 142/401] 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 143/401] 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 144/401] 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 145/401] 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 146/401] 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 147/401] 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 148/401] 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 149/401] 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 150/401] 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 434a4c1202382e0a0965a0792948423181bf3813 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 18:39:19 +0200 Subject: [PATCH 151/401] Enable intervals for regression tests --- tests/regression/73-mem-oob/01-oob-heap-simple.c | 2 +- tests/regression/73-mem-oob/02-oob-stack-simple.c | 2 +- tests/regression/73-mem-oob/03-oob-loop.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/73-mem-oob/01-oob-heap-simple.c index 91d9c7605a..10c7864184 100644 --- a/tests/regression/73-mem-oob/01-oob-heap-simple.c +++ b/tests/regression/73-mem-oob/01-oob-heap-simple.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include int main(int argc, char const *argv[]) { diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/73-mem-oob/02-oob-stack-simple.c index deedd52781..8d022feca4 100644 --- a/tests/regression/73-mem-oob/02-oob-stack-simple.c +++ b/tests/regression/73-mem-oob/02-oob-stack-simple.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include int main(int argc, char const *argv[]) { diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/73-mem-oob/03-oob-loop.c index 5e0e08c381..c800597757 100644 --- a/tests/regression/73-mem-oob/03-oob-loop.c +++ b/tests/regression/73-mem-oob/03-oob-loop.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval #include #include From 3db3d8c7c3c42588dc970d4b8f0219ee04a3d6a9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:06:51 +0200 Subject: [PATCH 152/401] Fix memOutOfBounds analysis and make it work --- src/analyses/memOutOfBounds.ml | 212 ++++++++++----------------------- 1 file changed, 66 insertions(+), 146 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 85e8530de7..c4e5f59bc2 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -3,28 +3,22 @@ open Analyses open MessageCategory module AS = AnalysisState +module VDQ = ValueDomainQueries module Spec = struct include Analyses.IdentitySpec module D = Lattice.Unit - module C = Lattice.Unit + module C = D - (* TODO: Do this later *) + (* TODO: Check out later for benchmarking *) let context _ _ = () let name () = "memOutOfBounds" (* HELPER FUNCTIONS *) - - (* A safe way to call [cilint_to_int] without having to worry about exceptions *) - let cilint_to_int_wrapper i = - try - Some (cilint_to_int i) - with _ -> None - let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -59,128 +53,57 @@ struct in host_contains_a_ptr host || offset_contains_a_ptr offset - let lval_is_ptr_var (lval:lval) = - let (host, _) = lval in - match host with - | Var v -> isPointerType v.vtype - (* Intuition: If the lval has a Mem host, then it's not a direct ptr which is what we're looking for here *) - | Mem e -> false - - let exp_points_to_heap ctx (exp:exp) = - match ctx.ask (Queries.MayPointTo exp) with + let points_to_heap_only ctx ptr = + 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.elements a - |> List.map fst - |> List.exists (fun x -> ctx.ask (Queries.IsHeapVar x)) - | _ -> false (* TODO: Is this sound? Maybe not quite. *) - - let get_size_for_heap_ptr ctx (exp:exp) = - (* TODO: - BlobSize always seems to respond with top when it's passed an Lval exp of a ptr var which in turn contains mem allocated via malloc. - Am I doing smth wrong here? - *) - match ctx.ask (Queries.BlobSize exp) with - | a when not (Queries.ID.is_top a) -> - begin match Queries.ID.to_int a with - | Some i -> Some (IntOps.BigIntOps.to_int i) - | None -> None - end - | _ -> None - - (* TODO: Here we assume that the given exp is a Lval exp *) - let get_size_for_stack_ptr ctx (exp:exp) = - match exp with - | Lval lval -> - if lval_is_ptr_var lval then - let (host, _) = lval in - begin match host with - | Var v -> - begin match sizeOf v.vtype with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None - end - | _ -> None + Queries.LS.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a + | _ -> false + + let get_size_of_ptr_target ctx ptr = + (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) + if points_to_heap_only ctx ptr then + ctx.ask (Queries.BlobSize ptr) + else + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (Queries.LS.is_top a) -> + let pts_list = Queries.LS.elements a in + let pts_elems_to_sizes (v, _) = + if isArrayType v.vtype then + ctx.ask (Queries.EvalLength ptr) + else + let var_type_size = match Cil.getInteger (sizeOf v.vtype) with + | Some z -> + let ikindOfTypeSize = intKindForValue z true in + VDQ.ID.of_int ikindOfTypeSize z + | None -> VDQ.ID.bot () + in + var_type_size + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> VDQ.ID.bot () + | [x] -> x + | x::xs -> List.fold_left (fun acc elem -> + if VDQ.ID.compare acc elem >= 0 then elem else acc + ) x xs end - else None - | _ -> None + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + VDQ.ID.top () - let get_ptr_size_for_exp ctx (exp:exp) = - match exp_points_to_heap ctx exp with - (* We're dealing with a ptr that points to the heap *) - | true -> get_size_for_heap_ptr ctx exp - (* Assumption here is that if it doesn't point to the heap, then it points to the stack *) - | false -> get_size_for_stack_ptr ctx exp + let eval_ptr_offset_in_binop ctx exp = + ctx.ask (Queries.EvalInt exp) - (** - * If we get [None], then the offset's size/value is unknown - * In the case [NoOffset], [Some 0] indicates that this offset type simply has value 0 - *) - let rec get_offset_size = function - | NoOffset -> Some 0 - | Index (e, o) -> - let exp_val = begin match constFold true e with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None - end - in - begin match exp_val, get_offset_size o with - | Some ei, Some oi -> Some (ei + oi) - | _, _ -> None - end - | Field (f, o) -> - begin match get_offset_size o, sizeOf f.ftype with - | Some oi, Const (CInt (i, _, _)) -> - begin match cilint_to_int_wrapper i with - | Some i -> Some (oi + i) - | None -> None - end - | _, _ -> None - end - - let rec check_lval_for_oob_access ctx (lval:lval) = - let undefined_behavior = Undefined MemoryOutOfBoundsAccess in - let cwe_number = 823 in - match lval_contains_a_ptr lval with - | false -> () (* Nothing to do here *) - | true -> - let (host, offset) = lval in - match host, get_offset_size offset with - | _, None -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) "Offset size for lval %a not known. A memory out-of-bounds access may occur" CilType.Lval.pretty lval - | Var v, Some oi -> - begin match sizeOf v.vtype with - | Const (CInt (i, _, _)) -> - begin match cilint_to_int_wrapper i with - | Some i -> - if i < oi then - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Offset bigger than var type's size for lval %a. A memory out-of-bounds access must occur" CilType.Lval.pretty lval - | _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval - end - | _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Unknown size of var %a for lval %a. A memory out-of-bounds access might occur" CilType.Varinfo.pretty v CilType.Lval.pretty lval - end - | Mem e, Some oi -> - check_exp_for_oob_access ctx e; - (* TODO: - * Not sure if we actually need these checks below. - * They never seem to apply - * I.e., for ptrs, it always seems to be the case that we have a binop which adds the offset to the ptr - instead of having the offset represented as an offset of the lval - * For this reason, the checks below are currently commented out - *) - (* begin match get_ptr_size_for_exp ctx e with - | Some ei -> - if ei < oi then - M.warn "Offset bigger than size of pointer memory, denoted by expression %a in lval %a" d_exp e CilType.Lval.pretty lval - | _ -> M.warn "Unknown size of pointer memory, denoted by exp %a for lval %a" d_exp e CilType.Lval.pretty lval - end *) + let rec check_lval_for_oob_access ctx lval = + if not @@ lval_contains_a_ptr lval then () + else + match lval with + | Var _, _ -> () + | Mem e, _ -> check_exp_for_oob_access ctx e - and check_exp_for_oob_access ctx (exp:exp) = + and check_exp_for_oob_access ctx exp = match exp with | Const _ | SizeOf _ @@ -193,8 +116,8 @@ struct | AlignOfE e | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e - | BinOp (bop, e1, e2, _) -> - check_binop_exp ctx bop e1 e2; + | BinOp (bop, e1, e2, t) -> + check_binop_exp ctx bop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -205,34 +128,31 @@ struct | StartOf lval | AddrOf lval -> check_lval_for_oob_access ctx lval - and check_binop_exp ctx (binop:binop) (e1:exp) (e2:exp) = - let undefined_behavior = Undefined MemoryOutOfBoundsAccess in + and check_binop_exp ctx binop e1 e2 t = + let binopexp = BinOp (binop, e1, e2, t) in + let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in match binop with | PlusPI | IndexPI | MinusPI -> - let ptr_size = get_ptr_size_for_exp ctx e1 in - let offset_size = eval_ptr_offset_in_binop e2 in - begin match ptr_size, offset_size with - | Some pi, Some oi -> - if pi < oi then - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer size in expression %a %a %a is smaller than offset for pointer arithmetic. Memory out-of-bounds access must occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 - | None, _ -> + let ptr_size = get_size_of_ptr_target ctx e1 in + let offset_size = eval_ptr_offset_in_binop ctx e2 in + begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with + | true, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp e1 CilType.Binop.pretty binop d_exp e2 - | _, None -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, true -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior undefined_behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a %a %a not known. Memory out-of-bounds access might occur" d_exp e1 CilType.Binop.pretty binop d_exp e2 + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp + | false, false -> + if ptr_size < offset_size then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer size (%a) in expression %a is smaller than offset (%a) for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + end end | _ -> () - and eval_ptr_offset_in_binop (exp:exp) = - match constFold true exp with - | Const (CInt (i, _, _)) -> cilint_to_int_wrapper i - | _ -> None (* TODO: Maybe try to also Eval the exp via Queries and not rely only on constFold *) - (* TRANSFER FUNCTIONS *) From 6e7664d47f2c85e3cc40cd4ff571038ee55da059 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:08:28 +0200 Subject: [PATCH 153/401] Remove unused transfer funs --- src/analyses/memOutOfBounds.ml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index c4e5f59bc2..655596d3ec 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,12 +174,6 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; 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 = - ctx.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 = Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; From 79d4319ae752893680ed1fa7d7ed2be067629b16 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:10:54 +0200 Subject: [PATCH 154/401] Move regression tests to a correctly numbered folder --- tests/regression/{73-mem-oob => 77-mem-oob}/01-oob-heap-simple.c | 0 tests/regression/{73-mem-oob => 77-mem-oob}/02-oob-stack-simple.c | 0 tests/regression/{73-mem-oob => 77-mem-oob}/03-oob-loop.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{73-mem-oob => 77-mem-oob}/01-oob-heap-simple.c (100%) rename tests/regression/{73-mem-oob => 77-mem-oob}/02-oob-stack-simple.c (100%) rename tests/regression/{73-mem-oob => 77-mem-oob}/03-oob-loop.c (100%) diff --git a/tests/regression/73-mem-oob/01-oob-heap-simple.c b/tests/regression/77-mem-oob/01-oob-heap-simple.c similarity index 100% rename from tests/regression/73-mem-oob/01-oob-heap-simple.c rename to tests/regression/77-mem-oob/01-oob-heap-simple.c diff --git a/tests/regression/73-mem-oob/02-oob-stack-simple.c b/tests/regression/77-mem-oob/02-oob-stack-simple.c similarity index 100% rename from tests/regression/73-mem-oob/02-oob-stack-simple.c rename to tests/regression/77-mem-oob/02-oob-stack-simple.c diff --git a/tests/regression/73-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c similarity index 100% rename from tests/regression/73-mem-oob/03-oob-loop.c rename to tests/regression/77-mem-oob/03-oob-loop.c From 84bd2cc7331e949607d5b48c3b0766d4c436e20e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 20 Aug 2023 19:13:37 +0200 Subject: [PATCH 155/401] Add comments to 77/03 test case --- tests/regression/77-mem-oob/03-oob-loop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c index c800597757..502a4cc0e2 100644 --- a/tests/regression/77-mem-oob/03-oob-loop.c +++ b/tests/regression/77-mem-oob/03-oob-loop.c @@ -9,8 +9,10 @@ int main(int argc, char const *argv[]) { ptr++; } - printf("%s", *ptr); //WARN - free(ptr); //WARN + //TODO: We cannot currently detect OOB memory accesses happening due to loops like the one above + // => Both lines below can't have WARNs for now + printf("%s", *ptr); //NOWARN + free(ptr); //NOWARN return 0; } From fe1660e0e74d6934aec68d590ade46d29b171d0c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 21 Aug 2023 10:24:32 +0300 Subject: [PATCH 156/401] 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 157/401] 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 158/401] 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 159/401] 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 160/401] 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 161/401] 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 6ed476e3216d808fb6e6a16a6b15ca19c93efbfd Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 21 Aug 2023 18:17:46 +0200 Subject: [PATCH 162/401] Warn for function args in enter and not in combine_assign --- src/analyses/memOutOfBounds.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 655596d3ec..cfab5c8b73 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,9 +174,12 @@ struct List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; ctx.local + let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; + [ctx.local, ctx.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 = Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; ctx.local let startstate v = () From 24c07c4af851ce6856f0551aca995e2c7edf5049 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Aug 2023 11:34:40 +0300 Subject: [PATCH 163/401] 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 164/401] 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 165/401] 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 166/401] 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 167/401] 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 168/401] 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 169/401] 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 170/401] 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 171/401] 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 172/401] 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 173/401] 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 174/401] 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 175/401] 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 176/401] 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 177/401] 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 178/401] 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 179/401] 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 180/401] 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 181/401] 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 182/401] 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 183/401] 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 184/401] 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 185/401] 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 186/401] 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 187/401] 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 188/401] 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 189/401] 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 190/401] 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 191/401] 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 192/401] 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 193/401] 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 194/401] 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 195/401] 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 196/401] 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 197/401] 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 198/401] 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 199/401] 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 4e7b21b7d4c411eaf3576438ac599df91a1a2af9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 24 Aug 2023 12:20:21 +0200 Subject: [PATCH 200/401] Try to check and warn only upon dereferences Also slightly change some warning messages --- src/analyses/memOutOfBounds.ml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index cfab5c8b73..9a03968ca3 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -101,7 +101,17 @@ struct else match lval with | Var _, _ -> () - | Mem e, _ -> check_exp_for_oob_access ctx e + | Mem e, _ -> + begin match e with + | Lval lval -> check_lval_for_oob_access ctx lval + | BinOp (PlusPI as binop, e1, e2, t) + | BinOp (MinusPI as binop, e1, e2, t) + | BinOp (IndexPI as binop, e1, e2, t) -> + check_binop_exp ctx binop e1 e2 t; + check_exp_for_oob_access ctx e1; + check_exp_for_oob_access ctx e2 + | _ -> check_exp_for_oob_access ctx e + end and check_exp_for_oob_access ctx exp = match exp with @@ -117,7 +127,6 @@ struct | UnOp (_, e, _) | CastE (_, e) -> check_exp_for_oob_access ctx e | BinOp (bop, e1, e2, t) -> - check_binop_exp ctx bop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | Question (e1, e2, e3, _) -> @@ -141,14 +150,14 @@ struct begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with | true, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer (%a) size in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp | _, true -> AS.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp | false, false -> if ptr_size < offset_size then begin AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Pointer size (%a) in expression %a is smaller than offset (%a) for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is smaller than offset %a for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size end end | _ -> () From 2e4b9f34cd69da48af447b178a438f45fa01efd7 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 25 Aug 2023 11:25:31 +0300 Subject: [PATCH 201/401] 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 202/401] 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 203/401] 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 204/401] 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 205/401] 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 206/401] 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 207/401] 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 208/401] 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 209/401] 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 291d60cd20d2a5ed29c3d76cd1e4cc05b07a562f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:40:20 +0200 Subject: [PATCH 210/401] Check for OOB mem access on the address level and only warn on deref --- src/analyses/memOutOfBounds.ml | 195 ++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 29 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 9a03968ca3..4b6c6db43f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -19,6 +19,36 @@ struct (* HELPER FUNCTIONS *) + let intdom_of_int x = + IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + + let to_index ?typ offs = + let idx_of_int x = + IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) + in + let rec offset_to_index_offset ?typ offs = match offs with + | `NoOffset -> idx_of_int 0 + | `Field (field, o) -> + let field_as_offset = Field (field, NoOffset) in + let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in + let bits_offset = idx_of_int bits_offset in + let remaining_offset = offset_to_index_offset ~typ:field.ftype o in + IntDomain.IntDomTuple.add bits_offset remaining_offset + | `Index (x, o) -> + let (item_typ, item_size_in_bits) = + match Option.map unrollType typ with + | Some TArray(item_typ, _, _) -> + let item_size_in_bits = bitsSizeOf item_typ in + (Some item_typ, idx_of_int item_size_in_bits) + | _ -> + (None, IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind ()) + in + let bits_offset = IntDomain.IntDomTuple.mul item_size_in_bits x in + let remaining_offset = offset_to_index_offset ?typ:item_typ o in + IntDomain.IntDomTuple.add bits_offset remaining_offset + in + offset_to_index_offset ?typ offs + let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -61,6 +91,14 @@ struct let get_size_of_ptr_target ctx ptr = (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) + (* TODO: + * If the ptr's address has been offset, then Queries.BlobSize will answer with bot. For example: + char *ptr = malloc(10 * sizeof(char)); + ptr++; + printf("%s", *ptr); // => Issues a WARN even though it shouldn't + * However, in this case we're too imprecise, since we're always comparing something with bot + * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? + *) if points_to_heap_only ctx ptr then ctx.ask (Queries.BlobSize ptr) else @@ -68,16 +106,19 @@ struct | a when not (Queries.LS.is_top a) -> let pts_list = Queries.LS.elements a in let pts_elems_to_sizes (v, _) = - if isArrayType v.vtype then - ctx.ask (Queries.EvalLength ptr) - else - let var_type_size = match Cil.getInteger (sizeOf v.vtype) with - | Some z -> - let ikindOfTypeSize = intKindForValue z true in - VDQ.ID.of_int ikindOfTypeSize z - | None -> VDQ.ID.bot () - in - var_type_size + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + | `Bot -> VDQ.ID.bot () + | `Top -> VDQ.ID.top () + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) + end in (* Map each points-to-set element to its size *) let pts_sizes = List.map pts_elems_to_sizes pts_list in @@ -93,26 +134,109 @@ struct M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; VDQ.ID.top () - let eval_ptr_offset_in_binop ctx exp = - ctx.ask (Queries.EvalInt exp) + let get_ptr_deref_type ptr_typ = + match ptr_typ with + | TPtr (t, _) -> Some t + | _ -> None - let rec check_lval_for_oob_access ctx lval = + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + + let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = + let eval_offset = ctx.ask (Queries.EvalInt exp) in + let eval_offset = Option.get @@ VDQ.ID.to_int eval_offset in + let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in + let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in + match eval_offset with + | `Lifted i -> `Lifted (IntDomain.IntDomTuple.mul i ptr_contents_typ_size_in_bytes) + | `Top -> `Top + | `Bot -> `Bot + + let rec offs_to_idx typ offs = + match offs with + | `NoOffset -> intdom_of_int 0 + | `Field (field, o) -> + let field_as_offset = Field (field, NoOffset) in + let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in + let bytes_offset = intdom_of_int (bits_offset / 8) in + let remaining_offset = offs_to_idx field.ftype o in + IntDomain.IntDomTuple.add bytes_offset remaining_offset + | `Index (x, o) -> + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in + let remaining_offset = offs_to_idx typ o in + IntDomain.IntDomTuple.add bytes_offset remaining_offset + + let rec get_addr_offs ctx ptr = + match ctx.ask (Queries.MayPointTo ptr) with + | a when not (VDQ.LS.is_top a) -> + let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in + begin match ptr_deref_type with + | Some t -> + begin match VDQ.LS.is_empty a with + | true -> + M.warn "Pointer %a has an empty points-to-set" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | false -> + (* + Offset should be the same for all elements in the points-to set. + Hence, we can just pick one element and obtain its offset + TODO: Does this make sense? + *) + let (_, o) = VDQ.LS.choose a in + let rec to_int_dom_offs = function + | `NoOffset -> `NoOffset + | `Field (f, o) -> `Field (f, to_int_dom_offs o) + | `Index (i, o) -> `Index (VDQ.ID.unlift (fun x -> x) @@ ctx.ask (Queries.EvalInt i), to_int_dom_offs o) + in + offs_to_idx t (to_int_dom_offs o) + end + | None -> + M.error "Expression %a doesn't have pointer type" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + end + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; + IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + + and check_lval_for_oob_access ctx lval = if not @@ lval_contains_a_ptr lval then () else match lval with | Var _, _ -> () | Mem e, _ -> begin match e with - | Lval lval -> check_lval_for_oob_access ctx lval - | BinOp (PlusPI as binop, e1, e2, t) - | BinOp (MinusPI as binop, e1, e2, t) - | BinOp (IndexPI as binop, e1, e2, t) -> + | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp + | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> check_binop_exp ctx binop e1 e2 t; check_exp_for_oob_access ctx e1; check_exp_for_oob_access ctx e2 | _ -> check_exp_for_oob_access ctx e end + and check_no_binop_deref ctx lval_exp = + let behavior = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let ptr_size = get_size_of_ptr_target ctx lval_exp in + let addr_offs = get_addr_offs ctx lval_exp in + let ptr_type = typeOf lval_exp in + let ptr_contents_type = get_ptr_deref_type ptr_type in + match ptr_contents_type with + | Some t -> + begin match VDQ.ID.is_top ptr_size with + | true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a not known. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | false -> + let offs = `Lifted addr_offs in + if ptr_size < offs then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size VDQ.ID.pretty offs + end + end + | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp + and check_exp_for_oob_access ctx exp = match exp with | Const _ @@ -146,19 +270,32 @@ struct | IndexPI | MinusPI -> let ptr_size = get_size_of_ptr_target ctx e1 in - let offset_size = eval_ptr_offset_in_binop ctx e2 in - begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size with - | true, _ -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp - | _, true -> - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp - | false, false -> - if ptr_size < offset_size then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is smaller than offset %a for pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size d_exp binopexp VDQ.ID.pretty offset_size + let addr_offs = get_addr_offs ctx e1 in + let ptr_type = typeOf e1 in + let ptr_contents_type = get_ptr_deref_type ptr_type in + begin match ptr_contents_type with + | Some t -> + let offset_size = eval_ptr_offset_in_binop ctx e2 t in + (* Make sure to add the address offset to the binop offset *) + let offset_size_with_addr_size = match offset_size with + | `Lifted os -> `Lifted (IntDomain.IntDomTuple.add os addr_offs) + | `Top -> `Top + | `Bot -> `Bot + in + begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size_with_addr_size with + | true, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp + | false, false -> + if ptr_size < offset_size_with_addr_size then begin + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp VDQ.ID.pretty ptr_size VDQ.ID.pretty offset_size_with_addr_size + end end + | _ -> M.error "Binary expression %a doesn't have a pointer" d_exp binopexp end | _ -> () From 2a3aaaec12b59236608b7b2e4c81e2fe27f3de36 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:40:53 +0200 Subject: [PATCH 211/401] Adjust the OOB mem access test case with a loop --- tests/regression/77-mem-oob/03-oob-loop.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/regression/77-mem-oob/03-oob-loop.c b/tests/regression/77-mem-oob/03-oob-loop.c index 502a4cc0e2..4f637d487e 100644 --- a/tests/regression/77-mem-oob/03-oob-loop.c +++ b/tests/regression/77-mem-oob/03-oob-loop.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --set exp.unrolling-factor 10 --enable ana.int.interval #include #include @@ -9,10 +9,8 @@ int main(int argc, char const *argv[]) { ptr++; } - //TODO: We cannot currently detect OOB memory accesses happening due to loops like the one above - // => Both lines below can't have WARNs for now - printf("%s", *ptr); //NOWARN - free(ptr); //NOWARN + printf("%s", *ptr); //WARN + free(ptr); //WARN return 0; } From 46bd81f482cc6407eeddd32297676fbc9b21bb79 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:46:05 +0200 Subject: [PATCH 212/401] Add test case with pointer arithmetic and subsequent derefs --- .../77-mem-oob/04-oob-deref-after-ptr-arith.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c diff --git a/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c b/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c new file mode 100644 index 0000000000..5046a00664 --- /dev/null +++ b/tests/regression/77-mem-oob/04-oob-deref-after-ptr-arith.c @@ -0,0 +1,18 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include + +int main(int argc, char const *argv[]) { + char *ptr = malloc(5 * sizeof(char)); + + ptr++;//NOWARN + printf("%s", *ptr);//NOWARN + ptr = ptr + 5;//NOWARN + printf("%s", *ptr);//WARN + *(ptr + 1) = 'b';//WARN + *(ptr + 10) = 'c';//WARN + + free(ptr); + + return 0; +} From 37fb4e8173a3e1d1b3b6b404219bc9b681e01721 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:51:05 +0200 Subject: [PATCH 213/401] Refactor to_int_dom_offs --- src/analyses/memOutOfBounds.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 4b6c6db43f..329539ebfb 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -188,7 +188,12 @@ struct let rec to_int_dom_offs = function | `NoOffset -> `NoOffset | `Field (f, o) -> `Field (f, to_int_dom_offs o) - | `Index (i, o) -> `Index (VDQ.ID.unlift (fun x -> x) @@ ctx.ask (Queries.EvalInt i), to_int_dom_offs o) + | `Index (i, o) -> + let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with + | `Lifted i -> i + | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + in + `Index (exp_as_int_dom, to_int_dom_offs o) in offs_to_idx t (to_int_dom_offs o) end From ff3e644d81efdd7f5adaadaec31f8cff99b0b77c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 25 Aug 2023 18:52:41 +0200 Subject: [PATCH 214/401] Cover bot and top cases in to_int_dom_offs --- src/analyses/memOutOfBounds.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 329539ebfb..2b4ca3d4c4 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -191,7 +191,8 @@ struct | `Index (i, o) -> let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with | `Lifted i -> i - | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | `Bot -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () + | `Top -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () in `Index (exp_as_int_dom, to_int_dom_offs o) in From a054a4f401772ea3318c640d4c0827dbce37e778 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 12:29:39 +0200 Subject: [PATCH 215/401] Warn for UAF only in case there's a deref happening --- src/analyses/useAfterFree.ml | 65 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c1f963b466..e88cfc1e51 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -82,38 +82,43 @@ struct end let rec warn_lval_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = - let state = ctx.local in - let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in - let cwe_number = if is_double_free then 415 else 416 in - let rec offset_might_contain_freed offset = - match offset with - | NoOffset -> () - | Field (f, o) -> offset_might_contain_freed o - | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name ctx e; offset_might_contain_freed o - in - let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *) - let lval_to_query = - match lval_host with - | 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) -> - let warn_for_heap_var var = - if D.mem var state then begin - AnalysisState.svcomp_may_use_after_free := true; - 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 - end + match is_double_free, lval with + (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *) + | false, (Var _, NoOffset) -> () + | _ -> + let state = ctx.local in + let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in + let cwe_number = if is_double_free then 415 else 416 in + let rec offset_might_contain_freed = function + | NoOffset -> () + | Field (f, o) -> offset_might_contain_freed o + | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name ctx e; offset_might_contain_freed o in - let pointed_to_heap_vars = - Queries.LS.elements a - |> List.map fst - |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *) + let lval_to_query = + match lval_host with + | 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 - 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 *) - List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars - | _ -> () + begin 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) -> + let warn_for_heap_var var = + if D.mem var state then begin + AnalysisState.svcomp_may_use_after_free := true; + 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 + end + in + let pointed_to_heap_vars = + Queries.LS.elements a + |> List.map fst + |> List.filter (fun var -> ctx.ask (Queries.IsHeapVar var)) + in + (* Warn for all heap vars that the lval possibly points to *) + List.iter warn_for_heap_var pointed_to_heap_vars; + (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) + List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars + | _ -> () + end and warn_exp_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = match exp with From 32714309d38154f3af572c8186879cc6eb7da176 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 12:30:01 +0200 Subject: [PATCH 216/401] Adapt regression tests to not WARN for UAF if there's no deref --- .../74-use_after_free/04-function-call-uaf.c | 3 ++- tests/regression/74-use_after_free/06-uaf-struct.c | 8 +++++--- tests/regression/74-use_after_free/09-juliet-uaf.c | 8 +++++--- .../74-use_after_free/11-wrapper-funs-uaf.c | 12 +++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/regression/74-use_after_free/04-function-call-uaf.c b/tests/regression/74-use_after_free/04-function-call-uaf.c index f83f9966b4..d110db9edc 100644 --- a/tests/regression/74-use_after_free/04-function-call-uaf.c +++ b/tests/regression/74-use_after_free/04-function-call-uaf.c @@ -17,7 +17,8 @@ int main() { free(ptr1); free(ptr2); - f(ptr1, ptr2, ptr3); //WARN + // No deref happening in the function call, hence nothing to warn about + f(ptr1, ptr2, ptr3); //NOWARN free(ptr3); //WARN diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-use_after_free/06-uaf-struct.c index 02c4f3e77a..13eed8e3db 100644 --- a/tests/regression/74-use_after_free/06-uaf-struct.c +++ b/tests/regression/74-use_after_free/06-uaf-struct.c @@ -17,13 +17,15 @@ int main(int argc, char **argv) { char line[128]; while (1) { - printf("[ auth = %p, service = %p ]\n", auth, service); //WARN + // No deref happening here => nothing to warn about + printf("[ auth = %p, service = %p ]\n", auth, service); //NOWARN if (fgets(line, sizeof(line), stdin) == NULL) break; if (strncmp(line, "auth ", 5) == 0) { - auth = malloc(sizeof(auth)); //WARN - memset(auth, 0, sizeof(auth)); //WARN + // No deref happening in either of the 2 lines below => no need to warn + auth = malloc(sizeof(auth)); //NOWARN + memset(auth, 0, sizeof(auth)); //NOWARN if (strlen(line + 5) < 31) { strcpy(auth->name, line + 5); //WARN } diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-use_after_free/09-juliet-uaf.c index 5a5bf3ee32..c37ef26aca 100644 --- a/tests/regression/74-use_after_free/09-juliet-uaf.c +++ b/tests/regression/74-use_after_free/09-juliet-uaf.c @@ -21,7 +21,8 @@ static char * helperBad(char * aString) reversedString[i] = '\0'; free(reversedString); - return reversedString; // WARN (Use After Free (CWE-416)) + // No need to warn in the line below, as there's no dereferencing happening + return reversedString; // NOWARN } else { @@ -67,8 +68,9 @@ void CWE416_Use_After_Free__return_freed_ptr_08_bad() if(staticReturnsTrue()) { { - char * reversedString = helperBad("BadSink"); // WARN (Use After Free (CWE-416)) - printf("%s\n", reversedString); // WARN (Use After Free (CWE-416)) + // No need to warn in the two lines below, since there's no dereferencing of the freed memory + char * reversedString = helperBad("BadSink"); // NOWARN + printf("%s\n", reversedString); // NOWARN } } } diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c index 3ed540b53d..6ae79e0062 100644 --- a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c +++ b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c @@ -27,12 +27,14 @@ int main(int argc, char const *argv[]) { my_free2(p); *(p + 42) = 'c'; //WARN - printf("%s", p); //WARN - - char *p2 = p; //WARN + // No dereferencing in the line below => no need to warn + printf("%s", p); //NOWARN - my_free2(p); //WARN - my_free2(p2); //WARN + // No dereferencing happening in the lines below => no need to warn for an invalid-deref + // Also no need to warn for an invalid-free, as the call to free is within these functions and they're not the "free" function itself + char *p2 = p; //NOWARN + my_free2(p); //NOWARN + my_free2(p2); //NOWARN return 0; } From d49db71fee1a9ffc125cf373eaa0bf2dab0baa58 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 26 Aug 2023 14:38:23 +0200 Subject: [PATCH 217/401] Clean up and use proper global vars for SV-COMP --- src/analyses/useAfterFree.ml | 21 +++++++++++++-------- src/framework/analysisState.ml | 4 ++-- src/witness/witness.ml | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index e88cfc1e51..6e2ca3b04f 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -1,6 +1,5 @@ (** An analysis for the detection of use-after-free vulnerabilities ([useAfterFree]). *) -open GobConfig open GoblintCil open Analyses open MessageCategory @@ -25,13 +24,19 @@ struct (* HELPER FUNCTIONS *) + let set_global_svcomp_var is_double_free = + if is_double_free then + AnalysisState.svcomp_may_invalid_free := true + else + AnalysisState.svcomp_may_invalid_deref := true + let get_current_threadid ctx = ctx.ask Queries.CurrentThreadId let get_joined_threads ctx = ctx.ask Queries.MustJoinedThreads - let warn_for_multi_threaded_access ctx (heap_var:varinfo) behavior cwe_number = + let warn_for_multi_threaded_access ctx ?(is_double_free = false) (heap_var:varinfo) behavior cwe_number = let freeing_threads = ctx.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () @@ -59,23 +64,23 @@ struct | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var end else if D.mem heap_var ctx.local then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" @@ -104,7 +109,7 @@ struct | a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar, `NoOffset) a) -> let warn_for_heap_var var = if D.mem var state then begin - AnalysisState.svcomp_may_use_after_free := true; + set_global_svcomp_var is_double_free; 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 end in @@ -116,7 +121,7 @@ struct (* Warn for all heap vars that the lval possibly points to *) List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) - List.iter (fun heap_var -> warn_for_multi_threaded_access ctx heap_var undefined_behavior cwe_number) pointed_to_heap_vars + List.iter (fun heap_var -> warn_for_multi_threaded_access ctx ~is_double_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars | _ -> () end diff --git a/src/framework/analysisState.ml b/src/framework/analysisState.ml index e987c414f2..ca619d4dfb 100644 --- a/src/framework/analysisState.ml +++ b/src/framework/analysisState.ml @@ -7,8 +7,8 @@ let should_warn = ref false (** Whether signed overflow or underflow happened *) let svcomp_may_overflow = ref false -(** Whether a Use-After-Free (UAF) happened *) -let svcomp_may_use_after_free = ref false +(** Whether an invalid free happened *) +let svcomp_may_invalid_free = ref false (** Whether an invalid pointer dereference happened *) let svcomp_may_invalid_deref = ref false diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 797541c606..8a77a10bc7 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -479,7 +479,7 @@ struct let next _ = [] end in - if not !AnalysisState.svcomp_may_use_after_free then + if not !AnalysisState.svcomp_may_invalid_free then let module TaskResult = struct module Arg = Arg From 034b0ab90198a60311485dc290b434386c41fd21 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 27 Aug 2023 13:49:07 +0200 Subject: [PATCH 218/401] Use proper bug names when warning for multi-threaded programs --- src/analyses/useAfterFree.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 6e2ca3b04f..b680bc5369 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -60,28 +60,29 @@ struct | `Top -> true | `Bot -> false in + let bug_name = if is_double_free then "Double Free" else "Use After Free" in match get_current_threadid ctx with | `Lifted current -> let possibly_started = G.exists (possibly_started current) freeing_threads in if possibly_started then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. Use-After-Free might occur" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "There's a thread that's been started in parallel with the memory-freeing threads for heap variable %a. %s might occur" CilType.Varinfo.pretty heap_var bug_name end else begin let current_is_unique = ThreadId.Thread.is_unique current in let any_equal_current threads = G.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end else if D.mem heap_var ctx.local then begin set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end end | `Top -> set_global_svcomp_var is_double_free; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. A Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "CurrentThreadId is top. %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var | `Bot -> M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" end From 68e1b7111bbf57a734a3ff9e1abce0e405e5f935 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 27 Aug 2023 14:00:17 +0200 Subject: [PATCH 219/401] Add a valid-memsafety.prp file --- tests/sv-comp/valid-memsafety.prp | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/sv-comp/valid-memsafety.prp diff --git a/tests/sv-comp/valid-memsafety.prp b/tests/sv-comp/valid-memsafety.prp new file mode 100644 index 0000000000..06a87f5a37 --- /dev/null +++ b/tests/sv-comp/valid-memsafety.prp @@ -0,0 +1,4 @@ +CHECK( init(main()), LTL(G valid-free) ) +CHECK( init(main()), LTL(G valid-deref) ) +CHECK( init(main()), LTL(G valid-memtrack) ) + From 3cbc8e7db66d5373249eaac2b64b2a9f10a8aef3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Aug 2023 13:43:58 +0300 Subject: [PATCH 220/401] 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 221/401] 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 222/401] 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 cd95e8d45dbcaf68adacd3508cc5938eec4d500c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 22:17:59 +0200 Subject: [PATCH 223/401] Try to not warn for threads joined before free() --- src/analyses/useAfterFree.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index b680bc5369..94d996249d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -45,11 +45,11 @@ struct match tid with | `Lifted tid -> let created_threads = ctx.ask Queries.CreatedThreads in - (* Discard joined threads, as they're supposed to be joined before the point of freeing the memory *) - let threads = ConcDomain.MustThreadSet.diff created_threads joined_threads in - let not_started = MHP.definitely_not_started (current, threads) tid in + let not_started = MHP.definitely_not_started (current, created_threads) tid in let possibly_started = not not_started in - possibly_started + (* If [current] is possibly running together with [tid], but is also joined before the free() in [tid], then no need to WARN *) + let current_joined_before_free = ConcDomain.MustThreadSet.mem current joined_threads in + possibly_started && not current_joined_before_free | `Top -> true | `Bot -> false in From ba4d301aede5ad76f7175f00a4ca82c09adeddc4 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 22:18:35 +0200 Subject: [PATCH 224/401] Enable threadJoins for all multi-threaded test cases --- tests/regression/74-use_after_free/12-multi-threaded-uaf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c b/tests/regression/74-use_after_free/12-multi-threaded-uaf.c index 0c647eff76..f6d11ae098 100644 --- a/tests/regression/74-use_after_free/12-multi-threaded-uaf.c +++ b/tests/regression/74-use_after_free/12-multi-threaded-uaf.c @@ -1,4 +1,4 @@ -//PARAM: --set ana.activated[+] useAfterFree +//PARAM: --set ana.activated[+] useAfterFree --set ana.activated[+] threadJoins #include #include #include From 66f0c9da414be2dc0d989a9dd22db35978bf00ef Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:25:53 +0200 Subject: [PATCH 225/401] Add checks for implicit dereferencing of pointers --- src/analyses/useAfterFree.ml | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 94d996249d..e8a0d99925 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -87,10 +87,10 @@ struct M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" end - let rec warn_lval_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = - match is_double_free, lval with + let rec warn_lval_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = + match is_implicitly_derefed, is_double_free, lval with (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *) - | false, (Var _, NoOffset) -> () + | false, false, (Var _, NoOffset) -> () | _ -> let state = ctx.local in let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in @@ -126,7 +126,7 @@ struct | _ -> () end - and warn_exp_might_contain_freed ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = + and warn_exp_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = match exp with (* Base recursion cases *) | Const _ @@ -140,18 +140,18 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e + | CastE (_, e) -> warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e | BinOp (_, e1, e2, _) -> - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e2 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2 | Question (e1, e2, e3, _) -> - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e2; - warn_exp_might_contain_freed ~is_double_free transfer_fn_name ctx e3 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e3 (* Lval cases (need [warn_lval_might_contain_freed] for them) *) | Lval lval | StartOf lval - | AddrOf lval -> warn_lval_might_contain_freed ~is_double_free transfer_fn_name ctx lval + | AddrOf lval -> warn_lval_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx lval let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = let side_effect_globals_to_heap_var heap_var = @@ -210,9 +210,16 @@ struct let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = let state = ctx.local in - Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; - List.iter (fun arg -> warn_exp_might_contain_freed ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; let desc = LibraryFunctions.find f in + let is_arg_implicitly_derefed arg = + let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in + let read_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = true } arglist in + let write_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args + in + Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; + List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with From 54646364ce12217a8e64c300a20548698c646aac Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:26:14 +0200 Subject: [PATCH 226/401] Accommodate implicit dereferencing in regression tests --- tests/regression/74-use_after_free/06-uaf-struct.c | 9 +++++---- tests/regression/74-use_after_free/09-juliet-uaf.c | 5 +++-- tests/regression/74-use_after_free/11-wrapper-funs-uaf.c | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/regression/74-use_after_free/06-uaf-struct.c b/tests/regression/74-use_after_free/06-uaf-struct.c index 13eed8e3db..fa3ffc7b56 100644 --- a/tests/regression/74-use_after_free/06-uaf-struct.c +++ b/tests/regression/74-use_after_free/06-uaf-struct.c @@ -17,15 +17,16 @@ int main(int argc, char **argv) { char line[128]; while (1) { - // No deref happening here => nothing to warn about - printf("[ auth = %p, service = %p ]\n", auth, service); //NOWARN + // printf() is considered an implicit deref => need to warn here + printf("[ auth = %p, service = %p ]\n", auth, service); //WARN if (fgets(line, sizeof(line), stdin) == NULL) break; if (strncmp(line, "auth ", 5) == 0) { - // No deref happening in either of the 2 lines below => no need to warn + // No deref happening in the line below => no need to warn auth = malloc(sizeof(auth)); //NOWARN - memset(auth, 0, sizeof(auth)); //NOWARN + // memset() is considered an implicit deref => need to warn + memset(auth, 0, sizeof(auth)); //WARN if (strlen(line + 5) < 31) { strcpy(auth->name, line + 5); //WARN } diff --git a/tests/regression/74-use_after_free/09-juliet-uaf.c b/tests/regression/74-use_after_free/09-juliet-uaf.c index c37ef26aca..e1a88508a6 100644 --- a/tests/regression/74-use_after_free/09-juliet-uaf.c +++ b/tests/regression/74-use_after_free/09-juliet-uaf.c @@ -68,9 +68,10 @@ void CWE416_Use_After_Free__return_freed_ptr_08_bad() if(staticReturnsTrue()) { { - // No need to warn in the two lines below, since there's no dereferencing of the freed memory + // No need to warn in the line below, since there's no dereferencing of the freed memory char * reversedString = helperBad("BadSink"); // NOWARN - printf("%s\n", reversedString); // NOWARN + // printf() is considered an implicit deref => need to warn here + printf("%s\n", reversedString); // WARN } } } diff --git a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c index 6ae79e0062..cc6539eff2 100644 --- a/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c +++ b/tests/regression/74-use_after_free/11-wrapper-funs-uaf.c @@ -27,8 +27,8 @@ int main(int argc, char const *argv[]) { my_free2(p); *(p + 42) = 'c'; //WARN - // No dereferencing in the line below => no need to warn - printf("%s", p); //NOWARN + // printf() is considered an implicit deref => need to warn + printf("%s", p); //WARN // No dereferencing happening in the lines below => no need to warn for an invalid-deref // Also no need to warn for an invalid-free, as the call to free is within these functions and they're not the "free" function itself From 049dc5853368f7b604aeb1a17aa449788d66ee72 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Mon, 28 Aug 2023 23:32:26 +0200 Subject: [PATCH 227/401] Remove redundant intersection when side-effecting --- src/analyses/useAfterFree.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index e8a0d99925..9af1b8ca7a 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -156,11 +156,7 @@ struct let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = let side_effect_globals_to_heap_var heap_var = let current_globals = ctx.global heap_var in - let joined_threads_to_add = match G.find_opt threadid current_globals with - | Some threads -> ConcDomain.ThreadSet.inter joined_threads threads - | None -> joined_threads - in - let globals_to_side_effect = G.add threadid joined_threads_to_add current_globals in + let globals_to_side_effect = G.add threadid joined_threads current_globals in ctx.sideg heap_var globals_to_side_effect in D.iter side_effect_globals_to_heap_var freed_heap_vars From dc0df887e22adc34ff93f170f337ddcc7b9aff19 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 12:27:57 +0300 Subject: [PATCH 228/401] Add MustLocksetA and MustProtectedVarsA to queries --- src/domains/queries.ml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 0388ce2995..8680103d7a 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -81,6 +81,7 @@ type _ t = | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t | MustLockset: LS.t t + | MustLocksetA: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t | MustBeUniqueThread: MustBool.t t @@ -115,6 +116,7 @@ type _ t = | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t | MustProtectedVars: mustprotectedvars -> LS.t t + | MustProtectedVarsA: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -145,6 +147,7 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module LS) + | MustLocksetA -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -180,6 +183,7 @@ struct | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) | MustProtectedVars _ -> (module LS) + | MustProtectedVarsA _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -209,6 +213,7 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> LS.top () + | MustLocksetA -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -244,6 +249,7 @@ struct | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () | MustProtectedVars _ -> LS.top () + | MustProtectedVarsA _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -274,6 +280,7 @@ struct | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 | Any MustLockset -> 10 + | Any MustLocksetA -> 1010 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -299,6 +306,7 @@ struct | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 + | Any (MustProtectedVarsA _) -> 3939 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -356,6 +364,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 @@ -395,6 +404,7 @@ struct | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (MustProtectedVars m) -> hash_mustprotectedvars m + | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv @@ -417,6 +427,7 @@ struct | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" | Any MustLockset -> Pretty.dprintf "MustLockset" + | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start | Any MustBeUniqueThread -> Pretty.dprintf "MustBeUniqueThread" @@ -445,6 +456,7 @@ struct | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" + | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" From 326b8e66d7ed97d21ce894bf0a32612487038815 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 12:28:42 +0300 Subject: [PATCH 229/401] Use MustLocksetA and MustProtectedVarsA in mutexAnalysis --- src/analyses/base.ml | 1 + src/analyses/commonPriv.ml | 19 +++++++++---------- src/analyses/mutexAnalysis.ml | 10 ++-------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..d7c7d4429b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2542,6 +2542,7 @@ struct | MayBePublicWithout _ | MustBeProtectedBy _ | MustLockset + | MustLocksetA | MustBeAtomic | MustBeSingleThreaded _ | MustBeUniqueThread diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1b92cb320d..4aca250ba5 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,12 +61,13 @@ struct let protected_vars (ask: Q.ask): varinfo list = let module VS = Set.Make (CilType.Varinfo) in - Q.LS.fold (fun (v, _) acc -> - let m = ValueDomain.Addr.of_var v in (* TODO: don't ignore offsets *) - Q.LS.fold (fun l acc -> - VS.add (fst l) acc (* always `NoOffset from mutex analysis *) - ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc - ) (ask.f Q.MustLockset) VS.empty + Q.AD.fold (fun m acc -> + Q.AD.fold (fun l acc -> + match l with + | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) + | _ -> acc + ) (ask.f (Q.MustProtectedVarsA {mutex = m; write = true})) acc + ) (ask.f Q.MustLocksetA) VS.empty |> VS.elements end @@ -126,10 +127,8 @@ struct if !AnalysisState.global_initialization then Lockset.empty () else - let ls = ask.f Queries.MustLockset in - Q.LS.fold (fun (var, offs) acc -> - Lockset.add (Lock.of_mval (var, Lock.Offs.of_exp offs)) acc - ) ls (Lockset.empty ()) + let ad = ask.f Queries.MustLocksetA in + Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 7d8298a0a4..bc03216ba6 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -231,15 +231,9 @@ struct true else *) Mutexes.leq mutex_lockset protecting - | Queries.MustLockset -> + | Queries.MustLocksetA -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - let ls = Mutexes.fold (fun addr ls -> - match Addr.to_mval addr with - | Some (var, offs) -> Queries.LS.add (var, Addr.Offs.to_exp offs) ls - | None -> ls - ) held_locks (Queries.LS.empty ()) - in - ls + Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks From 2bf93536925839af61816451656586208012f763 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:44:53 +0300 Subject: [PATCH 230/401] Use MustProtectedVarsA in mutexAnalysis --- src/analyses/mutexAnalysis.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index bc03216ba6..9bde708566 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -242,6 +242,11 @@ struct VarSet.fold (fun v acc -> Queries.LS.add (v, `NoOffset) acc ) protected (Queries.LS.empty ()) + | Queries.MustProtectedVarsA {mutex = m; write} -> + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in + VarSet.fold (fun v acc -> + Queries.AD.join (Queries.AD.of_var v) acc + ) protected (Queries.AD.empty ()) | Queries.IterSysVars (Global g, f) -> f (Obj.repr (V.protecting g)) (* TODO: something about V.protected? *) | WarnGlobal g -> From 65567a603f6b3fb8c7c869fa1bbc6f8a23d69691 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:50:29 +0300 Subject: [PATCH 231/401] Remove MustLockset and MustProtectedVars from queries --- src/analyses/base.ml | 1 - src/analyses/mutexAnalysis.ml | 5 ----- src/domains/queries.ml | 16 ++-------------- 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d7c7d4429b..a7330459f0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2541,7 +2541,6 @@ struct | MayBePublic _ | MayBePublicWithout _ | MustBeProtectedBy _ - | MustLockset | MustLocksetA | MustBeAtomic | MustBeSingleThreaded _ diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9bde708566..55172c48eb 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -237,11 +237,6 @@ struct | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks - | Queries.MustProtectedVars {mutex = m; write} -> - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in - VarSet.fold (fun v acc -> - Queries.LS.add (v, `NoOffset) acc - ) protected (Queries.LS.empty ()) | Queries.MustProtectedVarsA {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 8680103d7a..a160e6dd7b 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -80,7 +80,6 @@ type _ t = | MayBePublic: maybepublic -> MayBool.t t (* old behavior with write=false *) | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t - | MustLockset: LS.t t | MustLocksetA: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t @@ -115,7 +114,6 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVars: mustprotectedvars -> LS.t t | MustProtectedVarsA: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -146,7 +144,6 @@ struct | MayPointTo _ -> (module AD) | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) - | MustLockset -> (module LS) | MustLocksetA -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) @@ -182,7 +179,6 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVars _ -> (module LS) | MustProtectedVarsA _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) @@ -212,7 +208,6 @@ struct | MayPointTo _ -> AD.top () | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () - | MustLockset -> LS.top () | MustLocksetA -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () @@ -248,7 +243,6 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVars _ -> LS.top () | MustProtectedVarsA _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () @@ -279,8 +273,7 @@ struct | Any (MayBePublic _) -> 7 | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 - | Any MustLockset -> 10 - | Any MustLocksetA -> 1010 + | Any MustLocksetA -> 10 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -305,8 +298,7 @@ struct | Any (Invariant _) -> 36 | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 - | Any (MustProtectedVars _) -> 39 - | Any (MustProtectedVarsA _) -> 3939 + | Any (MustProtectedVarsA _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -363,7 +355,6 @@ struct | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 - | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 @@ -403,7 +394,6 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi - | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start @@ -426,7 +416,6 @@ struct | Any (MayBePublic x) -> Pretty.dprintf "MayBePublic _" | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" - | Any MustLockset -> Pretty.dprintf "MustLockset" | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start @@ -455,7 +444,6 @@ struct | Any CreatedThreads -> Pretty.dprintf "CreatedThreads" | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" - | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" From 4bf3680a0889e97f7e1cd845241139ccc95d144a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 14:55:02 +0300 Subject: [PATCH 232/401] Rename MustLocksetA -> MustLockset, MustProtectedVarsA -> MustProtectedVars --- src/analyses/base.ml | 2 +- src/analyses/commonPriv.ml | 6 +++--- src/analyses/mutexAnalysis.ml | 4 ++-- src/domains/queries.ml | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a7330459f0..4183261ddb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2541,7 +2541,7 @@ struct | MayBePublic _ | MayBePublicWithout _ | MustBeProtectedBy _ - | MustLocksetA + | MustLockset | MustBeAtomic | MustBeSingleThreaded _ | MustBeUniqueThread diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 4aca250ba5..199fae98b0 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -66,8 +66,8 @@ struct match l with | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) | _ -> acc - ) (ask.f (Q.MustProtectedVarsA {mutex = m; write = true})) acc - ) (ask.f Q.MustLocksetA) VS.empty + ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc + ) (ask.f Q.MustLockset) VS.empty |> VS.elements end @@ -127,7 +127,7 @@ struct if !AnalysisState.global_initialization then Lockset.empty () else - let ad = ask.f Queries.MustLocksetA in + let ad = ask.f Queries.MustLockset in Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: reversed SetDomain.Hoare *) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 55172c48eb..9cc019db76 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -231,13 +231,13 @@ struct true else *) Mutexes.leq mutex_lockset protecting - | Queries.MustLocksetA -> + | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks - | Queries.MustProtectedVarsA {mutex = m; write} -> + | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> Queries.AD.join (Queries.AD.of_var v) acc diff --git a/src/domains/queries.ml b/src/domains/queries.ml index a160e6dd7b..b10d1b673d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -80,7 +80,7 @@ type _ t = | MayBePublic: maybepublic -> MayBool.t t (* old behavior with write=false *) | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t - | MustLocksetA: AD.t t + | MustLockset: AD.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t | MustBeUniqueThread: MustBool.t t @@ -114,7 +114,7 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVarsA: mustprotectedvars -> AD.t t + | MustProtectedVars: mustprotectedvars -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -144,7 +144,7 @@ struct | MayPointTo _ -> (module AD) | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) - | MustLocksetA -> (module AD) + | MustLockset -> (module AD) | EvalFunvar _ -> (module LS) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -179,7 +179,7 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVarsA _ -> (module AD) + | MustProtectedVars _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -208,7 +208,7 @@ struct | MayPointTo _ -> AD.top () | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () - | MustLocksetA -> AD.top () + | MustLockset -> AD.top () | EvalFunvar _ -> LS.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -243,7 +243,7 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVarsA _ -> AD.top () + | MustProtectedVars _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -273,7 +273,7 @@ struct | Any (MayBePublic _) -> 7 | Any (MayBePublicWithout _) -> 8 | Any (MustBeProtectedBy _) -> 9 - | Any MustLocksetA -> 10 + | Any MustLockset -> 10 | Any MustBeAtomic -> 11 | Any (MustBeSingleThreaded _)-> 12 | Any MustBeUniqueThread -> 13 @@ -298,7 +298,7 @@ struct | Any (Invariant _) -> 36 | Any (IterSysVars _) -> 37 | Any (InvariantGlobal _) -> 38 - | Any (MustProtectedVarsA _) -> 39 + | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 @@ -355,7 +355,7 @@ struct | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 - | Any (MustProtectedVarsA m1), Any (MustProtectedVarsA m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 @@ -394,7 +394,7 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi - | Any (MustProtectedVarsA m) -> hash_mustprotectedvars m + | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv @@ -416,7 +416,7 @@ struct | Any (MayBePublic x) -> Pretty.dprintf "MayBePublic _" | Any (MayBePublicWithout x) -> Pretty.dprintf "MayBePublicWithout _" | Any (MustBeProtectedBy x) -> Pretty.dprintf "MustBeProtectedBy _" - | Any MustLocksetA -> Pretty.dprintf "MustLocksetA" + | Any MustLockset -> Pretty.dprintf "MustLockset" | Any MustBeAtomic -> Pretty.dprintf "MustBeAtomic" | Any (MustBeSingleThreaded {since_start}) -> Pretty.dprintf "MustBeSingleThreaded since_start=%b" since_start | Any MustBeUniqueThread -> Pretty.dprintf "MustBeUniqueThread" @@ -444,7 +444,7 @@ struct | Any CreatedThreads -> Pretty.dprintf "CreatedThreads" | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" - | Any (MustProtectedVarsA m) -> Pretty.dprintf "MustProtectedVarsA _" + | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" From 586a5959a6ba4090f25088c000c94a2a710611c6 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:00:20 +0300 Subject: [PATCH 233/401] Add EvalFunvarA to queries --- src/domains/queries.ml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index b10d1b673d..1f17b198b6 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -88,6 +88,7 @@ type _ t = | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t | EvalFunvar: exp -> LS.t t + | EvalFunvarA: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) @@ -146,6 +147,7 @@ struct | Regions _ -> (module LS) | MustLockset -> (module AD) | EvalFunvar _ -> (module LS) + | EvalFunvarA _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) | MayBePublic _ -> (module MayBool) @@ -210,6 +212,7 @@ struct | Regions _ -> LS.top () | MustLockset -> AD.top () | EvalFunvar _ -> LS.top () + | EvalFunvarA _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () | MayBePublic _ -> MayBool.top () @@ -280,6 +283,7 @@ struct | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 | Any (EvalFunvar _) -> 16 + | Any (EvalFunvarA _) -> 1616 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -330,6 +334,7 @@ struct | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 + | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 @@ -375,6 +380,7 @@ struct | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x | Any (EvalFunvar e) -> CilType.Exp.hash e + | Any (EvalFunvarA e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e | Any (EvalLength e) -> CilType.Exp.hash e @@ -424,6 +430,7 @@ struct | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e + | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e From f15b41fb6fe467c4b4ef113306bea23008d830a5 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:08:10 +0300 Subject: [PATCH 234/401] Use EvalFunvarA in base --- src/analyses/base.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4183261ddb..2272a953ca 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1207,6 +1207,11 @@ struct let fs = eval_funvar ctx e in List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs end + | Q.EvalFunvarA e -> + begin + let fs = eval_funvar ctx e in + List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs + end | Q.EvalJumpBuf e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address jmp_buf -> From bdd90b4c4f7cffa3130b126d1a7b746ee1d9d3d4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:35:27 +0300 Subject: [PATCH 235/401] Use EvalFunvarA in constraints --- src/framework/constraints.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 740d1f85a9..9bbfef9c89 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -299,12 +299,12 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.EvalFunvar e -> + | Queries.EvalFunvarA e -> let (d,l) = ctx.local in if leq0 l then - Queries.LS.empty () + Queries.AD.empty () else - query' ctx (Queries.EvalFunvar e) + query' ctx (Queries.EvalFunvarA e) | q -> query' ctx q end @@ -754,8 +754,8 @@ struct [v] | _ -> (* Depends on base for query. *) - let ls = ctx.ask (Queries.EvalFunvar e) in - Queries.LS.fold (fun ((x,_)) xs -> x::xs) ls [] + let ad = ctx.ask (Queries.EvalFunvarA e) in + List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) in let one_function f = match f.vtype with From e9e53a71080bcbca164906cd8bef1a1d420c5345 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:39:07 +0300 Subject: [PATCH 236/401] Remove EvalFunvar queries --- src/analyses/base.ml | 5 ----- src/domains/queries.ml | 9 +-------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2272a953ca..c272bf1a63 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1202,11 +1202,6 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with - | Q.EvalFunvar e -> - begin - let fs = eval_funvar ctx e in - List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs - end | Q.EvalFunvarA e -> begin let fs = eval_funvar ctx e in diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1f17b198b6..648a0c561e 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -87,7 +87,6 @@ type _ t = | CurrentThreadId: ThreadIdDomain.ThreadLifted.t t | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t - | EvalFunvar: exp -> LS.t t | EvalFunvarA: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t @@ -146,7 +145,6 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module AD) - | EvalFunvar _ -> (module LS) | EvalFunvarA _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -211,7 +209,6 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> AD.top () - | EvalFunvar _ -> LS.top () | EvalFunvarA _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () @@ -282,8 +279,7 @@ struct | Any MustBeUniqueThread -> 13 | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 - | Any (EvalFunvar _) -> 16 - | Any (EvalFunvarA _) -> 1616 + | Any (EvalFunvarA _) -> 16 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -333,7 +329,6 @@ struct | Any (MayBePublic x1), Any (MayBePublic x2) -> compare_maybepublic x1 x2 | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 - | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 @@ -379,7 +374,6 @@ struct | Any (MayBePublic x) -> hash_maybepublic x | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x - | Any (EvalFunvar e) -> CilType.Exp.hash e | Any (EvalFunvarA e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e @@ -429,7 +423,6 @@ struct | Any CurrentThreadId -> Pretty.dprintf "CurrentThreadId" | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" - | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e From 9fef5946d61b009ea31d2090870ed73a5b8755de Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:39:46 +0300 Subject: [PATCH 237/401] Rename EvalFunvarA -> EvalFunvar --- src/analyses/base.ml | 2 +- src/domains/queries.ml | 14 +++++++------- src/framework/constraints.ml | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c272bf1a63..5dfbe3c64a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1202,7 +1202,7 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with - | Q.EvalFunvarA e -> + | Q.EvalFunvar e -> begin let fs = eval_funvar ctx e in List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 648a0c561e..8626fb9d11 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -87,7 +87,7 @@ type _ t = | CurrentThreadId: ThreadIdDomain.ThreadLifted.t t | ThreadCreateIndexedNode: ThreadNodeLattice.t t | MayBeThreadReturn: MayBool.t t - | EvalFunvarA: exp -> AD.t t + | EvalFunvar: exp -> AD.t t | EvalInt: exp -> ID.t t | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) @@ -145,7 +145,7 @@ struct | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) | MustLockset -> (module AD) - | EvalFunvarA _ -> (module AD) + | EvalFunvar _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) | MayBePublic _ -> (module MayBool) @@ -209,7 +209,7 @@ struct | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () | MustLockset -> AD.top () - | EvalFunvarA _ -> AD.top () + | EvalFunvar _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () | MayBePublic _ -> MayBool.top () @@ -279,7 +279,7 @@ struct | Any MustBeUniqueThread -> 13 | Any CurrentThreadId -> 14 | Any MayBeThreadReturn -> 15 - | Any (EvalFunvarA _) -> 16 + | Any (EvalFunvar _) -> 16 | Any (EvalInt _) -> 17 | Any (EvalStr _) -> 18 | Any (EvalLength _) -> 19 @@ -329,7 +329,7 @@ struct | Any (MayBePublic x1), Any (MayBePublic x2) -> compare_maybepublic x1 x2 | Any (MayBePublicWithout x1), Any (MayBePublicWithout x2) -> compare_maybepublicwithout x1 x2 | Any (MustBeProtectedBy x1), Any (MustBeProtectedBy x2) -> compare_mustbeprotectedby x1 x2 - | Any (EvalFunvarA e1), Any (EvalFunvarA e2) -> CilType.Exp.compare e1 e2 + | Any (EvalFunvar e1), Any (EvalFunvar e2) -> CilType.Exp.compare e1 e2 | Any (EvalInt e1), Any (EvalInt e2) -> CilType.Exp.compare e1 e2 | Any (EvalStr e1), Any (EvalStr e2) -> CilType.Exp.compare e1 e2 | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 @@ -374,7 +374,7 @@ struct | Any (MayBePublic x) -> hash_maybepublic x | Any (MayBePublicWithout x) -> hash_maybepublicwithout x | Any (MustBeProtectedBy x) -> hash_mustbeprotectedby x - | Any (EvalFunvarA e) -> CilType.Exp.hash e + | Any (EvalFunvar e) -> CilType.Exp.hash e | Any (EvalInt e) -> CilType.Exp.hash e | Any (EvalStr e) -> CilType.Exp.hash e | Any (EvalLength e) -> CilType.Exp.hash e @@ -423,7 +423,7 @@ struct | Any CurrentThreadId -> Pretty.dprintf "CurrentThreadId" | Any ThreadCreateIndexedNode -> Pretty.dprintf "ThreadCreateIndexedNode" | Any MayBeThreadReturn -> Pretty.dprintf "MayBeThreadReturn" - | Any (EvalFunvarA e) -> Pretty.dprintf "EvalFunvarA %a" CilType.Exp.pretty e + | Any (EvalFunvar e) -> Pretty.dprintf "EvalFunvar %a" CilType.Exp.pretty e | Any (EvalInt e) -> Pretty.dprintf "EvalInt %a" CilType.Exp.pretty e | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 9bbfef9c89..3ea62a2f4c 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -299,12 +299,12 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.EvalFunvarA e -> + | Queries.EvalFunvar e -> let (d,l) = ctx.local in if leq0 l then Queries.AD.empty () else - query' ctx (Queries.EvalFunvarA e) + query' ctx (Queries.EvalFunvar e) | q -> query' ctx q end @@ -754,7 +754,7 @@ struct [v] | _ -> (* Depends on base for query. *) - let ad = ctx.ask (Queries.EvalFunvarA e) in + let ad = ctx.ask (Queries.EvalFunvar e) in List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) in let one_function f = From 8e42121dd7a75bdc44568defb29e44dc5cec66a4 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 15:45:30 +0300 Subject: [PATCH 238/401] Add MayBeTaintedA to queries --- src/domains/queries.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 8626fb9d11..2cefb3f318 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -121,6 +121,7 @@ type _ t = | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t | MayBeTainted: LS.t t + | MayBeTaintedA: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -186,6 +187,7 @@ struct | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) | MayBeTainted -> (module LS) + | MayBeTaintedA -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -250,6 +252,7 @@ struct | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () | MayBeTainted -> LS.top () + | MayBeTaintedA -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () end @@ -301,6 +304,7 @@ struct | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 | Any MayBeTainted -> 41 + | Any MayBeTaintedA -> 4141 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -453,6 +457,7 @@ struct | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" + | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv From 392834b054524c2eeac202de7b7ffce375717be3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 17:01:14 +0300 Subject: [PATCH 239/401] Use MayBeTaintedA in base --- src/analyses/base.ml | 69 ++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 5dfbe3c64a..6f0f3a099a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2386,34 +2386,38 @@ struct in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st - let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : Q.LS.t) : store = + let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : AD.t) : store = let ask = (Analyses.ask_of_ctx ctx) in - Q.LS.fold (fun (v, o) st -> - if CPA.mem v fun_st.cpa then - let lval = Mval.Exp.to_cil (v,o) in - let address = eval_lv ask ctx.global st lval in - let lval_type = (AD.type_of address) in - if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Mval.Exp.pretty (v, o) d_type lval_type; - match (CPA.find_opt v (fun_st.cpa)), lval_type with - | None, _ -> st - (* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *) - | Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa} - (* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *) - | Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa} - | _, _ -> begin - let new_val = get ask ctx.global fun_st address None in - if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val; - let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in - let partDep = Dep.find_opt v fun_st.deps in - match partDep with - | None -> st' - (* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *) - | Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in - match val_opt with - | None -> accCPA - | Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)} - end - else st) tainted_lvs local_st + AD.fold (fun addr st -> + match addr with + | Addr.Addr (v,o) -> + if CPA.mem v fun_st.cpa then + let lval = Addr.Mval.to_cil (v,o) in + let address = eval_lv ask ctx.global st lval in + let lval_type = Addr.type_of addr in + if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Addr.Mval.pretty (v,o) d_type lval_type; + match (CPA.find_opt v (fun_st.cpa)), lval_type with + | None, _ -> st + (* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *) + | Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa} + (* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *) + | Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa} + | _, _ -> begin + let new_val = get ask ctx.global fun_st address None in + if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val; + let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in + let partDep = Dep.find_opt v fun_st.deps in + match partDep with + | None -> st' + (* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *) + | Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in + match val_opt with + | None -> accCPA + | Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)} + end + else st + | _ -> st + ) tainted_lvs local_st let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = let combine_one (st: D.t) (fun_st: D.t) = @@ -2427,10 +2431,10 @@ struct (* Remove the return value as this is dealt with separately. *) let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in let ask = (Analyses.ask_of_ctx ctx) in - let tainted = f_ask.f Q.MayBeTainted in - if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname Q.LS.pretty tainted; + let tainted = f_ask.f Q.MayBeTaintedA in + if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted; if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; - if Q.LS.is_top tainted then + if AD.is_top tainted then let cpa_local = CPA.filter (fun x _ -> not (is_global ask x)) st.cpa in let cpa' = CPA.fold CPA.add cpa_noreturn cpa_local in (* add cpa_noreturn to cpa_local *) if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty cpa'; @@ -2445,7 +2449,10 @@ struct let cpa_caller' = CPA.fold CPA.add cpa_new cpa_caller in if M.tracing then M.trace "taintPC" "cpa_caller': %a\n" CPA.pretty cpa_caller'; (* remove lvals from the tainted set that correspond to variables for which we just added a new mapping from the callee*) - let tainted = Q.LS.filter (fun (v, _) -> not (CPA.mem v cpa_new)) tainted in + let tainted = AD.filter (function + | Addr.Addr (v,_) -> not (CPA.mem v cpa_new) + | _ -> false + ) tainted in let st_combined = combine_st ctx {st with cpa = cpa_caller'} fun_st tainted in if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty st_combined.cpa; { fun_st with cpa = st_combined.cpa } From 224615f63b07448ba45afb79f42c2d8284d4b968 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:19:38 +0300 Subject: [PATCH 240/401] Use MayBeTaintedA --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 10 +++-- src/analyses/poisonVariables.ml | 18 +++++---- src/analyses/taintPartialContexts.ml | 42 ++++++++------------ src/analyses/varEq.ml | 11 +++-- src/cdomains/addressDomain.ml | 2 + 7 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ca60e5dc30..64b367be68 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -407,7 +407,7 @@ struct let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) - let tainted = f_ask.f Queries.MayBeTainted in + let tainted = f_ask.f Queries.MayBeTaintedA in let tainted_vars = TaintPartialContexts.conv_varset tainted in let new_rel = RD.keep_filter st.rel (fun var -> match RV.find_metadata var with diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 04246bf28a..282f5bf020 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -146,7 +146,7 @@ struct (* combine caller's state with globals from callee *) (* TODO (precision): globals with only global vars are kept, the rest is lost -> collect which globals are assigned to *) (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) ctx.local au *) - let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTainted) in + let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTaintedA) in D.only_untainted ctx.local tainted (* tainted globals might have changed... *) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 0375bd3f74..3cc10196fd 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -24,10 +24,14 @@ struct not v.vglob (* *) && not (BaseUtil.is_volatile v) && v.vstorage <> Static let relevants_from_ls ls = - if Queries.LS.is_top ls then + if Queries.AD.is_top ls then VS.top () else - Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ()) + Queries.AD.fold (fun addr acc -> + match addr with + | Queries.AD.Addr.Addr (v, _) when is_relevant v -> VS.add v acc + | _ -> acc + ) ls (VS.empty ()) let relevants_from_ad ad = (* TODO: what about AD with both known and unknown pointers? *) @@ -45,7 +49,7 @@ struct [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in + let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTaintedA) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index f69e5b2fbc..ddbb6a5a40 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -15,12 +15,16 @@ struct let context _ _ = () - 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 check_mval tainted (addr: Queries.AD.elt) = + match addr with + | Queries.AD.Addr.Addr (v,_) -> + 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_mval tainted ((v, offset): Queries.LS.elt) = match offset with - | `NoOffset -> VS.remove v tainted + let rem_mval tainted (addr: Queries.AD.elt) = + match addr with + | Queries.AD.Addr.Addr (v,`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 *) @@ -90,7 +94,7 @@ struct | 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) + | Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (Queries.AD.Addr.Addr (v,o)) | _ -> () ) ad end; @@ -103,7 +107,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 _ -> rem_mval vs addr | _ -> vs ) ad ctx.local end diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index d4ea17d2e0..39183ef087 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -6,31 +6,19 @@ 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 AD = ValueDomain.AD module Spec = struct include Analyses.IdentitySpec let name () = "taintPartialContexts" - module D = D + module D = AD module C = Lattice.Unit (* Add Lval or any Lval which it may point to to the set *) let taint_lval ctx (lval:lval) : D.t = - 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.MayPointTo e))) d - ) + D.union (ctx.ask (Queries.MayPointTo (AddrOf lval))) ctx.local (* this analysis is context insensitive*) let context _ _ = () @@ -45,14 +33,12 @@ struct let d_return = if D.is_top d then d - else ( + else let locals = f.sformals @ f.slocals in - D.filter (fun (v, _) -> - not (List.exists (fun local -> - CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local)) - ) locals) + D.filter (function + | AD.Addr.Addr (v,_) -> not (List.exists (fun local -> CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local))) locals) + | _ -> false ) d - ) in if M.tracing then M.trace "taintPC" "returning from %s: tainted vars: %a\n without locals: %a\n" f.svar.vname D.pretty d D.pretty d_return; d_return @@ -94,9 +80,10 @@ struct else deep_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs + (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) + let d = List.fold_left (fun accD addr -> D.union accD (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.ReachableFrom addr)))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs in d @@ -111,7 +98,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | MayBeTainted -> (ctx.local : Queries.LS.t) + | MayBeTaintedA -> (ctx.local : Queries.AD.t) | _ -> Queries.Result.top q end @@ -122,5 +109,8 @@ let _ = module VS = SetDomain.ToppedSet(Basetype.Variables) (struct let topname = "All" end) (* Convert Lval set to (less precise) Varinfo set. *) -let conv_varset (lval_set : Spec.D.t) : VS.t = - if Spec.D.is_top lval_set then VS.top () else VS.of_list (List.map (fun (v, _) -> v) (Spec.D.elements lval_set)) +let conv_varset (addr_set : Spec.D.t) : VS.t = + if Spec.D.is_top addr_set then + VS.top () + else + VS.of_list (List.filter_map (fun addr -> Spec.D.Addr.to_var_may addr) (Spec.D.elements addr_set)) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 77823d99d7..3bfd188aba 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -433,14 +433,19 @@ struct | false -> [ctx.local,nst] let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let tainted = f_ask.f Queries.MayBeTainted in + let tainted = f_ask.f Queries.MayBeTaintedA in let d_local = (* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *) - if Queries.LS.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then + if Queries.AD.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then D.top () else - let taint_exp = Queries.ES.of_list (List.map Mval.Exp.to_cil_exp (Queries.LS.elements tainted)) in + let taint_exp = + Queries.AD.elements tainted + |> List.filter_map Addr.to_mval + |> List.map Addr.Mval.to_cil_exp + |> Queries.ES.of_list + in D.filter (fun exp -> not (Queries.ES.mem exp taint_exp)) ctx.local in let d = D.meet au d_local in 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 26151efbaf3d2cb107a69209ca5414c96d9b1cd2 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:21:09 +0300 Subject: [PATCH 241/401] Remove MayBeTainted from queries --- src/domains/queries.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 2cefb3f318..65c915e472 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -120,7 +120,6 @@ type _ t = | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t - | MayBeTainted: LS.t t | MayBeTaintedA: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -186,7 +185,6 @@ struct | WarnGlobal _ -> (module Unit) | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) - | MayBeTainted -> (module LS) | MayBeTaintedA -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -251,7 +249,6 @@ struct | WarnGlobal _ -> Unit.top () | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () - | MayBeTainted -> LS.top () | MayBeTaintedA -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () @@ -303,8 +300,7 @@ struct | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 - | Any MayBeTainted -> 41 - | Any MayBeTaintedA -> 4141 + | Any MayBeTaintedA -> 41 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -456,7 +452,6 @@ struct | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" - | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf From 0d3af6cfeb416e039a0058670be588bcf40a74bc Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 29 Aug 2023 20:23:02 +0300 Subject: [PATCH 242/401] Rename MayBeTaintedA -> MayBeTainted --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/condVars.ml | 2 +- src/analyses/modifiedSinceLongjmp.ml | 2 +- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/varEq.ml | 2 +- src/domains/queries.ml | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 64b367be68..ca60e5dc30 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -407,7 +407,7 @@ struct let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in if M.tracing then M.tracel "combine" "relation remove vars: %a\n" (docList (fun v -> Pretty.text (RD.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) - let tainted = f_ask.f Queries.MayBeTaintedA in + let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in let new_rel = RD.keep_filter st.rel (fun var -> match RV.find_metadata var with diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6f0f3a099a..24596f5072 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2431,7 +2431,7 @@ struct (* Remove the return value as this is dealt with separately. *) let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in let ask = (Analyses.ask_of_ctx ctx) in - let tainted = f_ask.f Q.MayBeTaintedA in + let tainted = f_ask.f Q.MayBeTainted in if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted; if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa; if AD.is_top tainted then diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 282f5bf020..04246bf28a 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -146,7 +146,7 @@ struct (* combine caller's state with globals from callee *) (* TODO (precision): globals with only global vars are kept, the rest is lost -> collect which globals are assigned to *) (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) ctx.local au *) - let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTaintedA) in + let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTainted) in D.only_untainted ctx.local tainted (* tainted globals might have changed... *) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index 3cc10196fd..fafbe54840 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -49,7 +49,7 @@ struct [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTaintedA) in + let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 39183ef087..660766667e 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -98,7 +98,7 @@ struct let query ctx (type a) (q: a Queries.t) : a Queries.result = match q with - | MayBeTaintedA -> (ctx.local : Queries.AD.t) + | MayBeTainted -> (ctx.local : Queries.AD.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 3bfd188aba..1159ff8b1b 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -433,7 +433,7 @@ struct | false -> [ctx.local,nst] let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let tainted = f_ask.f Queries.MayBeTaintedA in + let tainted = f_ask.f Queries.MayBeTainted in let d_local = (* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 65c915e472..e51ee90f68 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -120,7 +120,7 @@ type _ t = | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) | IterSysVars: VarQuery.t * Obj.t VarQuery.f -> Unit.t t (** [iter_vars] for [Constraints.FromSpec]. [Obj.t] represents [Spec.V.t]. *) | MayAccessed: AccessDomain.EventSet.t t - | MayBeTaintedA: AD.t t + | MayBeTainted: AD.t t | MayBeModifiedSinceSetjmp: JmpBufDomain.BufferEntry.t -> VS.t t | TmpSpecial: Mval.Exp.t -> ML.t t @@ -185,7 +185,7 @@ struct | WarnGlobal _ -> (module Unit) | IterSysVars _ -> (module Unit) | MayAccessed -> (module AccessDomain.EventSet) - | MayBeTaintedA -> (module AD) + | MayBeTainted -> (module AD) | MayBeModifiedSinceSetjmp _ -> (module VS) | TmpSpecial _ -> (module ML) @@ -249,7 +249,7 @@ struct | WarnGlobal _ -> Unit.top () | IterSysVars _ -> Unit.top () | MayAccessed -> AccessDomain.EventSet.top () - | MayBeTaintedA -> AD.top () + | MayBeTainted -> AD.top () | MayBeModifiedSinceSetjmp _ -> VS.top () | TmpSpecial _ -> ML.top () end @@ -300,7 +300,7 @@ struct | Any (InvariantGlobal _) -> 38 | Any (MustProtectedVars _) -> 39 | Any MayAccessed -> 40 - | Any MayBeTaintedA -> 41 + | Any MayBeTainted -> 41 | Any (PathQuery _) -> 42 | Any DYojson -> 43 | Any (EvalValue _) -> 44 @@ -452,7 +452,7 @@ struct | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" - | Any MayBeTaintedA -> Pretty.dprintf "MayBeTaintedA" + | Any MayBeTainted -> Pretty.dprintf "MayBeTainted" | Any DYojson -> Pretty.dprintf "DYojson" | Any MayBeModifiedSinceSetjmp buf -> Pretty.dprintf "MayBeModifiedSinceSetjmp %a" JmpBufDomain.BufferEntry.pretty buf | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv From aceffa897f7294261db65e2531661c64239a0376 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:48:35 +0200 Subject: [PATCH 243/401] Allow Queries.BlobSize to be asked for the size from the start address The idea is to ignore address offsets and thus getting bot from Queries.BlobSize --- src/analyses/base.ml | 14 ++++++++++++-- src/domains/queries.ml | 10 ++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 824fde18d9..b50fed9c50 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1256,16 +1256,26 @@ struct end | Q.EvalValue e -> eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e - | Q.BlobSize e -> begin + | Q.BlobSize (e, from_base_addr) -> begin let p = eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> let 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)) || o <> `NoOffset) s then + (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) + if ValueDomainQueries.LS.exists (fun (v, o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false)) s then Queries.Result.bot q else ( + (* If we need the BlobSize from the base address, then remove any offsets *) + let a = + if from_base_addr then + ValueDomainQueries.LS.elements s + |> List.map (fun (v, _) -> Addr.of_var v) + |> AD.of_list + else + a + in 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 diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 214fcf1384..1735a047ea 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -91,7 +91,9 @@ type _ t = | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) | EvalValue: exp -> VD.t t - | BlobSize: exp -> ID.t t (* size of a dynamically allocated `Blob pointed to by exp *) + | BlobSize: exp * bool -> ID.t t + (* Size of a dynamically allocated `Blob pointed to by exp. *) + (* If the second component is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) | CondVars: exp -> ES.t t | PartAccess: access -> Obj.t t (** Only queried by access and deadlock analysis. [Obj.t] represents [MCPAccess.A.t], needed to break dependency cycle. *) | IterPrevVars: iterprevvar -> Unit.t t @@ -334,7 +336,7 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize e1), Any (BlobSize e2) -> CilType.Exp.compare e1 e2 + | Any (BlobSize (e1, _)), Any (BlobSize (e2, _)) -> CilType.Exp.compare e1 e2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 @@ -379,7 +381,7 @@ struct | Any (EvalLength e) -> CilType.Exp.hash e | Any (EvalMutexAttr e) -> CilType.Exp.hash e | Any (EvalValue e) -> CilType.Exp.hash e - | Any (BlobSize e) -> CilType.Exp.hash e + | Any (BlobSize (e, from_base_addr)) -> CilType.Exp.hash e + Hashtbl.hash from_base_addr | Any (CondVars e) -> CilType.Exp.hash e | Any (PartAccess p) -> hash_access p | Any (IterPrevVars i) -> 0 @@ -427,7 +429,7 @@ struct | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e | Any (EvalValue e) -> Pretty.dprintf "EvalValue %a" CilType.Exp.pretty e - | Any (BlobSize e) -> Pretty.dprintf "BlobSize %a" CilType.Exp.pretty e + | Any (BlobSize (e, from_base_addr)) -> Pretty.dprintf "BlobSize %a (from base address: %b)" CilType.Exp.pretty e from_base_addr | Any (CondVars e) -> Pretty.dprintf "CondVars %a" CilType.Exp.pretty e | Any (PartAccess p) -> Pretty.dprintf "PartAccess _" | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" From 7fb671947da29c5f1d6a67b9a1b91d5e8fd3db05 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:49:32 +0200 Subject: [PATCH 244/401] Use Queries.BlobSize for getting blob sizes without address offsets --- src/analyses/memOutOfBounds.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 2b4ca3d4c4..e81b097054 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -100,7 +100,8 @@ struct * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? *) if points_to_heap_only ctx ptr then - ctx.ask (Queries.BlobSize ptr) + (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize (ptr, true)) else match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) -> From 0e126df6b638d755af1e36ba4b50bc1b67935eb9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 16:51:29 +0200 Subject: [PATCH 245/401] Clean up unnecessary comments --- src/analyses/memOutOfBounds.ml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index e81b097054..acf6eb7660 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -90,15 +90,6 @@ struct | _ -> false let get_size_of_ptr_target ctx ptr = - (* Call Queries.BlobSize only if ptr points solely to the heap. Otherwise we get bot *) - (* TODO: - * If the ptr's address has been offset, then Queries.BlobSize will answer with bot. For example: - char *ptr = malloc(10 * sizeof(char)); - ptr++; - printf("%s", *ptr); // => Issues a WARN even though it shouldn't - * However, in this case we're too imprecise, since we're always comparing something with bot - * and thus we always get a warning for an OOB memory access. Should we maybe change Queries.BlobSize again? - *) if points_to_heap_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) ctx.ask (Queries.BlobSize (ptr, true)) From 293d3e7d02bd4a8a52db31611bb4f0ebe4e1ffc5 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 17:22:20 +0200 Subject: [PATCH 246/401] Add check for implicit pointer dereferences in special functions Also remove a resolved TODO comment --- src/analyses/memOutOfBounds.ml | 44 +++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index acf6eb7660..0e93adb295 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -174,7 +174,6 @@ struct (* Offset should be the same for all elements in the points-to set. Hence, we can just pick one element and obtain its offset - TODO: Does this make sense? *) let (_, o) = VDQ.LS.choose a in let rec to_int_dom_offs = function @@ -198,19 +197,22 @@ struct M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - and check_lval_for_oob_access ctx lval = + and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = if not @@ lval_contains_a_ptr lval then () else - match lval with - | Var _, _ -> () - | Mem e, _ -> + (* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *) + (* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *) + match lval, is_implicitly_derefed with + | (Var _, _), false -> () + | (Var v, _), true -> check_no_binop_deref ctx (Lval lval) + | (Mem e, _), _ -> begin match e with | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> check_binop_exp ctx binop e1 e2 t; - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2 - | _ -> check_exp_for_oob_access ctx e + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2 + | _ -> check_exp_for_oob_access ctx ~is_implicitly_derefed e end and check_no_binop_deref ctx lval_exp = @@ -235,7 +237,7 @@ struct end | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp - and check_exp_for_oob_access ctx exp = + and check_exp_for_oob_access ctx ?(is_implicitly_derefed = false) exp = match exp with | Const _ | SizeOf _ @@ -247,17 +249,17 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> check_exp_for_oob_access ctx e + | CastE (_, e) -> check_exp_for_oob_access ctx ~is_implicitly_derefed e | BinOp (bop, e1, e2, t) -> - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2 + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2 | Question (e1, e2, e3, _) -> - check_exp_for_oob_access ctx e1; - check_exp_for_oob_access ctx e2; - check_exp_for_oob_access ctx e3 + check_exp_for_oob_access ctx ~is_implicitly_derefed e1; + check_exp_for_oob_access ctx ~is_implicitly_derefed e2; + check_exp_for_oob_access ctx ~is_implicitly_derefed e3 | Lval lval | StartOf lval - | AddrOf lval -> check_lval_for_oob_access ctx lval + | AddrOf lval -> check_lval_for_oob_access ctx ~is_implicitly_derefed lval and check_binop_exp ctx binop e1 e2 t = let binopexp = BinOp (binop, e1, e2, t) in @@ -314,8 +316,16 @@ struct ctx.local let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let desc = LibraryFunctions.find f in + let is_arg_implicitly_derefed arg = + let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in + let read_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = true } arglist in + let write_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in + let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in + List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args + in Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - List.iter (fun arg -> check_exp_for_oob_access ctx arg) arglist; + List.iter (fun arg -> check_exp_for_oob_access ctx ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From e4b349a61545e00c53f2c4267a496f959b2e83fa Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 31 Aug 2023 17:23:03 +0200 Subject: [PATCH 247/401] Add a test case with implicit dereferences --- .../77-mem-oob/05-oob-implicit-deref.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/regression/77-mem-oob/05-oob-implicit-deref.c diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/77-mem-oob/05-oob-implicit-deref.c new file mode 100644 index 0000000000..088493d3b9 --- /dev/null +++ b/tests/regression/77-mem-oob/05-oob-implicit-deref.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include +#include + +int main(int argc, char const *argv[]) { + int *ptr = malloc(4 * sizeof(int)); + + // Both lines below are considered derefs => no need to warn, since ptr is pointing within its bounds + memset(ptr, 0, 4 * sizeof(int)); //NOWARN + printf("%p", (void *) ptr); //NOWARN + ptr = ptr + 10; // ptr no longer points within its allocated bounds + + // Each of both lines below should now receive a WARN + memset(ptr, 0, 4 * sizeof(int)); //WARN + printf("%p", (void *) ptr); //WARN + + return 0; +} From b55afa6a6fdafab6f9a9b3c4502d3d2e2d15e9d9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Sep 2023 13:05:30 +0300 Subject: [PATCH 248/401] 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 249/401] 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 2b4549905c80bbca10d82e04a7d2f21b9978143e Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:01:45 +0200 Subject: [PATCH 250/401] Disable Info warnings for test case 77/05 for now Also add a comment explaining why it's a temporary solution --- tests/regression/77-mem-oob/05-oob-implicit-deref.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/05-oob-implicit-deref.c b/tests/regression/77-mem-oob/05-oob-implicit-deref.c index 088493d3b9..8bec6a72e0 100644 --- a/tests/regression/77-mem-oob/05-oob-implicit-deref.c +++ b/tests/regression/77-mem-oob/05-oob-implicit-deref.c @@ -1,4 +1,8 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +/* + Note: the "--disable warn.info" above is a temporary workaround, + since the GitHub CI seems to be considering Info messages as violations of NOWARN (cf. https://github.com/goblint/analyzer/issues/1151) +*/ #include #include #include From 924130132a766149cc97745629fd52e08ec6b798 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:02:35 +0200 Subject: [PATCH 251/401] Remove TODO comment and add a few other explaining comments --- src/analyses/memOutOfBounds.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 0e93adb295..1738970739 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -1,3 +1,5 @@ +(** An analysis for the detection of out-of-bounds memory accesses ([memOutOfBounds]).*) + open GoblintCil open Analyses open MessageCategory @@ -5,6 +7,12 @@ open MessageCategory module AS = AnalysisState module VDQ = ValueDomainQueries +(* + Note: + * This functionality is implemented as an analysis solely for the sake of maintaining + separation of concerns, as well as for having the ablility to conveniently turn it on or off + * It doesn't track any internal state +*) module Spec = struct include Analyses.IdentitySpec @@ -12,7 +20,6 @@ struct module D = Lattice.Unit module C = D - (* TODO: Check out later for benchmarking *) let context _ _ = () let name () = "memOutOfBounds" From 0bae455057d108b3d2e5f368622384ccdf9f8d44 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 6 Sep 2023 12:07:16 +0200 Subject: [PATCH 252/401] Use a record instead of a tuple for Queries.BlobSize --- src/analyses/base.ml | 2 +- src/analyses/memOutOfBounds.ml | 2 +- src/domains/queries.ml | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f237ca8296..28a5b236a3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1257,7 +1257,7 @@ struct end | Q.EvalValue e -> eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e - | Q.BlobSize (e, from_base_addr) -> begin + | Q.BlobSize {exp = e; base_address = from_base_addr} -> begin let p = eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e in (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 1738970739..97907e9d6f 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -99,7 +99,7 @@ struct let get_size_of_ptr_target ctx ptr = if points_to_heap_only ctx ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize (ptr, true)) + ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else match ctx.ask (Queries.MayPointTo ptr) with | a when not (Queries.LS.is_top a) -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1735a047ea..d7de0b1e79 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -91,9 +91,9 @@ type _ t = | EvalStr: exp -> SD.t t | EvalLength: exp -> ID.t t (* length of an array or string *) | EvalValue: exp -> VD.t t - | BlobSize: exp * bool -> ID.t t + | BlobSize: {exp: Cil.exp; base_address: bool} -> ID.t t (* Size of a dynamically allocated `Blob pointed to by exp. *) - (* If the second component is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) + (* If the record's second field is set to true, then address offsets are discarded and the size of the `Blob is asked for the base address. *) | CondVars: exp -> ES.t t | PartAccess: access -> Obj.t t (** Only queried by access and deadlock analysis. [Obj.t] represents [MCPAccess.A.t], needed to break dependency cycle. *) | IterPrevVars: iterprevvar -> Unit.t t @@ -336,7 +336,7 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize (e1, _)), Any (BlobSize (e2, _)) -> CilType.Exp.compare e1 e2 + | Any (BlobSize {exp = e1; _}), Any (BlobSize {exp = e2; _}) -> CilType.Exp.compare e1 e2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 @@ -381,7 +381,7 @@ struct | Any (EvalLength e) -> CilType.Exp.hash e | Any (EvalMutexAttr e) -> CilType.Exp.hash e | Any (EvalValue e) -> CilType.Exp.hash e - | Any (BlobSize (e, from_base_addr)) -> CilType.Exp.hash e + Hashtbl.hash from_base_addr + | Any (BlobSize {exp = e; base_address = b}) -> CilType.Exp.hash e + Hashtbl.hash b | Any (CondVars e) -> CilType.Exp.hash e | Any (PartAccess p) -> hash_access p | Any (IterPrevVars i) -> 0 @@ -429,7 +429,7 @@ struct | Any (EvalStr e) -> Pretty.dprintf "EvalStr %a" CilType.Exp.pretty e | Any (EvalLength e) -> Pretty.dprintf "EvalLength %a" CilType.Exp.pretty e | Any (EvalValue e) -> Pretty.dprintf "EvalValue %a" CilType.Exp.pretty e - | Any (BlobSize (e, from_base_addr)) -> Pretty.dprintf "BlobSize %a (from base address: %b)" CilType.Exp.pretty e from_base_addr + | Any (BlobSize {exp = e; base_address = b}) -> Pretty.dprintf "BlobSize %a (base_address: %b)" CilType.Exp.pretty e b | Any (CondVars e) -> Pretty.dprintf "CondVars %a" CilType.Exp.pretty e | Any (PartAccess p) -> Pretty.dprintf "PartAccess _" | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" From d97504bf08c8f79e04539665432ad05ffd843523 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Sep 2023 16:13:29 +0300 Subject: [PATCH 253/401] 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 254/401] 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 255/401] 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 256/401] 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 257/401] 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 258/401] 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 259/401] 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 260/401] 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 261/401] 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 262/401] 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 263/401] 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 264/401] 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 265/401] 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 266/401] 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 267/401] 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 268/401] 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 269/401] 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 270/401] 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 271/401] 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 272/401] 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 273/401] 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 274/401] 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 275/401] 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 276/401] 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 277/401] 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 278/401] 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 279/401] 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 280/401] 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 281/401] 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 282/401] 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 283/401] 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 284/401] 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 285/401] 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 286/401] 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 287/401] 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 288/401] 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 289/401] 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 290/401] 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 291/401] 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 292/401] 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 cb08d337d202fe2ac6154390f8973f9195c5a7ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Sep 2023 11:03:07 +0300 Subject: [PATCH 293/401] Simplify some AD conversions, add TODOs --- src/analyses/commonPriv.ml | 2 +- src/analyses/poisonVariables.ml | 11 +++-------- src/analyses/taintPartialContexts.ml | 2 +- src/analyses/varEq.ml | 3 +-- src/cdomains/lockDomain.ml | 2 +- src/framework/constraints.ml | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 199fae98b0..7d06844c1b 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -128,7 +128,7 @@ struct Lockset.empty () else let ad = ask.f Queries.MustLockset in - Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) + Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: use AD as Lockset *) (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index ddbb6a5a40..acd687835e 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -92,11 +92,8 @@ struct | 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!" | 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 (Queries.AD.Addr.Addr (v,o)) - | _ -> () - ) ad + (* Use original access state instead of current with removed written vars. *) + Queries.AD.iter (check_mval octx.local) ad end; ctx.local | Access {ad; kind = Write; _} -> @@ -106,9 +103,7 @@ struct ctx.local | ad -> Queries.AD.fold (fun addr vs -> - match addr with - | Queries.AD.Addr.Addr _ -> rem_mval vs addr - | _ -> vs + rem_mval vs addr ) ad ctx.local end | _ -> ctx.local diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index eaafbf01f5..feb9599977 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -113,4 +113,4 @@ let conv_varset (addr_set : Spec.D.t) : VS.t = if Spec.D.is_top addr_set then VS.top () else - VS.of_list (List.filter_map (fun addr -> Spec.D.Addr.to_var_may addr) (Spec.D.elements addr_set)) + VS.of_list (Spec.D.to_var_may addr_set) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index db164a313c..dcd49c9f02 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -441,8 +441,7 @@ struct D.top () else let taint_exp = - Queries.AD.elements tainted - |> List.filter_map Addr.to_mval + Queries.AD.to_mval tainted |> List.map Addr.Mval.to_cil_exp |> Queries.ES.of_list in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 0de5afc32c..4bc97b34ab 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -7,7 +7,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil -module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO HoareDomain? *) +module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) module Simple = Lattice.Reverse (Mutexes) module Priorities = IntDomain.Lifted diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 3ea62a2f4c..26fdfac606 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -755,7 +755,7 @@ struct | _ -> (* Depends on base for query. *) let ad = ctx.ask (Queries.EvalFunvar e) in - List.filter_map (fun addr -> Queries.AD.Addr.to_var addr) (Queries.AD.elements ad) + Queries.AD.to_var_may ad (* TODO: don't convert, handle UnknownPtr below *) in let one_function f = match f.vtype with From fb7159c0fe7aa1e0ed887f807db7feb3c9699114 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 16 Sep 2023 16:33:40 +0200 Subject: [PATCH 294/401] 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 295/401] 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 296/401] 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 297/401] 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 298/401] 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 299/401] 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 300/401] 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 a32be380cd024523408b4e1348eab0d65da2842c Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:06:56 +0300 Subject: [PATCH 301/401] Use AddressDomain in eval_funvar --- src/analyses/base.ml | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1364c009d5..912e298143 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1097,20 +1097,15 @@ struct | Int x -> ValueDomain.ID.to_int x | _ -> None - let eval_funvar ctx fval: varinfo list = - let exception OnlyUnknown in - try - let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in - 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; - 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; - [dummyFunDec.svar] + let eval_funvar ctx fval: Queries.AD.t = + let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in + if AD.is_top fp then begin + if AD.cardinal fp = 1 then + (M.warn ~category:Unsound "Unknown call to function %a." d_exp fval;) + else + (M.warn ~category:Imprecise "Function pointer %a may contain unknown functions." d_exp fval;) + end; + fp (** Evaluate expression as address. Avoids expensive Apron EvalInt if the Int result would be useless to us anyway. *) @@ -1204,10 +1199,7 @@ struct let query ctx (type a) (q: a Q.t): a Q.result = match q with | Q.EvalFunvar e -> - begin - let fs = eval_funvar ctx e in - List.fold_left (fun ad v -> Q.AD.join (Q.AD.of_var v) ad) (Q.AD.empty ()) fs - end + eval_funvar ctx e | Q.EvalJumpBuf e -> begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with | Address jmp_buf -> From 3879fdc2a1f3ced2cc11c15cacd36250219b6744 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:12:19 +0300 Subject: [PATCH 302/401] Use relevants_from_ad instead of relevants_from_ls --- src/analyses/modifiedSinceLongjmp.ml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/analyses/modifiedSinceLongjmp.ml b/src/analyses/modifiedSinceLongjmp.ml index fafbe54840..5dae8748cb 100644 --- a/src/analyses/modifiedSinceLongjmp.ml +++ b/src/analyses/modifiedSinceLongjmp.ml @@ -23,7 +23,8 @@ struct (* Only checks for v.vglob on purpose, acessing espaced locals after longjmp is UB like for any local *) not v.vglob (* *) && not (BaseUtil.is_volatile v) && v.vstorage <> Static - let relevants_from_ls ls = + 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 @@ -33,23 +34,12 @@ struct | _ -> acc ) ls (VS.empty ()) - let relevants_from_ad ad = - (* TODO: what about AD with both known and unknown pointers? *) - if Queries.AD.is_top ad then - VS.top () - else - Queries.AD.fold (fun addr vs -> - match addr with - | 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 = [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = - let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in + let taintedcallee = relevants_from_ad (f_ask.f Queries.MayBeTainted) in add_to_all_defined taintedcallee ctx.local let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = From 29179999053aa95fcdfe44b3fb83f501be9f7b07 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 18 Sep 2023 15:47:48 +0300 Subject: [PATCH 303/401] Use VS domain instead of AD for MustProtectedVars --- src/analyses/commonPriv.ml | 11 +++-------- src/analyses/mutexAnalysis.ml | 4 ++-- src/domains/queries.ml | 6 +++--- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 7d06844c1b..db75455b40 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -60,15 +60,10 @@ struct ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection}) let protected_vars (ask: Q.ask): varinfo list = - let module VS = Set.Make (CilType.Varinfo) in Q.AD.fold (fun m acc -> - Q.AD.fold (fun l acc -> - match l with - | Q.AD.Addr.Addr (v,_) -> VS.add v acc (* always `NoOffset from mutex analysis *) - | _ -> acc - ) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc - ) (ask.f Q.MustLockset) VS.empty - |> VS.elements + Q.VS.join (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc + ) (ask.f Q.MustLockset) (Q.VS.empty ()) + |> Q.VS.elements end module MutexGlobals = diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9cc019db76..5a61976ef5 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -240,8 +240,8 @@ struct | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> - Queries.AD.join (Queries.AD.of_var v) acc - ) protected (Queries.AD.empty ()) + Queries.VS.add v acc + ) protected (Queries.VS.empty ()) | Queries.IterSysVars (Global g, f) -> f (Obj.repr (V.protecting g)) (* TODO: something about V.protected? *) | WarnGlobal g -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index e51ee90f68..3a0e493640 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -114,7 +114,7 @@ type _ t = | CreatedThreads: ConcDomain.ThreadSet.t t | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t - | MustProtectedVars: mustprotectedvars -> AD.t t + | MustProtectedVars: mustprotectedvars -> VS.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -179,7 +179,7 @@ struct | CreatedThreads -> (module ConcDomain.ThreadSet) | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) - | MustProtectedVars _ -> (module AD) + | MustProtectedVars _ -> (module VS) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -243,7 +243,7 @@ struct | CreatedThreads -> ConcDomain.ThreadSet.top () | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () - | MustProtectedVars _ -> AD.top () + | MustProtectedVars _ -> VS.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () From c9c3980b6eea0022a3bd95b15ad05563cfa0c85b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 15:51:16 +0300 Subject: [PATCH 304/401] Add missing library functions for nnn --- src/analyses/libraryFunctions.ml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..7b92ff8a86 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -117,12 +117,14 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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]]); + ("mbstowcs", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); ("abs", unknown [drop "j" []]); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_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 }); + ("atexit", unknown [drop "function" [s]]); ] (** C POSIX library functions. @@ -246,6 +248,8 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("timer_gettime", unknown [drop "timerid" []; drop "curr_value" [w_deep]]); ("timer_getoverrun", unknown [drop "timerid" []]); ("lstat", unknown [drop "pathname" [r]; drop "statbuf" [w]]); + ("fstat", unknown [drop "fd" []; drop "buf" [w]]); + ("fstatat", unknown [drop "dirfd" []; drop "pathname" [r]; drop "buf" [w]; drop "flags" []]); ("getpwnam", unknown [drop "name" [r]]); ("chdir", unknown [drop "path" [r]]); ("closedir", unknown [drop "dirp" [r]]); @@ -292,6 +296,23 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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 *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); + ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); + ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("mkdtemp", unknown [drop "template" [r; w]]); + ("mkstemp", unknown [drop "template" [r; w]]); + ("regcomp", unknown [drop "preg" [w_deep]; drop "regex" [r]; drop "cflags" []]); + ("regexec", unknown [drop "preg" [r_deep]; drop "string" [r]; drop "nmatch" []; drop "pmatch" [w_deep]; drop "eflags" []]); + ("regfree", unknown [drop "preg" [f_deep]]); + ("ffs", unknown [drop "i" []]); + ("_exit", special [drop "status" []] Abort); + ("execvp", unknown [drop "file" [r]; drop "argv" [r_deep]]); + ("statvfs", unknown [drop "path" [r]; drop "buf" [w]]); + ("readlink", unknown [drop "path" [r]; drop "buf" [w]; drop "bufsz" []]); + ("wcswidth", unknown [drop "s" [r]; drop "n" []]); + ("link", unknown [drop "oldpath" [r]; drop "newpath" [r]]); + ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); + ("posix_fadvise", unknown [drop "fd" []; drop "offset" []; drop "len" []; drop "advice" []]); + ("getppid", unknown []); ] (** Pthread functions. *) @@ -454,6 +475,9 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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 }); + ("rawmemchr", unknown [drop "s" [r]; drop "c" []]); + ("memrchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); + ("memmem", unknown [drop "haystack" [r]; drop "haystacklen" []; drop "needle" [r]; drop "needlelen" [r]]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -470,6 +494,12 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__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" []]); + ("inotify_init1", unknown [drop "flags" []]); + ("inotify_add_watch", unknown [drop "fd" []; drop "pathname" [r]; drop "mask" []]); + ("inotify_rm_watch", unknown [drop "fd" []; drop "wd" []]); + ("fts_open", unknown [drop "path_argv" [r_deep]; drop "options" []; drop "compar" [s]]); (* TODO: use Call instead of Spawn *) + ("fts_read", unknown [drop "ftsp" [r_deep; w_deep]]); + ("fts_close", unknown [drop "ftsp" [f_deep]]); ] let big_kernel_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[big kernel lock]" intType))) @@ -801,10 +831,14 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("wattrset", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []]); ("endwin", unknown []); ("wgetch", unknown [drop "win" [r_deep; w_deep]]); + ("wget_wch", unknown [drop "win" [r_deep; w_deep]; drop "wch" [w]]); + ("unget_wch", unknown [drop "wch" []]); ("wmove", unknown [drop "win" [r_deep; w_deep]; drop "y" []; drop "x" []]); ("waddch", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); + ("waddnstr", unknown [drop "win" [r_deep; w_deep]; drop "str" [r]; drop "n" []]); ("waddnwstr", unknown [drop "win" [r_deep; w_deep]; drop "wstr" [r]; drop "n" []]); ("wattr_on", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []; drop "opts" []]); (* opts argument currently not used *) + ("wattr_off", unknown [drop "win" [r_deep; w_deep]; drop "attrs" []; drop "opts" []]); (* opts argument currently not used *) ("wrefresh", unknown [drop "win" [r_deep; w_deep]]); ("mvprintw", unknown (drop "win" [r_deep; w_deep] :: drop "y" [] :: drop "x" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("initscr", unknown []); @@ -813,10 +847,19 @@ let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("start_color", unknown []); ("use_default_colors", unknown []); ("wclear", unknown [drop "win" [r_deep; w_deep]]); + ("wclrtoeol", unknown [drop "win" [r_deep; w_deep]]); ("can_change_color", unknown []); ("init_color", unknown [drop "color" []; drop "red" []; drop "green" []; drop "blue" []]); ("init_pair", unknown [drop "pair" []; drop "f" [r]; drop "b" [r]]); ("wbkgd", unknown [drop "win" [r_deep; w_deep]; drop "ch" []]); + ("keyname", unknown [drop "c" []]); + ("newterm", unknown [drop "type" [r]; drop "outfd" [r_deep; w_deep]; drop "infd" [r_deep; w_deep]]); + ("cbreak", unknown []); + ("nonl", unknown []); + ("keypad", unknown [drop "win" [r_deep; w_deep]; drop "bf" []]); + ("set_escdelay", unknown [drop "size" []]); + ("printw", unknown (drop "fmt" [r] :: VarArgs (drop' [r]))); + ("werase", unknown [drop "win" [r_deep; w_deep]]); ] let pcre_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From ffc5c6d47b41d3389f203320bca03c55f5c5ef77 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:19:01 +0300 Subject: [PATCH 305/401] Fix return_on_success for some pthread locking functions --- src/analyses/libraryFunctions.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7b92ff8a86..6b024a5770 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -343,16 +343,16 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_mutexattr_destroy", unknown [drop "attr" [f]]); ("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}); - ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true}); + ("pthread_rwlock_rdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = false; return_on_success = false}); + ("pthread_rwlock_tryrdlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = true; write = false; return_on_success = false}); + ("pthread_rwlock_wrlock", special [__ "rwlock" []] @@ fun rwlock -> Lock {lock = rwlock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("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" [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_lock", special [__ "lock" []] @@ fun lock -> Lock {lock = lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = false}); ("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]]); From 2d91b05500729f7fe0124754cc9e7ec6f9a37ce6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:30:51 +0300 Subject: [PATCH 306/401] Make pthread_join invalidate precise --- src/analyses/libraryFunctions.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6b024a5770..b032c7ce86 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -319,6 +319,7 @@ 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_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); ("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); @@ -928,11 +929,6 @@ let classify fn exps: categories = `Unknown fn in match fn with - | "pthread_join" -> - begin match exps with - | [id; ret_var] -> `ThreadJoin (id, ret_var) - | _ -> strange_arguments () - end | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> begin match exps with | size::_ -> `Malloc size From f27a6e7ced33f44ae9f61b37e82f0427b3440cf5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:42:28 +0300 Subject: [PATCH 307/401] Convert remaining old library function classify --- src/analyses/libraryFunctions.ml | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index b032c7ce86..99187f2ccc 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__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); + ("calloc", special [__ "n" []; __ "size" []] @@ fun n size -> Calloc {count = n; size}); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); @@ -434,6 +435,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Malloc size); ] let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -561,6 +563,10 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__cmpxchg_wrong_size", special [] Abort); ("__xadd_wrong_size", special [] Abort); ("__put_user_bad", special [] Abort); + ("kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); + ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); + ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); + ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc iso_packets); (* TODO: iso_packets is size in bytes? *) ] (** Goblint functions. *) @@ -924,27 +930,7 @@ type categories = [ let classify fn exps: categories = - let strange_arguments () = - M.warn ~category:Program "%s arguments are strange!" fn; - `Unknown fn - in - match fn with - | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> - begin match exps with - | size::_ -> `Malloc size - | _ -> strange_arguments () - end - | "kzalloc" -> - begin match exps with - | size::_ -> `Calloc (Cil.one, size) - | _ -> strange_arguments () - end - | "calloc" -> - begin match exps with - | n::size::_ -> `Calloc (n, size) - | _ -> strange_arguments () - end - | x -> `Unknown x + fn module Invalidate = @@ -1075,7 +1061,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) From 4e83af9270b32e17f5c55ed0c7e1f7adffc93e01 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Sep 2023 17:49:30 +0300 Subject: [PATCH 308/401] Remove old library function classify --- src/analyses/libraryDesc.ml | 25 ++--------------------- src/analyses/libraryFunctions.ml | 35 +++++++------------------------- 2 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..5f9f6f90f9 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -126,31 +126,10 @@ type t = { attrs: attr list; (** Attributes of function. *) } -let special_of_old classify_name = fun args -> - match classify_name args with - | `Malloc e -> Malloc e - | `Calloc (count, size) -> Calloc { count; size; } - | `Realloc (ptr, size) -> Realloc { ptr; size; } - | `Lock (try_, write, return_on_success) -> - begin match args with - | [lock] -> Lock { lock ; try_; write; return_on_success; } - | [] -> failwith "lock has no arguments" - | _ -> failwith "lock has multiple arguments" - end - | `Unlock -> - begin match args with - | [arg] -> Unlock arg - | [] -> failwith "unlock has no arguments" - | _ -> failwith "unlock has multiple arguments" - end - | `ThreadCreate (thread, start_routine, arg) -> ThreadCreate { thread; start_routine; arg; } - | `ThreadJoin (thread, ret_var) -> ThreadJoin { thread; ret_var; } - | `Unknown _ -> Unknown - -let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old) (classify_name): t = { +let of_old ?(attrs: attr list=[]) (old_accesses: Accesses.old): t = { attrs; accs = Accesses.of_old old_accesses; - special = special_of_old classify_name; + special = fun _ -> Unknown; } module MathPrintable = struct diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 99187f2ccc..7f5d7fc8d8 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -918,21 +918,6 @@ let activated_library_descs: (string, LibraryDesc.t) Hashtbl.t ResettableLazy.t let reset_lazy () = ResettableLazy.reset activated_library_descs -type categories = [ - | `Malloc of exp - | `Calloc of exp * exp - | `Realloc of exp * exp - | `Lock of bool * bool * bool (* try? * write? * return on success *) - | `Unlock - | `ThreadCreate of exp * exp * exp (* id * f * x *) - | `ThreadJoin of exp * exp (* id * ret_var *) - | `Unknown of string ] - - -let classify fn exps: categories = - fn - - module Invalidate = struct [@@@warning "-unused-value-declaration"] (* some functions are not used below *) @@ -1221,7 +1206,7 @@ let is_safe_uncalled fn_name = List.exists (fun r -> Str.string_match r fn_name 0) kernel_safe_uncalled_regex -let unknown_desc ~f name = (* TODO: remove name argument, unknown function shouldn't have classify *) +let unknown_desc f = let old_accesses (kind: AccessKind.t) args = match kind with | Write when GobConfig.get_bool "sem.unknown_function.invalidate.args" -> args | Write -> [] @@ -1239,16 +1224,10 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul else [] in - let classify_name args = - match classify name args with - | `Unknown _ as category -> - (* TODO: remove hack when all classify are migrated *) - if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then - M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; - category - | category -> category - in - LibraryDesc.of_old ~attrs old_accesses classify_name + (* TODO: remove hack when all classify are migrated *) + if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then + M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; + LibraryDesc.of_old ~attrs old_accesses let find f = let name = f.vname in @@ -1257,9 +1236,9 @@ let find f = | None -> match get_invalidate_action name with | Some old_accesses -> - LibraryDesc.of_old old_accesses (classify name) + LibraryDesc.of_old old_accesses | None -> - unknown_desc ~f name + unknown_desc f let is_special fv = From 0f59ac967b1fd37bbea7c6c4c2a785a136f36c03 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 13:50:10 +0300 Subject: [PATCH 309/401] 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 c5136d1cdf945b9990376476bbd0881ee2f817ad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 14:28:13 +0300 Subject: [PATCH 310/401] Add tests for library function races via NULL --- .../regression/05-lval_ls/20-race-null-void.c | 54 ++++++++++++++++++ .../regression/05-lval_ls/21-race-null-type.c | 55 ++++++++++++++++++ .../05-lval_ls/22-race-null-void-deep.c | 56 +++++++++++++++++++ .../05-lval_ls/23-race-null-type-deep.c | 54 ++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 tests/regression/05-lval_ls/20-race-null-void.c create mode 100644 tests/regression/05-lval_ls/21-race-null-type.c create mode 100644 tests/regression/05-lval_ls/22-race-null-void-deep.c create mode 100644 tests/regression/05-lval_ls/23-race-null-type-deep.c diff --git a/tests/regression/05-lval_ls/20-race-null-void.c b/tests/regression/05-lval_ls/20-race-null-void.c new file mode 100644 index 0000000000..1950ada73e --- /dev/null +++ b/tests/regression/05-lval_ls/20-race-null-void.c @@ -0,0 +1,54 @@ +#include +#include + +void *t_fun(void *arg) { + void **top; + free(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + pthread_join(id, NULL); // NORACE + break; + case 1: + pthread_join(id, 0); // NORACE + break; + case 2: + pthread_join(id, zero); // NORACE + break; + case 3: + pthread_join(id, 1); // RACE + break; + case 4: + pthread_join(id, one); // RACE + break; + case 5: + pthread_join(id, r); // RACE + break; + case 6: + pthread_join(id, null); // NORACE + break; + case 7: + pthread_join(id, unknown); // RACE + break; + case 8: + pthread_join(id, top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/21-race-null-type.c b/tests/regression/05-lval_ls/21-race-null-type.c new file mode 100644 index 0000000000..6b5e6e42fd --- /dev/null +++ b/tests/regression/05-lval_ls/21-race-null-type.c @@ -0,0 +1,55 @@ +// PARAM: --enable ana.race.direct-arithmetic +#include +#include + +void *t_fun(void *arg) { + void *top; + time(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + time(NULL); // NORACE + break; + case 1: + time(0); // NORACE + break; + case 2: + time(zero); // NORACE + break; + case 3: + time(1); // RACE + break; + case 4: + time(one); // RACE + break; + case 5: + time(r); // RACE + break; + case 6: + time(null); // NORACE + break; + case 7: + time(unknown); // RACE + break; + case 8: + time(top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/22-race-null-void-deep.c b/tests/regression/05-lval_ls/22-race-null-void-deep.c new file mode 100644 index 0000000000..7e99f286b6 --- /dev/null +++ b/tests/regression/05-lval_ls/22-race-null-void-deep.c @@ -0,0 +1,56 @@ +#include +#include + +pthread_key_t key; + +void *t_fun(void *arg) { + void *top; + pthread_setspecific(key, top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + pthread_setspecific(key, NULL); // NORACE + break; + case 1: + pthread_setspecific(key, 0); // NORACE + break; + case 2: + pthread_setspecific(key, zero); // NORACE + break; + case 3: + pthread_setspecific(key, 1); // RACE + break; + case 4: + pthread_setspecific(key, one); // RACE + break; + case 5: + pthread_setspecific(key, r); // RACE + break; + case 6: + pthread_setspecific(key, null); // NORACE + break; + case 7: + pthread_setspecific(key, unknown); // RACE + break; + case 8: + pthread_setspecific(key, top); // RACE + break; + default: + break; + } + return 0; +} diff --git a/tests/regression/05-lval_ls/23-race-null-type-deep.c b/tests/regression/05-lval_ls/23-race-null-type-deep.c new file mode 100644 index 0000000000..6f29964d1e --- /dev/null +++ b/tests/regression/05-lval_ls/23-race-null-type-deep.c @@ -0,0 +1,54 @@ +#include +#include + +void *t_fun(void *arg) { + void *top; + fclose(top); // RACE + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + int r; // rand + int zero = 0; // IntDomain zero + void *null; + __goblint_assume(null == NULL); // AddressDomain NULL + int one = 1; // IntDomain one + void *unknown; + __goblint_assume(unknown != NULL); // AddressDomain unknown + void *top; + switch (r) { + case 0: + feof(NULL); // NORACE + break; + case 1: + feof(0); // NORACE + break; + case 2: + feof(zero); // NORACE + break; + case 3: + feof(1); // RACE + break; + case 4: + feof(one); // RACE + break; + case 5: + feof(r); // RACE + break; + case 6: + feof(null); // NORACE + break; + case 7: + feof(unknown); // RACE + break; + case 8: + feof(top); // RACE + break; + default: + break; + } + return 0; +} From a4772d64f1d1532b2783ba56ef3f12cc65ad6e4d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 15:06:26 +0300 Subject: [PATCH 311/401] Add some missing library functions for shairport --- src/analyses/libraryFunctions.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 7f5d7fc8d8..85a0388eba 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -299,6 +299,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("vdprintf", unknown [drop "fd" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mkdtemp", unknown [drop "template" [r; w]]); ("mkstemp", unknown [drop "template" [r; w]]); ("regcomp", unknown [drop "preg" [w_deep]; drop "regex" [r]; drop "cflags" []]); @@ -314,6 +315,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("renameat", unknown [drop "olddirfd" []; drop "oldpath" [r]; drop "newdirfd" []; drop "newpath" [r]]); ("posix_fadvise", unknown [drop "fd" []; drop "offset" []; drop "len" []; drop "advice" []]); ("getppid", unknown []); + ("lockf", unknown [drop "fd" []; drop "cmd" []; drop "len" []]); ] (** Pthread functions. *) @@ -321,6 +323,7 @@ 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_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval}); + ("pthread_kill", unknown [drop "thread" []; drop "sig" []]); ("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); @@ -481,6 +484,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("rawmemchr", unknown [drop "s" [r]; drop "c" []]); ("memrchr", unknown [drop "s" [r]; drop "c" []; drop "n" []]); ("memmem", unknown [drop "haystack" [r]; drop "haystacklen" []; drop "needle" [r]; drop "needlelen" [r]]); + ("getifaddrs", unknown [drop "ifap" [w]]); + ("freeifaddrs", unknown [drop "ifa" [f_deep]]); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 83978e953dbbf7d6a8c920d071b380bbd1441637 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:22:41 +0300 Subject: [PATCH 312/401] Fix ReachableFrom not adding back unknown pointer Regression from f7b38a0. --- src/analyses/base.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..c225da2939 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1287,7 +1287,11 @@ 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 (AD.join) (AD.empty ()) addrs + let addrs' = List.fold_left (AD.join) (AD.empty ()) addrs in + if AD.may_be_unknown a then + AD.add UnknownPtr addrs' (* add unknown back *) + else + addrs' | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From c458db248993021a753772beead0754697f2f73a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:44:44 +0300 Subject: [PATCH 313/401] Add Int cases to MayPointTo and ReachableFrom queries --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c225da2939..4e7235de2b 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1270,6 +1270,7 @@ struct 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 *) + | Int i -> AD.of_int i | _ -> Queries.Result.top q end | Q.EvalThread e -> begin @@ -1292,6 +1293,7 @@ struct AD.add UnknownPtr addrs' (* add unknown back *) else addrs' + | Int i -> AD.of_int i | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From 204594e695c92d106e6e9e628109228f20712339 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 19 Sep 2023 17:45:22 +0300 Subject: [PATCH 314/401] Fix ReachableFrom precision loss with int argument in 68-longjmp/41-poison-rec --- src/analyses/base.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4e7235de2b..0e6ad8f516 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1293,7 +1293,12 @@ struct AD.add UnknownPtr addrs' (* add unknown back *) else addrs' - | Int i -> AD.of_int i + | Int i -> + begin match Cilfacade.typeOf e with + | t when Cil.isPointerType t -> AD.of_int i (* integer used as pointer *) + | _ + | exception Cilfacade.TypeOfError _ -> AD.empty () (* avoid unknown pointer result for non-pointer expression *) + end | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin From d0e9064924f4548d4ac5fc277d5644cbbddcfcf3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 19 Sep 2023 21:49:05 +0200 Subject: [PATCH 315/401] 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 f7851e89bdc9438007ecd4cd4405d9584559ea6d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:05:46 +0300 Subject: [PATCH 316/401] Move memccpy to c_descs_list for C23 --- 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 85a0388eba..ee69e03faf 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -12,9 +12,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin_memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin___memset_chk", special [__ "dest" [w]; __ "ch" []; __ "count" []; drop "os" []] @@ fun dest ch count -> Memset { dest; ch; count; }); - ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); + ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); (* TODO: use n *) ("__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 }); + ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); (* C23 *) (* TODO: use n and c *) ("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 }); @@ -297,7 +298,6 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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 *) ("fnmatch", unknown [drop "pattern" [r]; drop "string" [r]; drop "flags" []]); ("realpath", unknown [drop "path" [r]; drop "resolved_path" [w]]); - ("memccpy", special [__ "dest" [w]; __ "src" [r]; drop "c" []; drop "n" []] @@ fun dest src -> Memcpy {dest; src}); ("dprintf", unknown (drop "fd" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("vdprintf", unknown [drop "fd" []; drop "format" [r]; drop "ap" [r_deep]]); (* TODO: what to do with a va_list type? is r_deep correct? *) ("mkdtemp", unknown [drop "template" [r; w]]); From d1d8d66dd1daeb3fce807cd2ac6cb8bc9b055a1f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:07:37 +0300 Subject: [PATCH 317/401] Use unknown_exp for usb_alloc_urb size --- 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 ee69e03faf..c3ca48da93 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -571,7 +571,7 @@ let linux_kernel_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("__kmalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Malloc size); ("kzalloc", special [__ "size" []; drop "flags" []] @@ fun size -> Calloc {count = Cil.one; size}); - ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc iso_packets); (* TODO: iso_packets is size in bytes? *) + ("usb_alloc_urb", special [__ "iso_packets" []; drop "mem_flags" []] @@ fun iso_packets -> Malloc MyCFG.unknown_exp); ] (** Goblint functions. *) From 71035d2900bd2c714378236456f2f91fc39d0221 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 20 Sep 2023 10:22:52 +0300 Subject: [PATCH 318/401] Add test for atexit soundness --- tests/regression/41-stdlib/07-atexit.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/regression/41-stdlib/07-atexit.c diff --git a/tests/regression/41-stdlib/07-atexit.c b/tests/regression/41-stdlib/07-atexit.c new file mode 100644 index 0000000000..4551400175 --- /dev/null +++ b/tests/regression/41-stdlib/07-atexit.c @@ -0,0 +1,13 @@ +#include +#include + +void bye() +{ + __goblint_check(1); // reachable +} + +int main() +{ + atexit(bye); + return 0; +} From 89e007478b4d2d5b96181f42e946fff6f68d7a1f Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:39:39 +0200 Subject: [PATCH 319/401] Make HeapVar support stack allocation, update IsHeapVar and add IsDynamicallyAlloced --- src/domains/queries.ml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 0388ce2995..5ddf417699 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -99,8 +99,9 @@ type _ t = | IterVars: itervar -> Unit.t t | PathQuery: int * 'a t -> 'a t (** Query only one path under witness lifter. *) | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) - | HeapVar: VI.t t + | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) + | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory, [false] if it represents the result of an alloca() call *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) @@ -152,6 +153,7 @@ struct | MayBePublicWithout _ -> (module MayBool) | MayBeThreadReturn -> (module MayBool) | IsHeapVar _ -> (module MayBool) + | IsDynamicallyAlloced _ -> (module MayBool) | MustBeProtectedBy _ -> (module MustBool) | MustBeAtomic -> (module MustBool) | MustBeSingleThreaded _ -> (module MustBool) @@ -163,7 +165,7 @@ struct | BlobSize _ -> (module ID) | CurrentThreadId -> (module ThreadIdDomain.ThreadLifted) | ThreadCreateIndexedNode -> (module ThreadNodeLattice) - | HeapVar -> (module VI) + | HeapVar _ -> (module VI) | EvalStr _ -> (module SD) | IterPrevVars _ -> (module Unit) | IterVars _ -> (module Unit) @@ -216,6 +218,7 @@ struct | MayBePublicWithout _ -> MayBool.top () | MayBeThreadReturn -> MayBool.top () | IsHeapVar _ -> MayBool.top () + | IsDynamicallyAlloced _ -> MayBool.top () | MutexType _ -> MutexAttrDomain.top () | MustBeProtectedBy _ -> MustBool.top () | MustBeAtomic -> MustBool.top () @@ -228,7 +231,7 @@ struct | BlobSize _ -> ID.top () | CurrentThreadId -> ThreadIdDomain.ThreadLifted.top () | ThreadCreateIndexedNode -> ThreadNodeLattice.top () - | HeapVar -> VI.top () + | HeapVar _ -> VI.top () | EvalStr _ -> SD.top () | IterPrevVars _ -> Unit.top () | IterVars _ -> Unit.top () @@ -288,7 +291,7 @@ struct | Any (PartAccess _) -> 23 | Any (IterPrevVars _) -> 24 | Any (IterVars _) -> 25 - | Any HeapVar -> 29 + | Any (HeapVar _) -> 29 | Any (IsHeapVar _) -> 30 | Any (IsMultiple _) -> 31 | Any (EvalThread _) -> 32 @@ -313,6 +316,7 @@ struct | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 | Any (TmpSpecial _) -> 53 + | Any (IsDynamicallyAlloced _) -> 54 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -347,6 +351,7 @@ struct else compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 + | Any (IsDynamicallyAlloced v1), Any (IsDynamicallyAlloced v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 @@ -387,6 +392,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v + | Any (IsDynamicallyAlloced v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -434,8 +440,9 @@ struct | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" | Any (IterVars i) -> Pretty.dprintf "IterVars _" | Any (PathQuery (i, q)) -> Pretty.dprintf "PathQuery (%d, %a)" i pretty (Any q) - | Any HeapVar -> Pretty.dprintf "HeapVar" + | Any (HeapVar {on_stack = on_stack}) -> Pretty.dprintf "HeapVar %b" on_stack | Any (IsHeapVar v) -> Pretty.dprintf "IsHeapVar %a" CilType.Varinfo.pretty v + | Any (IsDynamicallyAlloced v) -> Pretty.dprintf "IsDynamicallyAlloced %a" CilType.Varinfo.pretty v | Any (IsMultiple v) -> Pretty.dprintf "IsMultiple %a" CilType.Varinfo.pretty v | Any (EvalThread e) -> Pretty.dprintf "EvalThread %a" CilType.Exp.pretty e | Any (EvalJumpBuf e) -> Pretty.dprintf "EvalJumpBuf %a" CilType.Exp.pretty e From 9a06189b7cb843aeb9d645f59c3d08b86d2c8f6c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:41:08 +0200 Subject: [PATCH 320/401] Implement answer for IsDynamicallyAlloced --- src/analyses/wrapperFunctionAnalysis.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index d9bbdb6197..99fac46d6c 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -133,7 +133,7 @@ module MallocWrapper : MCPSpec = struct let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = let wrapper_node, counter = ctx.local in match q with - | Q.HeapVar -> + | Q.HeapVar {on_stack = on_stack} -> let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node | _ -> node_for_ctx ctx @@ -141,9 +141,12 @@ module MallocWrapper : MCPSpec = struct let count = UniqueCallCounter.find (`Lifted node) counter in let var = NodeVarinfoMap.to_varinfo (ctx.ask Q.CurrentThreadId, node, count) in var.vdecl <- UpdateCil.getLoc node; (* TODO: does this do anything bad for incremental? *) + if on_stack then var.vattr <- addAttribute (Attr ("stack_alloca", [])) var.vattr; (* If the call was for stack allocation, add an attr to mark the heap var *) `Lifted var | Q.IsHeapVar v -> NodeVarinfoMap.mem_varinfo v + | Q.IsDynamicallyAlloced v -> + NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with | Some (_, _, c) -> UniqueCount.is_top c || not (ctx.ask Q.MustBeUniqueThread) From d8c59651549d3689a675579c1d9851aeeaa5cf56 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:41:29 +0200 Subject: [PATCH 321/401] Update usage of HeapVar and IsHeapVar --- src/analyses/base.ml | 20 +++++++++++--------- src/analyses/mallocFresh.ml | 2 +- src/analyses/memLeak.ml | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 71e2661997..1212ceb9f4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -150,8 +150,8 @@ struct let longjmp_return = ref dummyFunDec.svar - let heap_var ctx = - let info = match (ctx.ask Q.HeapVar) with + let heap_var on_stack ctx = + let info = match (ctx.ask (Q.HeapVar {on_stack = on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info @@ -1254,7 +1254,7 @@ struct | Address a -> (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || o <> `NoOffset | _ -> false) a then Queries.Result.bot q else ( @@ -1995,7 +1995,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) + | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) || (ctx.ask (Q.IsHeapVar v) && not @@ ctx.ask (Q.IsDynamicallyAlloced v)) | _ -> false) in let has_non_zero_offset = AD.exists (function @@ -2263,13 +2263,14 @@ struct | Unknown, "__goblint_assume_join" -> let id = List.hd args in Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Malloc size, _ -> begin + | Malloc size, fname -> begin match lv with | Some lv -> + let is_stack_alloc = fname = "alloc" || fname = "__builtin_alloca" in let heap_var = if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var ctx)) AD.null_ptr - else AD.of_var (heap_var ctx) + then AD.join (AD.of_var (heap_var is_stack_alloc ctx)) AD.null_ptr + else AD.of_var (heap_var is_stack_alloc ctx) in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); @@ -2279,7 +2280,7 @@ struct | Calloc { count = n; size }, _ -> begin match lv with | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var ctx in + let heap_var = heap_var false ctx in let add_null addr = if get_bool "sem.malloc.fail" then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) @@ -2322,7 +2323,7 @@ struct let p_addr_get = get ask gs st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) let size_int = eval_int ask gs st size in let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var ctx) in + let heap_addr = AD.of_var (heap_var false ctx) in let heap_addr' = if get_bool "sem.malloc.fail" then AD.join heap_addr AD.null_ptr @@ -2563,6 +2564,7 @@ struct | MayBeThreadReturn | PartAccess _ | IsHeapVar _ + | IsDynamicallyAlloced _ | IsMultiple _ | CreatedThreads | MustJoinedThreads -> diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 2c2b99a075..2eed772527 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -43,7 +43,7 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - begin match ctx.ask HeapVar with + begin match ctx.ask (HeapVar {on_stack = false}) with | `Lifted var -> D.add var ctx.local | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 1bff7611a4..5b38ab79eb 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -44,7 +44,7 @@ struct | 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 + begin match ctx.ask (Queries.HeapVar {on_stack = false}) with | `Lifted var -> D.add var state | _ -> state end @@ -53,7 +53,7 @@ struct | 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 *) 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 *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state From 954be03ad32fcb685d2e04f50849df537b6c38f9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 20 Sep 2023 18:55:54 +0200 Subject: [PATCH 322/401] Add "alloca" as a library function --- src/analyses/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..326dedf434 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__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); + ("alloca", special [__ "size" []] @@ fun size -> Malloc size); (* TODO: Maybe define a new special type [Alloca], just like [Malloc]? *) ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); From aa97af86c23911bd05822149a7d9c9fc4f819ae2 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:38:24 +0200 Subject: [PATCH 323/401] Add Alloca special function type --- src/analyses/base.ml | 16 ++++++++++++---- src/analyses/libraryDesc.ml | 1 + src/analyses/libraryFunctions.ml | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1212ceb9f4..2138a51d41 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2263,14 +2263,22 @@ struct | Unknown, "__goblint_assume_join" -> let id = List.hd args in Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st - | Malloc size, fname -> begin + | Alloca size, _ -> begin + match lv with + | Some lv -> + let heap_var = AD.of_var (heap_var true ctx) in + (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) + set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); + (eval_lv (Analyses.ask_of_ctx ctx) gs st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + | _ -> st + end + | Malloc size, _ -> begin match lv with | Some lv -> - let is_stack_alloc = fname = "alloc" || fname = "__builtin_alloca" in let heap_var = if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var is_stack_alloc ctx)) AD.null_ptr - else AD.of_var (heap_var is_stack_alloc ctx) + then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr + else AD.of_var (heap_var false ctx) in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *) set_many ~ctx (Analyses.ask_of_ctx ctx) gs st [(heap_var, TVoid [], Blob (VD.bot (), eval_int (Analyses.ask_of_ctx ctx) gs st size, true)); diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..7cf68aa561 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -43,6 +43,7 @@ type math = (** Type of special function, or {!Unknown}. *) (* Use inline record if not single {!Cil.exp} argument. *) type special = + | Alloca of Cil.exp | Malloc of Cil.exp | Calloc of { count: Cil.exp; size: Cil.exp; } | Realloc of { ptr: Cil.exp; size: Cil.exp; } diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 326dedf434..0cc27f3523 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -63,8 +63,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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; }); + ("alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); - ("alloca", special [__ "size" []] @@ fun size -> Malloc size); (* TODO: Maybe define a new special type [Alloca], just like [Malloc]? *) ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); ("abort", special [] Abort); @@ -1037,6 +1037,7 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) + "alloca", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) From 6e9bb2f121114a9c91bf1bb19557bbcfd0d3ffca Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:39:42 +0200 Subject: [PATCH 324/401] Classify __builtin_alloca as Alloca and not as Malloc --- src/analyses/libraryFunctions.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 0cc27f3523..224b54907f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,6 +64,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__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; }); ("alloca", special [__ "size" []] @@ fun size -> Alloca size); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); ("free", special [__ "ptr" [f]] @@ fun ptr -> Free ptr); @@ -891,7 +892,7 @@ let classify fn exps: categories = | [id; ret_var] -> `ThreadJoin (id, ret_var) | _ -> strange_arguments () end - | "kmalloc" | "__kmalloc" | "usb_alloc_urb" | "__builtin_alloca" -> + | "kmalloc" | "__kmalloc" | "usb_alloc_urb" -> begin match exps with | size::_ -> `Malloc size | _ -> strange_arguments () From d9df607e99ea78600b81f879955208ec00391bcb Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 09:49:48 +0200 Subject: [PATCH 325/401] Implement and use IsHeapVar and IsDynamicallyAlloced right --- src/analyses/base.ml | 8 ++++---- src/analyses/memLeak.ml | 2 +- src/analyses/useAfterFree.ml | 4 ++-- src/analyses/wrapperFunctionAnalysis.ml | 4 ++-- src/domains/queries.ml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2138a51d41..7ee49603e9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1254,7 +1254,7 @@ struct | Address a -> (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsHeapVar v)) || (ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset | _ -> false) a then Queries.Result.bot q else ( @@ -1383,7 +1383,7 @@ struct let t = match t_override with | Some t -> t | None -> - if a.f (Q.IsHeapVar x) then + if a.f (Q.IsDynamicallyAlloced x) then (* the vtype of heap vars will be TVoid, so we need to trust the pointer we got to this to be of the right type *) (* i.e. use the static type of the pointer here *) lval_type @@ -1429,7 +1429,7 @@ struct (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. Allocated blocks are representend by Blobs with additional information, so they need to be looked-up. *) - let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsHeapVar x)) && offs = `NoOffset then begin + let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsDynamicallyAlloced x)) && offs = `NoOffset then begin VD.bot_value ~varAttr:x.vattr lval_type end else Priv.read_global a priv_getg st x @@ -1995,7 +1995,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsHeapVar v)) || (ctx.ask (Q.IsHeapVar v) && not @@ ctx.ask (Q.IsDynamicallyAlloced v)) + | Addr (v,_) -> not (ctx.ask (Q.IsDynamicallyAlloced v)) || (ctx.ask (Q.IsDynamicallyAlloced v) && not @@ ctx.ask (Q.IsHeapVar v)) | _ -> false) in let has_non_zero_offset = AD.exists (function diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 5b38ab79eb..abf0deb954 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -53,7 +53,7 @@ struct | 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 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) && ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 0aafbd1ad4..8c70322553 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -91,7 +91,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) -> v :: vars | _ -> vars ) ad [] in @@ -185,7 +185,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsHeapVar var) -> D.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) -> D.add var state | _ -> state ) ad (D.empty ()) in diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 99fac46d6c..43d472d0e3 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -144,9 +144,9 @@ module MallocWrapper : MCPSpec = struct if on_stack then var.vattr <- addAttribute (Attr ("stack_alloca", [])) var.vattr; (* If the call was for stack allocation, add an attr to mark the heap var *) `Lifted var | Q.IsHeapVar v -> - NodeVarinfoMap.mem_varinfo v - | Q.IsDynamicallyAlloced v -> NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr + | Q.IsDynamicallyAlloced v -> + NodeVarinfoMap.mem_varinfo v | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with | Some (_, _, c) -> UniqueCount.is_top c || not (ctx.ask Q.MustBeUniqueThread) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 5ddf417699..a5d1c727ab 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,7 +101,7 @@ type _ t = | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) - | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory, [false] if it represents the result of an alloca() call *) + | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) From 4c84a102c2093066b28032cd0dc0cd4a653034c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 21 Sep 2023 12:50:44 +0200 Subject: [PATCH 326/401] CI run fix: remove alloca from invalidate_actions --- src/analyses/libraryFunctions.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 224b54907f..800d3e4902 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1038,7 +1038,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "alloca", readsAll;(*safe*) "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) 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 327/401] 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 328/401] 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 1947aaeb407353d1c3c8bd111e9d60ee41c90a16 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 11:00:53 +0300 Subject: [PATCH 329/401] Add test for pthread locking function return values --- .../04-mutex/58-pthread-lock-return.c | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/regression/04-mutex/58-pthread-lock-return.c diff --git a/tests/regression/04-mutex/58-pthread-lock-return.c b/tests/regression/04-mutex/58-pthread-lock-return.c new file mode 100644 index 0000000000..0ae96b60ae --- /dev/null +++ b/tests/regression/04-mutex/58-pthread-lock-return.c @@ -0,0 +1,118 @@ +// PARAM: --disable sem.lock.fail +#include + +int g_mutex = 0; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +int g_rwlock = 0; +pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +// OS X has no spinlock +#ifndef __APPLE__ +int g_spin = 0; +pthread_spinlock_t spin; +#endif + +void *t_fun(void *arg) { + if (!pthread_mutex_lock(&mutex)) { + __goblint_check(1); // reachable + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_mutex_trylock(&mutex)) { + __goblint_check(1); // reachable + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + + if (!pthread_rwlock_wrlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_rwlock_trywrlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + + if (!pthread_rwlock_rdlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_rwlock_tryrdlock(&mutex)) { + __goblint_check(1); // reachable + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + } + else { + __goblint_check(1); // reachable + } + +#ifndef __APPLE__ + if (!pthread_spin_lock(&spin)) { + __goblint_check(1); // reachable + g_spin++; // NORACE + pthread_spin_unlock(&spin); + } + else { + __goblint_check(0); // NOWARN (unreachable) + } + + if (!pthread_spin_trylock(&spin)) { + __goblint_check(1); // reachable + g_spin++; // NORACE + pthread_spin_unlock(&spin); + } + else { + __goblint_check(1); // reachable + } +#endif + + return NULL; +} + +int main() { +#ifndef __APPLE__ + pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE); +#endif + + pthread_t id; + pthread_create(&id, NULL, &t_fun, NULL); + + pthread_mutex_lock(&mutex); + g_mutex++; // NORACE + pthread_mutex_unlock(&mutex); + + pthread_rwlock_wrlock(&mutex); + g_rwlock++; // NORACE + pthread_rwlock_unlock(&mutex); + +#ifndef __APPLE__ + pthread_spin_lock(&spin); + g_spin++; // NORACE + pthread_spin_unlock(&spin); +#endif + + pthread_join(id, NULL); + return 0; +} From 4bc0303cbccf0de5772e12ee4be2f6d5021ed470 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 11:26:59 +0300 Subject: [PATCH 330/401] Add atexit test with spawn disabled --- tests/regression/41-stdlib/08-atexit-no-spawn.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/regression/41-stdlib/08-atexit-no-spawn.c diff --git a/tests/regression/41-stdlib/08-atexit-no-spawn.c b/tests/regression/41-stdlib/08-atexit-no-spawn.c new file mode 100644 index 0000000000..7f25f42183 --- /dev/null +++ b/tests/regression/41-stdlib/08-atexit-no-spawn.c @@ -0,0 +1,14 @@ +// PARAM: --disable sem.unknown_function.spawn +#include +#include + +void bye() +{ + __goblint_check(0); // NOWARN (unreachable) +} + +int main() +{ + atexit(bye); + return 0; +} From 16077e3b146d464072004eaf0cd1480a2a7b99d1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 13:27:33 +0300 Subject: [PATCH 331/401] 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 332/401] 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 333/401] 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 334/401] 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) From 0caab0f1cbc006508dcd197d2687cf172659107e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:01:50 +0300 Subject: [PATCH 335/401] Add final messages (issue #1190) --- src/util/messages.ml | 24 ++++++++++++++++++++++-- src/util/server.ml | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index a06a183eee..6cd9027739 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -248,12 +248,21 @@ let add m = Table.add m ) +let final_table: unit Table.MH.t = Table.MH.create 13 + +let add_final m = + Table.MH.replace final_table m () + let finalize () = if get_bool "warn.deterministic" then ( !Table.messages_list |> List.sort Message.compare |> List.iter print - ) + ); + Table.MH.iter (fun m () -> + print m; + Table.add m + ) final_table (* TODO: also deterministic case *) let current_context: ControlSpecC.t option ref = ref None @@ -282,7 +291,7 @@ let msg_noloc severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn && Severity.should_warn severity && (Category.should_warn category || Tags.should_warn tags) then ( let finish doc = let text = GobPretty.show doc in - add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} + add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) in Pretty.gprintf finish fmt ) @@ -316,4 +325,15 @@ let debug_noloc ?tags = msg_noloc Debug ?tags let success ?loc = msg Success ?loc let success_noloc ?tags = msg_noloc Success ?tags +let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = + if !AnalysisState.should_warn then ( + let finish doc = + let text = GobPretty.show doc in + add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + in + Pretty.gprintf finish fmt + ) + else + GobPretty.igprintf () fmt + include Tracing diff --git a/src/util/server.ml b/src/util/server.ml index e133fb96c3..22f5a03350 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -264,6 +264,7 @@ let node_locator: Locator.t ResettableLazy.t = let analyze ?(reset=false) (s: t) = Messages.Table.(MH.clear messages_table); + Messages.(Table.MH.clear final_table); Messages.Table.messages_list := []; let file, reparsed = reparse s in if reset then ( From 857b6c1654ec37220ded09f0f1e79b341574e243 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:02:06 +0300 Subject: [PATCH 336/401] Add fixpoint not reached final message --- src/solvers/postSolver.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/solvers/postSolver.ml b/src/solvers/postSolver.ml index 346ce312b1..f96ca832a1 100644 --- a/src/solvers/postSolver.ml +++ b/src/solvers/postSolver.ml @@ -82,10 +82,13 @@ module Verify: F = let complain_constraint x ~lhs ~rhs = AnalysisState.verified := Some false; + M.msg_final Error ~category:Unsound "Fixpoint not reached"; ignore (Pretty.printf "Fixpoint not reached at %a\n @[Solver computed:\n%a\nRight-Hand-Side:\n%a\nDifference: %a\n@]" S.Var.pretty_trace x S.Dom.pretty lhs S.Dom.pretty rhs S.Dom.pretty_diff (rhs, lhs)) let complain_side x y ~lhs ~rhs = AnalysisState.verified := Some false; + + M.msg_final Error ~category:Unsound "Fixpoint not reached"; ignore (Pretty.printf "Fixpoint not reached at %a\nOrigin: %a\n @[Solver computed:\n%a\nSide-effect:\n%a\nDifference: %a\n@]" S.Var.pretty_trace y S.Var.pretty_trace x S.Dom.pretty lhs S.Dom.pretty rhs S.Dom.pretty_diff (rhs, lhs)) let one_side ~vh ~x ~y ~d = From f2d2c1c498749e86f43c8ae6e80342290617c2a9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:04:56 +0300 Subject: [PATCH 337/401] Add function definition missing final message --- src/analyses/libraryFunctions.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..b12da8230b 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1219,8 +1219,10 @@ let unknown_desc ~f name = (* TODO: remove name argument, unknown function shoul match classify name args with | `Unknown _ as category -> (* TODO: remove hack when all classify are migrated *) - if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then - M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname; + if not (CilType.Varinfo.equal f dummyFunDec.svar) && not (use_special f.vname) then ( + M.msg_final Error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing"; + M.error ~category:Imprecise ~tags:[Category Unsound] "Function definition missing for %s" f.vname + ); category | category -> category in From 2430e6e53d1983230c0d7f6d3849a8a54b815ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:38 +0300 Subject: [PATCH 338/401] Add both branches dead final message --- src/framework/constraints.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 288b7ad0ea..ff2a08263b 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1390,6 +1390,7 @@ struct let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) | `Top -> (* may be both true and false *) From 50609787821b53611716cdf705e3b594e58e7d18 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:47 +0300 Subject: [PATCH 339/401] Add ASM final message --- src/framework/analyses.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index df3346af93..bb2170509d 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -643,7 +643,8 @@ struct let vdecl ctx _ = ctx.local let asm x = - ignore (M.info ~category:Unsound "ASM statement ignored."); + M.msg_final Info ~category:Unsound "ASM ignored"; + M.info ~category:Unsound "ASM statement ignored."; x.local (* Just ignore. *) let skip x = x.local (* Just ignore. *) From a28a8ffc58287e087b6fab6322247a16e6fc79f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 14:29:58 +0300 Subject: [PATCH 340/401] Add no suitable function final message --- src/framework/constraints.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ff2a08263b..4efb0d5346 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -785,6 +785,7 @@ struct in let funs = List.filter_map one_function functions in if [] = funs then begin + M.msg_final Warning ~category:Unsound ~tags:[Category Call] "No suitable function to 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 From 684a74cd39ab8fd4e1a8514beec44cd879a88fe9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Sep 2023 15:20:34 +0300 Subject: [PATCH 341/401] Update cram tests with final messages --- tests/regression/04-mutex/49-type-invariants.t | 2 ++ tests/regression/04-mutex/77-type-nested-fields.t | 1 + tests/regression/04-mutex/79-type-nested-fields-deep1.t | 1 + tests/regression/04-mutex/80-type-nested-fields-deep2.t | 1 + tests/regression/04-mutex/90-distribute-fields-type-1.t | 1 + tests/regression/04-mutex/91-distribute-fields-type-2.t | 1 + tests/regression/04-mutex/92-distribute-fields-type-deep.t | 1 + tests/regression/04-mutex/93-distribute-fields-type-global.t | 1 + tests/regression/06-symbeq/16-type_rc.t | 2 ++ tests/regression/06-symbeq/21-mult_accs_rc.t | 2 ++ 10 files changed, 13 insertions(+) diff --git a/tests/regression/04-mutex/49-type-invariants.t b/tests/regression/04-mutex/49-type-invariants.t index 3ddd8f237d..4c105d1559 100644 --- a/tests/regression/04-mutex/49-type-invariants.t +++ b/tests/regression/04-mutex/49-type-invariants.t @@ -21,6 +21,7 @@ [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) + [Error][Imprecise][Unsound] Function definition missing $ 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:21:3-21:21) @@ -45,3 +46,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/04-mutex/77-type-nested-fields.t b/tests/regression/04-mutex/77-type-nested-fields.t index 738784f5d5..bb935cb0ed 100644 --- a/tests/regression/04-mutex/77-type-nested-fields.t +++ b/tests/regression/04-mutex/77-type-nested-fields.t @@ -27,3 +27,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 2e15f83c39..ba1399d225 100644 --- a/tests/regression/04-mutex/79-type-nested-fields-deep1.t +++ b/tests/regression/04-mutex/79-type-nested-fields-deep1.t @@ -27,3 +27,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 48e726f623..71bdcfb2e2 100644 --- a/tests/regression/04-mutex/80-type-nested-fields-deep2.t +++ b/tests/regression/04-mutex/80-type-nested-fields-deep2.t @@ -27,3 +27,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 ba3e3da0ed..46435045b9 100644 --- a/tests/regression/04-mutex/90-distribute-fields-type-1.t +++ b/tests/regression/04-mutex/90-distribute-fields-type-1.t @@ -29,3 +29,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 fd544b0b0b..c7e66c0527 100644 --- a/tests/regression/04-mutex/91-distribute-fields-type-2.t +++ b/tests/regression/04-mutex/91-distribute-fields-type-2.t @@ -29,3 +29,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 aefc1520d1..4fc1c7e101 100644 --- a/tests/regression/04-mutex/92-distribute-fields-type-deep.t +++ b/tests/regression/04-mutex/92-distribute-fields-type-deep.t @@ -29,3 +29,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing 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 2199c689b1..bf34d99936 100644 --- a/tests/regression/04-mutex/93-distribute-fields-type-global.t +++ b/tests/regression/04-mutex/93-distribute-fields-type-global.t @@ -23,3 +23,4 @@ [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) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/06-symbeq/16-type_rc.t b/tests/regression/06-symbeq/16-type_rc.t index 06a3b314a4..b63471a45e 100644 --- a/tests/regression/06-symbeq/16-type_rc.t +++ b/tests/regression/06-symbeq/16-type_rc.t @@ -10,6 +10,7 @@ Disable info messages because race summary contains (safe) memory location count 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) + [Error][Imprecise][Unsound] Function definition missing $ 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:21:3-21:15) @@ -20,3 +21,4 @@ Disable info messages because race summary contains (safe) memory location count 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) + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 9b02dcde76..227c66058e 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -15,6 +15,7 @@ Disable info messages because race summary contains (safe) memory location count [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) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:27:3-27:14) + [Error][Imprecise][Unsound] Function definition missing $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 21-mult_accs_rc.c [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) @@ -30,3 +31,4 @@ Disable info messages because race summary contains (safe) memory location count [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) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:27:3-27:14) + [Error][Imprecise][Unsound] Function definition missing From f06a0f5c7592b750c3ddb471d680b19a9634684c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Tue, 26 Sep 2023 16:49:10 +0200 Subject: [PATCH 342/401] Add check for bot and top address offsets --- src/analyses/memOutOfBounds.ml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 97907e9d6f..4b39594bb8 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -177,12 +177,7 @@ struct | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - | false -> - (* - Offset should be the same for all elements in the points-to set. - Hence, we can just pick one element and obtain its offset - *) - let (_, o) = VDQ.LS.choose a in + | false -> let rec to_int_dom_offs = function | `NoOffset -> `NoOffset | `Field (f, o) -> `Field (f, to_int_dom_offs o) @@ -194,6 +189,20 @@ struct in `Index (exp_as_int_dom, to_int_dom_offs o) in + let () = + if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t (to_int_dom_offs o)) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr + ) else if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_top @@ offs_to_idx t (to_int_dom_offs o)) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr + ) + in + (* Offset should be the same for all elements in the points-to set *) + (* Hence, we can just pick one element and obtain its offset *) + let (_, o) = VDQ.LS.choose a in offs_to_idx t (to_int_dom_offs o) end | None -> From eb97eca09e20c94924c2235d0ecd1a7cad12b626 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 11:26:18 +0300 Subject: [PATCH 343/401] Remove TODO annotations from passing checks --- tests/regression/01-cpa/33-asserts.c | 8 ++++---- tests/regression/28-race_reach/22-deref_read_racefree.c | 2 +- tests/regression/66-interval-set-one/51-widen-sides.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/regression/01-cpa/33-asserts.c b/tests/regression/01-cpa/33-asserts.c index f8bf6c3132..26efad44fc 100644 --- a/tests/regression/01-cpa/33-asserts.c +++ b/tests/regression/01-cpa/33-asserts.c @@ -26,14 +26,14 @@ int main(){ check(j==6); // assert UNKNOWN unknown(&k); - assume(k==4); // TODO? assert SUCCESS + assume(k==4); check(k==4); // assert SUCCESS unknown(&k); - assume(k+1==n); // TODO? FAIL + assume(k+1==n); - assume(n==5); // TODO? NOWARN - assert(0); // NOWARN + assume(n==5); // contradiction + assert(0); // NOWARN (unreachable) return 0; } \ No newline at end of file diff --git a/tests/regression/28-race_reach/22-deref_read_racefree.c b/tests/regression/28-race_reach/22-deref_read_racefree.c index 3386277083..2e4c5ebbb6 100644 --- a/tests/regression/28-race_reach/22-deref_read_racefree.c +++ b/tests/regression/28-race_reach/22-deref_read_racefree.c @@ -17,7 +17,7 @@ int main() { create_threads(t); q = p; pthread_mutex_lock(&mutex); - assert_racefree(*q); // TODO + assert_racefree(*q); pthread_mutex_unlock(&mutex); return 0; } diff --git a/tests/regression/66-interval-set-one/51-widen-sides.c b/tests/regression/66-interval-set-one/51-widen-sides.c index 72eb1396b1..b086baf026 100644 --- a/tests/regression/66-interval-set-one/51-widen-sides.c +++ b/tests/regression/66-interval-set-one/51-widen-sides.c @@ -3,13 +3,13 @@ int further(int n) { // Even sides-local can not save us here :( - __goblint_check(n <= 1); //TODO + __goblint_check(n <= 2); //TODO } int fun(int n, const char* arg) { // Fails with solvers.td3.side_widen sides, needs sides-local - __goblint_check(n <= 1); + __goblint_check(n <= 2); further(n); } @@ -26,5 +26,5 @@ int main() { doIt("two"); // In the setting with solvers.td3.side_widen sides, widening happens and the bound is lost - fun(1, "org"); + fun(2, "org"); } From a2bea7b3081b9c7ebe92d91dc2cf21283ece3ee8 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 27 Sep 2023 14:45:21 +0200 Subject: [PATCH 344/401] Fix issues after merge with master --- src/analyses/base.ml | 7 ++- src/analyses/memOutOfBounds.ml | 86 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c1047f9c6a..8b76a1cc03 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1261,10 +1261,9 @@ struct else ( (* If we need the BlobSize from the base address, then remove any offsets *) let a = - if from_base_addr then - ValueDomainQueries.LS.elements s - |> List.map (fun (v, _) -> Addr.of_var v) - |> AD.of_list + if from_base_addr then AD.map (function + | Addr (v, o) -> Addr (v, `NoOffset) + | addr -> addr) a else a in diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 4b39594bb8..94c16e9c94 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -92,8 +92,11 @@ struct let points_to_heap_only ctx ptr = 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.for_all (fun (v, _) -> ctx.ask (Queries.IsHeapVar v)) a + | a when not (Queries.AD.is_top a)-> + Queries.AD.for_all (function + | Addr (v, o) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a | _ -> false let get_size_of_ptr_target ctx ptr = @@ -102,21 +105,25 @@ struct ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) else match ctx.ask (Queries.MayPointTo ptr) with - | a when not (Queries.LS.is_top a) -> - let pts_list = Queries.LS.elements a in - let pts_elems_to_sizes (v, _) = - begin match v.vtype with - | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in - begin match ctx.ask (Queries.EvalLength ptr) with - | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) - | `Bot -> VDQ.ID.bot () - | `Top -> VDQ.ID.top () + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength ptr) with + | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) + | `Bot -> VDQ.ID.bot () + | `Top -> VDQ.ID.top () + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) end - | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) + | _ -> VDQ.ID.top () end in (* Map each points-to-set element to its size *) @@ -169,41 +176,36 @@ struct let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with - | a when not (VDQ.LS.is_top a) -> + | a when not (VDQ.AD.is_top a) -> let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in begin match ptr_deref_type with | Some t -> - begin match VDQ.LS.is_empty a with + begin match VDQ.AD.is_empty a with | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () | false -> - let rec to_int_dom_offs = function - | `NoOffset -> `NoOffset - | `Field (f, o) -> `Field (f, to_int_dom_offs o) - | `Index (i, o) -> - let exp_as_int_dom = match ctx.ask (Queries.EvalInt i) with - | `Lifted i -> i - | `Bot -> IntDomain.IntDomTuple.bot_of @@ Cilfacade.ptrdiff_ikind () - | `Top -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () - in - `Index (exp_as_int_dom, to_int_dom_offs o) - in - let () = - if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t (to_int_dom_offs o)) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) - M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr - ) else if VDQ.LS.exists (fun (_, o) -> IntDomain.IntDomTuple.is_top @@ offs_to_idx t (to_int_dom_offs o)) a then ( - (* TODO: Uncomment once staging-memsafety branch changes are applied *) - (* set_mem_safety_flag InvalidDeref; *) - M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr - ) - in + if VDQ.AD.exists (function + | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | _ -> false + ) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr + ) else if VDQ.AD.exists (function + | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | _ -> false + ) a then ( + (* TODO: Uncomment once staging-memsafety branch changes are applied *) + (* set_mem_safety_flag InvalidDeref; *) + M.warn "Pointer %a has a top address offset. An invalid memory access may occur" d_exp ptr + ); (* Offset should be the same for all elements in the points-to set *) (* Hence, we can just pick one element and obtain its offset *) - let (_, o) = VDQ.LS.choose a in - offs_to_idx t (to_int_dom_offs o) + begin match VDQ.AD.choose a with + | Addr (_, o) -> offs_to_idx t o + | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + end end | None -> M.error "Expression %a doesn't have pointer type" d_exp ptr; From f0c73c53fa4b88ddeb0484a01b05aa142a07dcf9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Wed, 27 Sep 2023 15:00:56 +0200 Subject: [PATCH 345/401] Add support for 3rd param of memcpy --- src/analyses/libraryDesc.ml | 2 +- src/analyses/libraryFunctions.ml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/analyses/libraryDesc.ml b/src/analyses/libraryDesc.ml index 94de4fbf82..2ff1169db8 100644 --- a/src/analyses/libraryDesc.ml +++ b/src/analyses/libraryDesc.ml @@ -62,7 +62,7 @@ type special = | Math of { fun_args: math; } | Memset of { dest: Cil.exp; ch: Cil.exp; count: Cil.exp; } | Bzero of { dest: Cil.exp; count: Cil.exp; } - | Memcpy of { dest: Cil.exp; src: Cil.exp } + | Memcpy of { dest: Cil.exp; src: Cil.exp; n: Cil.exp; } | Strcpy of { dest: Cil.exp; src: Cil.exp; n: Cil.exp option; } | Strcat of { dest: Cil.exp; src: Cil.exp; n: Cil.exp option; } | Strlen of Cil.exp diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 137a3103a5..575c036a23 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -12,12 +12,12 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin_memset", special [__ "dest" [w]; __ "ch" []; __ "count" []] @@ fun dest ch count -> Memset { dest; ch; count; }); ("__builtin___memset_chk", special [__ "dest" [w]; __ "ch" []; __ "count" []; drop "os" []] @@ fun dest ch count -> Memset { dest; ch; count; }); - ("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 }); - ("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 }); + ("memcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("memmove", special [__ "dest" [w]; __ "src" [r]; __ "count" []] @@ fun dest src count -> Memcpy { dest; src; n = count; }); + ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; __ "count" []] @@ fun dest src count -> Memcpy { dest; src; n = count; }); + ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; __ "count" []; drop "os" []] @@ fun dest src count -> Memcpy { dest; src; n = 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; }); @@ -452,8 +452,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 }); + ("mempcpy", special [__ "dest" [w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Memcpy { dest; src; n; }); + ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Memcpy { dest; src; n; }); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 7f9ec9a4ef10ee1221b62cd7b99d867b668a0204 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 16:03:49 +0300 Subject: [PATCH 346/401] Fix pthread-lock-return test on OSX --- tests/regression/04-mutex/58-pthread-lock-return.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/04-mutex/58-pthread-lock-return.c b/tests/regression/04-mutex/58-pthread-lock-return.c index 0ae96b60ae..3e2a05c94e 100644 --- a/tests/regression/04-mutex/58-pthread-lock-return.c +++ b/tests/regression/04-mutex/58-pthread-lock-return.c @@ -70,7 +70,7 @@ void *t_fun(void *arg) { #ifndef __APPLE__ if (!pthread_spin_lock(&spin)) { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) g_spin++; // NORACE pthread_spin_unlock(&spin); } @@ -79,12 +79,12 @@ void *t_fun(void *arg) { } if (!pthread_spin_trylock(&spin)) { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) g_spin++; // NORACE pthread_spin_unlock(&spin); } else { - __goblint_check(1); // reachable + __goblint_check(1); // TODO reachable (TODO for OSX) } #endif From eb674b70ec61db2bfd8738959e219a9cf9aa2457 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 28 Sep 2023 10:42:07 +0200 Subject: [PATCH 347/401] Add MemOOB to `goblint_lib.ml` (PR #1094) --- src/goblint_lib.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 625e81f18b..6e700485dd 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -85,6 +85,7 @@ module MallocFresh = MallocFresh module Malloc_null = Malloc_null module MemLeak = MemLeak module UseAfterFree = UseAfterFree +module MemOutOfBounds = MemOutOfBounds (** {2 Concurrency} From dd2a70bf0d3bbeee9b53403b7fbd157ec8eafa8e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Sep 2023 16:04:10 +0300 Subject: [PATCH 348/401] Add reachability timing --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 45c4ee2e9b..c4fdd633d3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -570,6 +570,8 @@ struct if M.tracing then M.traceu "reachability" "All reachable vars: %a\n" AD.pretty !visited; List.map AD.singleton (AD.elements !visited) + let reachable_vars ask args gs st = Timing.wrap "reachability" (reachable_vars ask args gs) st + let drop_non_ptrs (st:CPA.t) : CPA.t = if CPA.is_top st then st else let rec replace_val = function From 75f15808590ae9e595bc3857ac353cd0cf595a57 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 11:54:22 +0200 Subject: [PATCH 349/401] Improve double free check --- src/analyses/useAfterFree.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index ba2c49f012..1b1e2f008d 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -216,7 +216,7 @@ struct List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args in Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; - List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(f.vname = "free") ("special: " ^ f.vname) ctx arg) arglist; + List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(match desc.special arglist with Free _ -> true | _ -> false) ("special: " ^ f.vname) ctx arg) arglist; match desc.special arglist with | Free ptr -> begin match ctx.ask (Queries.MayPointTo ptr) with From c19e4c097e474d55f3f03884e08171556feb8cab Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 12:01:27 +0200 Subject: [PATCH 350/401] Fix blobsize query compare --- src/domains/queries.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index eedbb3ac34..1a7ec2276d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -337,7 +337,12 @@ struct | Any (EvalLength e1), Any (EvalLength e2) -> CilType.Exp.compare e1 e2 | Any (EvalMutexAttr e1), Any (EvalMutexAttr e2) -> CilType.Exp.compare e1 e2 | Any (EvalValue e1), Any (EvalValue e2) -> CilType.Exp.compare e1 e2 - | Any (BlobSize {exp = e1; _}), Any (BlobSize {exp = e2; _}) -> CilType.Exp.compare e1 e2 + | Any (BlobSize {exp = e1; base_address = b1}), Any (BlobSize {exp = e2; base_address = b2}) -> + let r = CilType.Exp.compare e1 e2 in + if r <> 0 then + r + else + Stdlib.compare b1 b2 | Any (CondVars e1), Any (CondVars e2) -> CilType.Exp.compare e1 e2 | Any (PartAccess p1), Any (PartAccess p2) -> compare_access p1 p2 | Any (IterPrevVars ip1), Any (IterPrevVars ip2) -> compare_iterprevvar ip1 ip2 From e780a83125a973e7b4df4d17e372a5a692e4bd37 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Sep 2023 15:41:25 +0300 Subject: [PATCH 351/401] Print final messages deterministically --- src/util/messages.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index 6cd9027739..9cc08b6faf 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -259,10 +259,13 @@ let finalize () = |> List.sort Message.compare |> List.iter print ); - Table.MH.iter (fun m () -> + Table.MH.to_seq_keys final_table + |> List.of_seq + |> List.sort Message.compare + |> List.iter (fun m -> print m; Table.add m - ) final_table (* TODO: also deterministic case *) + ) let current_context: ControlSpecC.t option ref = ref None From 3c7555bedfdb4fd041e1a5a6d5cf83c8b2c5247a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Sep 2023 16:41:42 +0300 Subject: [PATCH 352/401] Remove context from noloc and final messages --- src/util/messages.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/messages.ml b/src/util/messages.ml index 9cc08b6faf..42a3118978 100644 --- a/src/util/messages.ml +++ b/src/util/messages.ml @@ -294,7 +294,7 @@ let msg_noloc severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn && Severity.should_warn severity && (Category.should_warn category || Tags.should_warn tags) then ( let finish doc = let text = GobPretty.show doc in - add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + add {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = None}} in Pretty.gprintf finish fmt ) @@ -332,7 +332,7 @@ let msg_final severity ?(tags=[]) ?(category=Category.Unknown) fmt = if !AnalysisState.should_warn then ( let finish doc = let text = GobPretty.show doc in - add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = msg_context ()}} (* TODO: why context? *) + add_final {tags = Category category :: tags; severity; multipiece = Single {loc = None; text; context = None}} in Pretty.gprintf finish fmt ) From 55a8f1e34a81c899bf5cff57a101139c2c240830 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Thu, 28 Sep 2023 15:41:58 +0200 Subject: [PATCH 353/401] Remove __builtin_alloca from invalidate_actions --- src/analyses/libraryFunctions.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 800d3e4902..e4f42cfaf2 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -1038,7 +1038,6 @@ let invalidate_actions = [ "sigaddset", writesAll;(*unsafe*) "raise", writesAll;(*unsafe*) "_strlen", readsAll;(*safe*) - "__builtin_alloca", readsAll;(*safe*) "dlopen", readsAll;(*safe*) "dlsym", readsAll;(*safe*) "dlclose", readsAll;(*safe*) From 21584eec15a258f47f5f9cb47b5daeef13ba0865 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:43:40 +0200 Subject: [PATCH 354/401] Extend UAF analysis to support alloca() --- src/analyses/useAfterFree.ml | 41 +++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 8c70322553..c313c0a90b 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -4,7 +4,12 @@ open GoblintCil open Analyses open MessageCategory -module ToppedVarInfoSet = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) +module AllocaVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All alloca() Variables" end) +module HeapVars = SetDomain.ToppedSet(CilType.Varinfo)(struct let topname = "All Heap Variables" end) + +(* Heap vars created by alloca() and deallocated at function exit * Heap vars deallocated by free() *) +module StackAndHeapVars = Lattice.Prod(AllocaVars)(HeapVars) + module ThreadIdSet = SetDomain.Make(ThreadIdDomain.ThreadLifted) module Spec : Analyses.MCPSpec = @@ -13,7 +18,7 @@ struct let name () = "useAfterFree" - module D = ToppedVarInfoSet + module D = StackAndHeapVars module C = Lattice.Unit module G = ThreadIdSet module V = VarinfoV @@ -57,7 +62,7 @@ struct let any_equal_current threads = ThreadIdSet.exists (equal_current current) threads in if not current_is_unique && any_equal_current freeing_threads then M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a Use-After-Free might occur for heap variable %a" CilType.Varinfo.pretty heap_var - else if D.mem heap_var ctx.local then + else if HeapVars.mem heap_var (snd ctx.local) then M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Use-After-Free might occur in current unique thread %a for heap variable %a" ThreadIdDomain.FlagConfiguredTID.pretty current CilType.Varinfo.pretty heap_var end | `Top -> @@ -85,7 +90,7 @@ struct 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 + if HeapVars.mem v (snd 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 = @@ -129,7 +134,7 @@ struct let side_effect_mem_free ctx freed_heap_vars threadid = let threadid = G.singleton threadid in - D.iter (fun var -> ctx.sideg var threadid) freed_heap_vars + HeapVars.iter (fun var -> ctx.sideg var threadid) freed_heap_vars (* TRANSFER FUNCTIONS *) @@ -153,7 +158,7 @@ struct let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let caller_state = ctx.local in List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; - if D.is_empty caller_state then + if AllocaVars.is_empty (fst caller_state) && HeapVars.is_empty (snd caller_state) then [caller_state, caller_state] else ( let reachable_from_args = List.fold_left (fun ad arg -> Queries.AD.join ad (ctx.ask (ReachableFrom arg))) (Queries.AD.empty ()) args in @@ -161,13 +166,18 @@ struct [caller_state, caller_state] else 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 *) + let callee_state = (AllocaVars.empty (), HeapVars.filter (fun var -> List.mem var reachable_vars) (snd caller_state)) in (* TODO: use AD.mem directly *) [caller_state, callee_state] ) let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - let caller_state = ctx.local in - D.join caller_state callee_local + let (caller_stack_state, caller_heap_state) = ctx.local in + let callee_stack_state = fst callee_local in + let callee_heap_state = snd callee_local in + (* Put all alloca()-vars together with all freed() vars in the caller's second component *) + (* Don't change caller's first component => caller hasn't exited yet *) + let callee_combined_state = HeapVars.join callee_stack_state callee_heap_state in + (caller_stack_state, HeapVars.join caller_heap_state callee_combined_state) let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = Option.iter (fun x -> warn_lval_might_contain_freed "enter" ctx x) lval; @@ -185,13 +195,20 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) -> D.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state | _ -> state - ) ad (D.empty ()) + ) ad (HeapVars.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); - D.join state (pointed_to_heap_vars) (* Add all heap vars, which ptr points to, to the state *) + (* Add all heap vars, which ptr points to, to the state *) + (fst state, HeapVars.join (snd state) pointed_to_heap_vars) + | _ -> state + end + | Alloca _ -> + (* Create fresh heap var for the alloca() call *) + begin match ctx.ask (Queries.HeapVar {on_stack = true}) with + | `Lifted v -> (AllocaVars.add v (fst state), snd state) | _ -> state end | _ -> state From 05a3b98ea27b42994d275e14e43fb372c8337a2c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:44:02 +0200 Subject: [PATCH 355/401] Add alloca() UAF test case --- .../regression/74-use_after_free/13-alloca-uaf.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/regression/74-use_after_free/13-alloca-uaf.c diff --git a/tests/regression/74-use_after_free/13-alloca-uaf.c b/tests/regression/74-use_after_free/13-alloca-uaf.c new file mode 100644 index 0000000000..bb052b010c --- /dev/null +++ b/tests/regression/74-use_after_free/13-alloca-uaf.c @@ -0,0 +1,16 @@ +//PARAM: --set ana.activated[+] useAfterFree +#include +#include + +int *f() { + int *c = alloca(sizeof(int)); + return c; +} + +int main(int argc, char const *argv[]) { + int *ps = alloca(sizeof(int)); + int *c = f(); + int a = *ps; + int b = *c; //WARN + return 0; +} From 2fbcdb8d19e8e84998d2d709fb47bd949d743549 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 09:44:22 +0200 Subject: [PATCH 356/401] Add alloca() invalid dealloc test case --- .../09-juliet-invalid-dealloc-alloca.c | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c diff --git a/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c b/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c new file mode 100644 index 0000000000..9a84d1e49a --- /dev/null +++ b/tests/regression/75-invalid_dealloc/09-juliet-invalid-dealloc-alloca.c @@ -0,0 +1,75 @@ +#include +#include + +typedef struct twoIntsStruct { + int intOne ; + int intTwo ; +} twoIntsStruct; + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54_bad(void) { + twoIntsStruct *data; + data = (twoIntsStruct *)0; + { + twoIntsStruct *dataBuffer = __builtin_alloca(800UL); + { + size_t i; + i = 0UL; + + goto ldv_3204; + ldv_3203: + ; + + (dataBuffer + i)->intOne = 1; + (dataBuffer + i)->intTwo = 1; + + i += 1UL; + ldv_3204: + ; + + if (i <= 99UL) + goto ldv_3203; + else + goto ldv_3205; + ldv_3205: + ; + } + + data = dataBuffer; + } + + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54b_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54b_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54c_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54c_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54d_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54d_badSink(twoIntsStruct *data) { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54e_badSink(data); + return; +} + +void CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54e_badSink(twoIntsStruct *data) { + free((void *)data); //WARN + return; +} + +int main(int argc, char **argv) { + int __retres; + { + CWE590_Free_Memory_Not_on_Heap__free_struct_alloca_54_bad(); + __retres = 0; + goto return_label; + } + + __retres = 0; + return_label: + return __retres; +} From ee6dc3672fac8492a1e59bedb9fddc5c95b542a3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 10:54:19 +0300 Subject: [PATCH 357/401] Fix race in race_reach test for sv-benchmarks --- tests/regression/28-race_reach/61-invariant_racing.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/regression/28-race_reach/61-invariant_racing.c b/tests/regression/28-race_reach/61-invariant_racing.c index 3facd56d32..22277557f9 100644 --- a/tests/regression/28-race_reach/61-invariant_racing.c +++ b/tests/regression/28-race_reach/61-invariant_racing.c @@ -6,9 +6,12 @@ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *t_fun(void *arg) { pthread_mutex_lock(&mutex); - if (x == 0) { + pthread_mutex_lock(&__global_lock); + if (x == 0) { // NORACE + pthread_mutex_unlock(&__global_lock); pthread_mutex_unlock(&mutex); } else { + pthread_mutex_unlock(&__global_lock); pthread_mutex_unlock(&mutex); access(x); } From 5cca543065efea0562294392a4dec1d6833b4b58 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 10:17:57 +0200 Subject: [PATCH 358/401] Add forgotton base_address check in BlobSize after master merge --- 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 b825bafab0..21513cc175 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1249,7 +1249,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || o <> `NoOffset + | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( From 4090e7498b43093c2fd1a8a3250194b03c72043c Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 10:20:36 +0200 Subject: [PATCH 359/401] Fix __builtin_alloca classification --- 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 2388bff586..c8c9a25ec1 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -440,7 +440,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); - ("__builtin_alloca", special [__ "size" []] @@ fun size -> Malloc size); + ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ] let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ From 35513d09c2ed2a0fb6f00a1e0dd1854f9dd79329 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:27:35 +0200 Subject: [PATCH 360/401] Move all *alloca() functions to gcc_descs_list --- 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 c8c9a25ec1..87f7ef0572 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -64,8 +64,6 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("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; }); - ("alloca", special [__ "size" []] @@ fun size -> Alloca size); - ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ("malloc", special [__ "size" []] @@ fun size -> Malloc size); ("calloc", special [__ "n" []; __ "size" []] @@ fun n size -> Calloc {count = n; size}); ("realloc", special [__ "ptr" [r; f]; __ "size" []] @@ fun ptr size -> Realloc { ptr; size }); @@ -440,6 +438,7 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); + ("alloca", special [__ "size" []] @@ fun size -> Alloca size); ("__builtin_alloca", special [__ "size" []] @@ fun size -> Alloca size); ] From 6e7c00edaf5891050401b8f3f87a2d07333dca6a Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:54:52 +0200 Subject: [PATCH 361/401] Add out-of-bounds check for memset and memcpy --- src/analyses/base.ml | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c4fdd633d3..49f61fdd1e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,6 +2026,80 @@ struct 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 + (* Get the size of the smallest memory that dest points-to *) + let get_min_size_of_dest ctx dest = + let intdom_of_int x = + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + in + let points_to_heap_only = + match ctx.ask (Queries.MayPointTo dest) with + | a when not (AD.is_top a) -> + AD.for_all (function + | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) + | _ -> false + ) a + | _ -> false + in + if points_to_heap_only then + (* Ask for BlobSize from the base address (the second field set to true) in order to avoid BlobSize giving us bot *) + ctx.ask (Queries.BlobSize {exp = dest; base_address = true}) + else + match ctx.ask (Queries.MayPointTo dest) with + | a when not (Queries.AD.is_top a) -> + let pts_list = Queries.AD.elements a in + let pts_elems_to_sizes (addr: Queries.AD.elt) = + begin match addr with + | Addr (v, _) -> + begin match v.vtype with + | TArray (item_typ, _, _) -> + let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in + let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + begin match ctx.ask (Queries.EvalLength dest) with + | `Lifted arr_len -> `Lifted (ID.mul item_typ_size_in_bytes arr_len) + | `Bot -> `Bot + | `Top -> `Top + end + | _ -> + let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in + `Lifted (intdom_of_int type_size_in_bytes) + end + | _ -> `Top + end + in + (* Map each points-to-set element to its size *) + let pts_sizes = List.map pts_elems_to_sizes pts_list in + (* Take the smallest of all sizes that ptr's contents may have *) + begin match pts_sizes with + | [] -> `Bot + | [x] -> x + | x::xs -> List.fold_left (fun acc elem -> + if ValueDomainQueries.ID.compare acc elem >= 0 then elem else acc + ) x xs + end + | _ -> + M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; + `Top + + (* Used for memset() and memcpy() out-of-bounds checks *) + let check_count ctx fun_name dest n = + let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let dest_size = get_min_size_of_dest ctx dest in + let eval_n = ctx.ask (Queries.EvalInt n) in + match ValueDomainQueries.ID.is_top dest_size, ValueDomainQueries.ID.is_top eval_n with + | true, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name + | false, false -> + if dest_size < eval_n then begin + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + end + + let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> @@ -2093,7 +2167,8 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> - (* TODO: check count *) + (* Check count *) + check_count ctx f.vname dest count; let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = @@ -2110,7 +2185,9 @@ struct let dest_a, dest_typ = addr_type_of_exp dest in let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Memcpy { dest = dst; src }, _ -> + | Memcpy { dest = dst; src; n; }, _ -> + (* Check n *) + check_count ctx f.vname dst n; memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> From 5ed769f22598ed1aeb5cb36038ffc94cb1c49b55 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 11:55:16 +0200 Subject: [PATCH 362/401] Add regr. test case for memset and memcpy out-of-bounds --- .../77-mem-oob/06-memset-memcpy-oob.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-mem-oob/06-memset-memcpy-oob.c diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c new file mode 100644 index 0000000000..84a46c75ba --- /dev/null +++ b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +#include +#include + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memset(a, 0, 10 * sizeof(int)); //NOWARN + memset(a, 0, 41); //WARN + memset(a, 0, 40000000); //WARN + + memcpy(a, b, 40); //NOWARN + memcpy(a, b, 10 * sizeof(int)); //NOWARN + memcpy(a, b, 41); //WARN + memcpy(a, b, 40000000); //WARN + memcpy(a, b, 15 * sizeof(int)); //WARN + return 0; +} From 432a4e3259bea4ac597de6bf78db30f86ba101ae Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:31:31 +0300 Subject: [PATCH 363/401] Simply HeapVar ask in heap_var Co-authored-by: Simmo Saan --- 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 21513cc175..f2c4c4ed65 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -151,7 +151,7 @@ struct let longjmp_return = ref dummyFunDec.svar let heap_var on_stack ctx = - let info = match (ctx.ask (Q.HeapVar {on_stack = on_stack})) with + let info = match (ctx.ask (Q.HeapVar {on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info From 3bdc92d7c6e58520f6ed552cb687fa158adcf429 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 12:41:48 +0200 Subject: [PATCH 364/401] Extract long query ask into own function --- src/analyses/base.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f2c4c4ed65..1c21da3327 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1122,6 +1122,9 @@ struct (* interpreter end *) + let is_not_heap_alloc_var ctx v = + (not (ctx.ask (Queries.IsDynamicallyAlloced v))) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not (ctx.ask (Queries.IsHeapVar v))) + let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in let ask = Analyses.ask_of_ctx ctx in @@ -1249,7 +1252,7 @@ struct (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> (not @@ ctx.ask (Queries.IsDynamicallyAlloced v)) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not @@ ctx.ask (Queries.IsHeapVar v)) || (if not from_base_addr then o <> `NoOffset else false) + | Addr (v,o) -> is_not_heap_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( @@ -2009,7 +2012,7 @@ struct let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> not (ctx.ask (Q.IsDynamicallyAlloced v)) || (ctx.ask (Q.IsDynamicallyAlloced v) && not @@ ctx.ask (Q.IsHeapVar v)) + | Addr (v,_) -> is_not_heap_alloc_var ctx v | _ -> false) in let has_non_zero_offset = AD.exists (function From 7b1d29850036fb1287b10c8aeb962a162b7fac11 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 12:50:34 +0200 Subject: [PATCH 365/401] Rename queries: * HeapVar -> AllocVar * IsDynamicallyAlloced -> IsAllocVar --- src/analyses/base.ml | 10 +++++----- src/analyses/mallocFresh.ml | 2 +- src/analyses/memLeak.ml | 4 ++-- src/analyses/useAfterFree.ml | 6 +++--- src/analyses/wrapperFunctionAnalysis.ml | 4 ++-- src/domains/queries.ml | 26 +++++++++++++------------ 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1c21da3327..dacd412072 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -151,7 +151,7 @@ struct let longjmp_return = ref dummyFunDec.svar let heap_var on_stack ctx = - let info = match (ctx.ask (Q.HeapVar {on_stack})) with + let info = match (ctx.ask (Q.AllocVar {on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info @@ -1123,7 +1123,7 @@ struct (* interpreter end *) let is_not_heap_alloc_var ctx v = - (not (ctx.ask (Queries.IsDynamicallyAlloced v))) || (ctx.ask (Queries.IsDynamicallyAlloced v) && not (ctx.ask (Queries.IsHeapVar v))) + (not (ctx.ask (Queries.IsAllocVar v))) || (ctx.ask (Queries.IsAllocVar v) && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in @@ -1400,7 +1400,7 @@ struct let t = match t_override with | Some t -> t | None -> - if a.f (Q.IsDynamicallyAlloced x) then + if a.f (Q.IsAllocVar x) then (* the vtype of heap vars will be TVoid, so we need to trust the pointer we got to this to be of the right type *) (* i.e. use the static type of the pointer here *) lval_type @@ -1446,7 +1446,7 @@ struct (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. Allocated blocks are representend by Blobs with additional information, so they need to be looked-up. *) - let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsDynamicallyAlloced x)) && offs = `NoOffset then begin + let old_value = if not invariant && Cil.isIntegralType x.vtype && not (a.f (IsAllocVar x)) && offs = `NoOffset then begin VD.bot_value ~varAttr:x.vattr lval_type end else Priv.read_global a priv_getg st x @@ -2596,7 +2596,7 @@ struct | MayBeThreadReturn | PartAccess _ | IsHeapVar _ - | IsDynamicallyAlloced _ + | IsAllocVar _ | IsMultiple _ | CreatedThreads | MustJoinedThreads -> diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 2eed772527..3a501fc72f 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -43,7 +43,7 @@ struct | Malloc _ | Calloc _ | Realloc _ -> - begin match ctx.ask (HeapVar {on_stack = false}) with + begin match ctx.ask (AllocVar {on_stack = false}) with | `Lifted var -> D.add var ctx.local | _ -> ctx.local end diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index abf0deb954..8576096dfe 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -44,7 +44,7 @@ struct | 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 {on_stack = false}) with + begin match ctx.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> D.add var state | _ -> state end @@ -53,7 +53,7 @@ struct | 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 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> D.remove v state (* Unique pointed to heap vars *) | _ -> state end | _ -> state diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index c313c0a90b..7e89dddb69 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -96,7 +96,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsDynamicallyAlloced v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) -> v :: vars | _ -> vars ) ad [] in @@ -195,7 +195,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsDynamicallyAlloced var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state + | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsAllocVar var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state | _ -> state ) ad (HeapVars.empty ()) in @@ -207,7 +207,7 @@ struct end | Alloca _ -> (* Create fresh heap var for the alloca() call *) - begin match ctx.ask (Queries.HeapVar {on_stack = true}) with + begin match ctx.ask (Queries.AllocVar {on_stack = true}) with | `Lifted v -> (AllocaVars.add v (fst state), snd state) | _ -> state end diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 43d472d0e3..5c0176df48 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -133,7 +133,7 @@ module MallocWrapper : MCPSpec = struct let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = let wrapper_node, counter = ctx.local in match q with - | Q.HeapVar {on_stack = on_stack} -> + | Q.AllocVar {on_stack = on_stack} -> let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node | _ -> node_for_ctx ctx @@ -145,7 +145,7 @@ module MallocWrapper : MCPSpec = struct `Lifted var | Q.IsHeapVar v -> NodeVarinfoMap.mem_varinfo v && not @@ hasAttribute "stack_alloca" v.vattr - | Q.IsDynamicallyAlloced v -> + | Q.IsAllocVar v -> NodeVarinfoMap.mem_varinfo v | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 07e239bee5..c706339bf2 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -101,9 +101,11 @@ type _ t = | IterVars: itervar -> Unit.t t | PathQuery: int * 'a t -> 'a t (** Query only one path under witness lifter. *) | DYojson: FlatYojson.t t (** Get local state Yojson of one path under [PathQuery]. *) - | HeapVar: {on_stack: bool} -> VI.t t (* If on_stack is [true], then alloca() or a similar function was called *) + | AllocVar: {on_stack: bool} -> VI.t t + (* Create a variable representing a dynamic allocation-site *) + (* If on_stack is [true], then the dynamic allocation is on the stack (i.e., alloca() or a similar function was called). Otherwise, allocation is on the heap *) + | IsAllocVar: varinfo -> MayBool.t t (* [true] if variable represents dynamically allocated memory *) | IsHeapVar: varinfo -> MayBool.t t (* TODO: is may or must? *) - | IsDynamicallyAlloced: varinfo -> MayBool.t t (* [true] if heap var represents dynamically alloced memory *) | IsMultiple: varinfo -> MustBool.t t (* For locals: Is another copy of this local variable reachable via pointers? *) (* For dynamically allocated memory: Does this abstract variable corrrespond to a unique heap location? *) @@ -155,7 +157,7 @@ struct | MayBePublicWithout _ -> (module MayBool) | MayBeThreadReturn -> (module MayBool) | IsHeapVar _ -> (module MayBool) - | IsDynamicallyAlloced _ -> (module MayBool) + | IsAllocVar _ -> (module MayBool) | MustBeProtectedBy _ -> (module MustBool) | MustBeAtomic -> (module MustBool) | MustBeSingleThreaded _ -> (module MustBool) @@ -167,7 +169,7 @@ struct | BlobSize _ -> (module ID) | CurrentThreadId -> (module ThreadIdDomain.ThreadLifted) | ThreadCreateIndexedNode -> (module ThreadNodeLattice) - | HeapVar _ -> (module VI) + | AllocVar _ -> (module VI) | EvalStr _ -> (module SD) | IterPrevVars _ -> (module Unit) | IterVars _ -> (module Unit) @@ -220,7 +222,7 @@ struct | MayBePublicWithout _ -> MayBool.top () | MayBeThreadReturn -> MayBool.top () | IsHeapVar _ -> MayBool.top () - | IsDynamicallyAlloced _ -> MayBool.top () + | IsAllocVar _ -> MayBool.top () | MutexType _ -> MutexAttrDomain.top () | MustBeProtectedBy _ -> MustBool.top () | MustBeAtomic -> MustBool.top () @@ -233,7 +235,7 @@ struct | BlobSize _ -> ID.top () | CurrentThreadId -> ThreadIdDomain.ThreadLifted.top () | ThreadCreateIndexedNode -> ThreadNodeLattice.top () - | HeapVar _ -> VI.top () + | AllocVar _ -> VI.top () | EvalStr _ -> SD.top () | IterPrevVars _ -> Unit.top () | IterVars _ -> Unit.top () @@ -293,7 +295,7 @@ struct | Any (PartAccess _) -> 23 | Any (IterPrevVars _) -> 24 | Any (IterVars _) -> 25 - | Any (HeapVar _) -> 29 + | Any (AllocVar _) -> 29 | Any (IsHeapVar _) -> 30 | Any (IsMultiple _) -> 31 | Any (EvalThread _) -> 32 @@ -318,7 +320,7 @@ struct | Any ThreadCreateIndexedNode -> 51 | Any ThreadsJoinedCleanly -> 52 | Any (TmpSpecial _) -> 53 - | Any (IsDynamicallyAlloced _) -> 54 + | Any (IsAllocVar _) -> 54 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -358,7 +360,7 @@ struct else compare (Any q1) (Any q2) | Any (IsHeapVar v1), Any (IsHeapVar v2) -> CilType.Varinfo.compare v1 v2 - | Any (IsDynamicallyAlloced v1), Any (IsDynamicallyAlloced v2) -> CilType.Varinfo.compare v1 v2 + | Any (IsAllocVar v1), Any (IsAllocVar v2) -> CilType.Varinfo.compare v1 v2 | Any (IsMultiple v1), Any (IsMultiple v2) -> CilType.Varinfo.compare v1 v2 | Any (EvalThread e1), Any (EvalThread e2) -> CilType.Exp.compare e1 e2 | Any (EvalJumpBuf e1), Any (EvalJumpBuf e2) -> CilType.Exp.compare e1 e2 @@ -399,7 +401,7 @@ struct | Any (IterVars i) -> 0 | Any (PathQuery (i, q)) -> 31 * i + hash (Any q) | Any (IsHeapVar v) -> CilType.Varinfo.hash v - | Any (IsDynamicallyAlloced v) -> CilType.Varinfo.hash v + | Any (IsAllocVar v) -> CilType.Varinfo.hash v | Any (IsMultiple v) -> CilType.Varinfo.hash v | Any (EvalThread e) -> CilType.Exp.hash e | Any (EvalJumpBuf e) -> CilType.Exp.hash e @@ -447,9 +449,9 @@ struct | Any (IterPrevVars i) -> Pretty.dprintf "IterPrevVars _" | Any (IterVars i) -> Pretty.dprintf "IterVars _" | Any (PathQuery (i, q)) -> Pretty.dprintf "PathQuery (%d, %a)" i pretty (Any q) - | Any (HeapVar {on_stack = on_stack}) -> Pretty.dprintf "HeapVar %b" on_stack + | Any (AllocVar {on_stack = on_stack}) -> Pretty.dprintf "AllocVar %b" on_stack | Any (IsHeapVar v) -> Pretty.dprintf "IsHeapVar %a" CilType.Varinfo.pretty v - | Any (IsDynamicallyAlloced v) -> Pretty.dprintf "IsDynamicallyAlloced %a" CilType.Varinfo.pretty v + | Any (IsAllocVar v) -> Pretty.dprintf "IsAllocVar %a" CilType.Varinfo.pretty v | Any (IsMultiple v) -> Pretty.dprintf "IsMultiple %a" CilType.Varinfo.pretty v | Any (EvalThread e) -> Pretty.dprintf "EvalThread %a" CilType.Exp.pretty e | Any (EvalJumpBuf e) -> Pretty.dprintf "EvalJumpBuf %a" CilType.Exp.pretty e From 5f162216927f10cf42a9b1b5ad50ddea0bd4868d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:13:46 +0200 Subject: [PATCH 366/401] Fix wrong query usage in UAF --- src/analyses/useAfterFree.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index f3dbecacc2..02231336c0 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -121,7 +121,7 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsHeapVar v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) -> v :: vars | _ -> vars ) ad [] in From b1f2f2d9a5f3a665bdd63445f1d4796338b59401 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:14:04 +0200 Subject: [PATCH 367/401] Fix test numbering --- .../74-use_after_free/{13-alloca-uaf.c => 14-alloca-uaf.c} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/regression/74-use_after_free/{13-alloca-uaf.c => 14-alloca-uaf.c} (91%) diff --git a/tests/regression/74-use_after_free/13-alloca-uaf.c b/tests/regression/74-use_after_free/14-alloca-uaf.c similarity index 91% rename from tests/regression/74-use_after_free/13-alloca-uaf.c rename to tests/regression/74-use_after_free/14-alloca-uaf.c index bb052b010c..3dc494cb09 100644 --- a/tests/regression/74-use_after_free/13-alloca-uaf.c +++ b/tests/regression/74-use_after_free/14-alloca-uaf.c @@ -10,7 +10,7 @@ int *f() { int main(int argc, char const *argv[]) { int *ps = alloca(sizeof(int)); int *c = f(); - int a = *ps; + int a = *ps; //NOWARN int b = *c; //WARN return 0; } From 12bee2749dcba9ae933c657cb991ffb3ea292013 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 13:22:37 +0200 Subject: [PATCH 368/401] Add temporary fix for failing MacOS CI job --- tests/regression/77-mem-oob/06-memset-memcpy-oob.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c index 84a46c75ba..1050c199e0 100644 --- a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c +++ b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c @@ -1,4 +1,5 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed #include #include From 80a51be64966b15376f5d1f83e61046d6e522565 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 14:40:07 +0300 Subject: [PATCH 369/401] Add extensive atomic and volatile ignorable races tests --- .../regression/04-mutex/62-simple_atomic_nr.c | 83 ++++++++++++++++--- tests/regression/04-mutex/99-volatile.c | 51 ++++++++++-- 2 files changed, 114 insertions(+), 20 deletions(-) diff --git a/tests/regression/04-mutex/62-simple_atomic_nr.c b/tests/regression/04-mutex/62-simple_atomic_nr.c index d63f303251..fdef44bdd6 100644 --- a/tests/regression/04-mutex/62-simple_atomic_nr.c +++ b/tests/regression/04-mutex/62-simple_atomic_nr.c @@ -1,24 +1,83 @@ #include -#include #include -atomic_int myglobal; -pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +atomic_int g1; +_Atomic int g2; +_Atomic(int) g3; + +atomic_int a1[1]; +_Atomic int a2[1]; +_Atomic(int) a3[1]; + +struct s { + int f0; + atomic_int f1; + _Atomic int f2; + _Atomic(int) f3; +}; + +struct s s1; +_Atomic struct s s2; +_Atomic(struct s) s3; + +typedef atomic_int t_int1; +typedef _Atomic int t_int2; +typedef _Atomic(int) t_int3; + +t_int1 t1; +t_int2 t2; +t_int3 t3; + +typedef int t_int0; + +_Atomic t_int0 t0; +_Atomic(t_int0) t00; + +atomic_int *p0 = &g1; +int x; +// int * _Atomic p1 = &x; // TODO: https://github.com/goblint/cil/issues/64 +// _Atomic(int*) p2 = &x; // TODO: https://github.com/goblint/cil/issues/64 +// atomic_int * _Atomic p3 = &g1; // TODO: https://github.com/goblint/cil/issues/64 + +atomic_flag flag = ATOMIC_FLAG_INIT; void *t_fun(void *arg) { - pthread_mutex_lock(&mutex1); - myglobal=myglobal+1; // NORACE - pthread_mutex_unlock(&mutex1); + g1++; // NORACE + g2++; // NORACE + g3++; // NORACE + a1[0]++; // NORACE + a2[0]++; // NORACE + a3[0]++; // NORACE + s1.f1++; // NORACE + s1.f2++; // NORACE + s1.f3++; // NORACE + s2.f0++; // NORACE + s3.f0++; // NORACE + t1++; // NORACE + t2++; // NORACE + t3++; // NORACE + t0++; // NORACE + t00++; // NORACE + (*p0)++; // NORACE + // p1++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // p2++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // p3++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + // (*p3)++; // TODO NORACE: https://github.com/goblint/cil/issues/64 + + struct s ss = {0}; + s2 = ss; // NORACE + s3 = ss; // NORACE + + atomic_flag_clear(&flag); // NORACE + atomic_flag_test_and_set(&flag); // NORACE return NULL; } int main(void) { - pthread_t id; + pthread_t id, id2; pthread_create(&id, NULL, t_fun, NULL); - pthread_mutex_lock(&mutex2); - myglobal=myglobal+1; // NORACE - pthread_mutex_unlock(&mutex2); - pthread_join (id, NULL); + pthread_create(&id2, NULL, t_fun, NULL); + pthread_join(id, NULL); + pthread_join(id2, NULL); return 0; } diff --git a/tests/regression/04-mutex/99-volatile.c b/tests/regression/04-mutex/99-volatile.c index aaf81f13a1..7c2a255902 100644 --- a/tests/regression/04-mutex/99-volatile.c +++ b/tests/regression/04-mutex/99-volatile.c @@ -1,18 +1,53 @@ // PARAM: --disable ana.race.volatile #include -#include -volatile int myglobal; +volatile int g1; + +volatile int a1[1]; + +struct s { + int f0; + volatile int f1; +}; + +struct s s1; +volatile struct s s2; + +typedef volatile int t_int1; + +t_int1 t1; + +typedef int t_int0; + +volatile t_int0 t0; + +volatile int *p0 = &g1; +int x; +int * volatile p1 = &x; +volatile int * volatile p2 = &g1; void *t_fun(void *arg) { - myglobal= 8; //NORACE + g1++; // NORACE + a1[0]++; // NORACE + s1.f1++; // NORACE + s2.f0++; // NORACE + t1++; // NORACE + t0++; // NORACE + (*p0)++; // NORACE + p1++; // NORACE + p2++; // NORACE + (*p2)++; // NORACE + + struct s ss = {0}; + s2 = ss; // NORACE return NULL; } int main(void) { - pthread_t id; - pthread_create(&id, NULL, t_fun, (void*) &myglobal); - myglobal = 42; //NORACE - pthread_join (id, NULL); + pthread_t id, id2; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id2, NULL, t_fun, NULL); + pthread_join(id, NULL); + pthread_join(id2, NULL); return 0; -} \ No newline at end of file +} From 8b513e8431e6f6301fca9162e83a2b7ed792dbcd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:39:45 +0300 Subject: [PATCH 370/401] Rewrite ignorable race memo check --- src/domains/access.ml | 64 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index fa6446df16..7b6fa77b06 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -10,7 +10,7 @@ module M = Messages (* Some helper functions to avoid flagging race warnings on atomic types, and * other irrelevant stuff, such as mutexes and functions. *) -let is_ignorable_type (t: typ): bool = +let rec is_ignorable_type (t: typ): bool = match t with | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t"; _ }, _) -> true | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true @@ -18,20 +18,59 @@ let is_ignorable_type (t: typ): bool = begin match Cilfacade.split_anoncomp_name cname with | (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 + | _ -> false (* TODO: fall back to attrs case *) end | TComp ({ cname = "lock_class_key"; _ }, _) -> true | TInt (IInt, attr) when hasAttribute "mutex" attr -> true - | t when hasAttribute "atomic" (typeAttrs t) -> true (* C11 _Atomic *) - | _ -> false + | TFun _ -> true + | _ -> + let attrs = typeAttrsOuter t in + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + if List.exists is_ignorable_attr attrs then + true + else ( + match t with + | TNamed ({ttype; _}, attrs) -> is_ignorable_type (typeAddAttributes attrs ttype) + | _ -> false + ) + +let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = + if is_ignorable_type t then + true + else ( + let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeAddAttributes contageous + in + match o with + | `NoOffset -> false (* already checked t *) + | `Index (_, o') -> + begin match unrollType t with + | TArray (et, _, attrs) -> + let t' = blendAttributes attrs et in + is_ignorable_type_offset t' o' + | _ -> false (* index on non-array*) + end + | `Field (f, o') -> + begin match unrollType t with + | TComp (_, attrs) -> + let t' = blendAttributes attrs f.ftype in + is_ignorable_type_offset t' o' + | _ -> false (* field on non-compound *) + end + ) + +let is_ignorable_mval = function + | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) + | (v, o) -> is_ignorable_type_offset v.vtype o -let is_ignorable = function - | None -> false - | Some (v,os) when hasAttribute "thread" v.vattr && not (v.vaddrof) -> true (* Thread-Local Storage *) - | Some (v,os) when BaseUtil.is_volatile v && not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Some (v,os) -> - try isFunctionType v.vtype || is_ignorable_type v.vtype - with Not_found -> false +let is_ignorable_memo = function + | (`Type _, _) -> false (* TODO: do something *) + | (`Var v, o) -> is_ignorable_mval (v, o) module TSH = Hashtbl.Make (CilType.Typsig) @@ -195,8 +234,7 @@ let get_val_type e: acc_typ = (** Add access to {!Memo} after distributing. *) let add_one ~side memo: unit = - let mv = Memo.to_mval memo in - let ignorable = is_ignorable mv in + let ignorable = is_ignorable_memo memo in if M.tracing then M.trace "access" "add_one %a (ignorable = %B)\n" Memo.pretty memo ignorable; if not ignorable then side memo From 66071f2555aa6f6be4cb2be8b8795cab2a0e5888 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:47:28 +0300 Subject: [PATCH 371/401] Exclude atomic_flag races --- src/analyses/libraryFunctions.ml | 6 ++++++ src/domains/access.ml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index c3ca48da93..6ff591e7c4 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -127,6 +127,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); ("atexit", unknown [drop "function" [s]]); + ("atomic_flag_clear", unknown [drop "obj" [w]]); + ("atomic_flag_clear_explicit", unknown [drop "obj" [w]; drop "order" []]); + ("atomic_flag_test_and_set", unknown [drop "obj" [r; w]]); + ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); ] (** C POSIX library functions. @@ -435,6 +439,8 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_popcountll", unknown [drop "x" []]); ("__atomic_store_n", unknown [drop "ptr" [w]; drop "val" []; drop "memorder" []]); ("__atomic_load_n", unknown [drop "ptr" [r]; drop "memorder" []]); + ("__atomic_clear", unknown [drop "ptr" [w]; drop "memorder" []]); + ("__atomic_test_and_set", unknown [drop "ptr" [r; w]; drop "memorder" []]); ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); diff --git a/src/domains/access.ml b/src/domains/access.ml index 7b6fa77b06..7066e6e349 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -12,7 +12,7 @@ module M = Messages let rec is_ignorable_type (t: typ): bool = match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t"; _ }, _) -> true + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true | 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 From df9fda742d767ab92108b127a21b5d137967c312 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 15:58:36 +0300 Subject: [PATCH 372/401] Document refactored race ignore check --- src/domains/access.ml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 7066e6e349..3601624ae6 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -11,6 +11,7 @@ module M = Messages * other irrelevant stuff, such as mutexes and functions. *) let rec is_ignorable_type (t: typ): bool = + (* efficient pattern matching first *) match t with | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true @@ -18,13 +19,13 @@ let rec is_ignorable_type (t: typ): bool = begin match Cilfacade.split_anoncomp_name cname with | (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 (* TODO: fall back to attrs case *) + | _ -> false (* TODO: fall back to attrs case? *) end - | TComp ({ cname = "lock_class_key"; _ }, _) -> true - | TInt (IInt, attr) when hasAttribute "mutex" attr -> true + | TComp ({ cname = "lock_class_key"; _ }, _) -> true (* kernel? *) + | TInt (IInt, attr) when hasAttribute "mutex" attr -> true (* kernel? *) | TFun _ -> true - | _ -> - let attrs = typeAttrsOuter t in + | _ -> (* check attrs *) + let attrs = typeAttrsOuter t in (* only outer because we unroll TNamed ourselves *) let is_ignorable_attr = function | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) | Attr ("atomic", _) -> true (* C11 _Atomic *) @@ -33,14 +34,17 @@ let rec is_ignorable_type (t: typ): bool = if List.exists is_ignorable_attr attrs then true else ( + (* unroll TNamed once *) + (* can't use unrollType because we want to check TNamed-s at all intermediate typedefs as well *) match t with | TNamed ({ttype; _}, attrs) -> is_ignorable_type (typeAddAttributes attrs ttype) | _ -> false ) let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = + (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) if is_ignorable_type t then - true + true (* type at offset so far ignorable, no need to recurse *) else ( let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in @@ -53,7 +57,7 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = | TArray (et, _, attrs) -> let t' = blendAttributes attrs et in is_ignorable_type_offset t' o' - | _ -> false (* index on non-array*) + | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match unrollType t with @@ -66,7 +70,7 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = let is_ignorable_mval = function | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) - | (v, o) -> is_ignorable_type_offset v.vtype o + | (v, o) -> is_ignorable_type_offset v.vtype o (* can't use Cilfacade.typeOffset because we want to check types at all intermediate offsets as well *) let is_ignorable_memo = function | (`Type _, _) -> false (* TODO: do something *) From 18a1733dc41bbea8178bc7b481c4de48abe43970 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Fri, 29 Sep 2023 16:20:13 +0300 Subject: [PATCH 373/401] Add atomic library functions from nidhugg benchmark set --- src/analyses/libraryFunctions.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 6ff591e7c4..1964b7bc1f 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -131,6 +131,8 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("atomic_flag_clear_explicit", unknown [drop "obj" [w]; drop "order" []]); ("atomic_flag_test_and_set", unknown [drop "obj" [r; w]]); ("atomic_flag_test_and_set_explicit", unknown [drop "obj" [r; w]; drop "order" []]); + ("atomic_load", unknown [drop "obj" [r]]); + ("atomic_store", unknown [drop "obj" [w]; drop "desired" []]); ] (** C POSIX library functions. @@ -438,9 +440,20 @@ let gcc_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_popcountl", unknown [drop "x" []]); ("__builtin_popcountll", unknown [drop "x" []]); ("__atomic_store_n", unknown [drop "ptr" [w]; drop "val" []; drop "memorder" []]); + ("__atomic_store", unknown [drop "ptr" [w]; drop "val" [r]; drop "memorder" []]); ("__atomic_load_n", unknown [drop "ptr" [r]; drop "memorder" []]); + ("__atomic_load", unknown [drop "ptr" [r]; drop "ret" [w]; drop "memorder" []]); ("__atomic_clear", unknown [drop "ptr" [w]; drop "memorder" []]); + ("__atomic_compare_exchange_n", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" []; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); + ("__atomic_compare_exchange", unknown [drop "ptr" [r; w]; drop "expected" [r; w]; drop "desired" [r]; drop "weak" []; drop "success_memorder" []; drop "failure_memorder" []]); + ("__atomic_fetch_add", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_sub", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_and", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_xor", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_or", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); + ("__atomic_fetch_nand", unknown [drop "ptr" [r; w]; drop "val" []; drop "memorder" []]); ("__atomic_test_and_set", unknown [drop "ptr" [r; w]; drop "memorder" []]); + ("__atomic_thread_fence", unknown [drop "memorder" []]); ("__sync_fetch_and_add", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__sync_fetch_and_sub", unknown (drop "ptr" [r; w] :: drop "value" [] :: VarArgs (drop' []))); ("__builtin_va_copy", unknown [drop "dest" [w]; drop "src" [r]]); From 8c7dbb5d726ffb6084ad9276f1367f2f7032735c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:42:28 +0300 Subject: [PATCH 374/401] Ignore FILE type in races We are unsound for I/O races anyway. --- src/domains/access.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 3601624ae6..69fb5b53fc 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -13,8 +13,8 @@ module M = Messages let rec is_ignorable_type (t: typ): bool = (* efficient pattern matching first *) match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag"; _ }, _) -> true - | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag"; _}, _) -> true + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true + | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"; _}, _) -> true | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) From abc97e5773dd68e4745ebf3daac4a1538b9d5bb6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:56:45 +0300 Subject: [PATCH 375/401] Pin goblint-cil with exposed Cil.typeSigAddAttrs --- goblint.opam | 7 +++---- goblint.opam.locked | 6 ++++++ goblint.opam.template | 7 +++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/goblint.opam b/goblint.opam index 661222805b..c84e7a56f0 100644 --- a/goblint.opam +++ b/goblint.opam @@ -74,12 +74,11 @@ 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: [ - # 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" ] +pin-depends: [ + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # 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" ] -# ] +] 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 bb59c41dd1..8556e9eebb 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,3 +128,9 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +pin-depends: [ + [ + "goblint-cil.2.0.2" + "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" + ] +] diff --git a/goblint.opam.template b/goblint.opam.template index 6259c4d498..d9e1ebf477 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,12 +1,11 @@ # 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: [ - # 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" ] +pin-depends: [ + [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # 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" ] -# ] +] post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] From 93396c8c43ec961d21dadbe6598be5c4ca768a91 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 16:58:56 +0300 Subject: [PATCH 376/401] Duplicate race ignore check for typsig --- src/domains/access.ml | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 69fb5b53fc..ac34966880 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -68,12 +68,61 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = end ) +let is_ignorable_typsig (ts: typsig): bool = + (* efficient pattern matching first *) + match ts with + | TSComp (_, ("__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"), _) -> true + | TSComp (_, cname, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> + begin match Cilfacade.split_anoncomp_name cname with + | (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 (* TODO: fall back to attrs case? *) + end + | TSComp (_, "lock_class_key", _) -> true (* kernel? *) + | TSFun _ -> true + | TSBase t -> is_ignorable_type t + | _ -> (* check attrs *) + let attrs = typeSigAttrs ts in (* only outer because we unroll TNamed ourselves *) + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + List.exists is_ignorable_attr attrs + +let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = + (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) + if is_ignorable_typsig ts then + true (* type at offset so far ignorable, no need to recurse *) + else ( + let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeSigAddAttrs contageous + in + match o with + | `NoOffset -> false (* already checked t *) + | `Index (_, o') -> + begin match ts with + | TSArray (et, _, attrs) -> + let t' = blendAttributes attrs et in + is_ignorable_typsig_offset t' o' + | _ -> false (* index on non-array *) + end + | `Field (f, o') -> + begin match ts with + | TSComp (_, _, attrs) -> + let t' = blendAttributes attrs (typeSig f.ftype) in + is_ignorable_typsig_offset t' o' + | _ -> false (* field on non-compound *) + end + ) + let is_ignorable_mval = function | ({vaddrof = false; vattr; _}, _) when hasAttribute "thread" vattr -> true (* Thread-Local Storage *) | (v, o) -> is_ignorable_type_offset v.vtype o (* can't use Cilfacade.typeOffset because we want to check types at all intermediate offsets as well *) let is_ignorable_memo = function - | (`Type _, _) -> false (* TODO: do something *) + | (`Type ts, o) -> is_ignorable_typsig_offset ts o | (`Var v, o) -> is_ignorable_mval (v, o) module TSH = Hashtbl.Make (CilType.Typsig) From 1d3c7dd62f7d33f39dc69f95bbba0f83b659686e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:08:53 +0300 Subject: [PATCH 377/401] Extract common comp name and attrs ignoring for races --- src/domains/access.ml | 56 +++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index ac34966880..832c6a762e 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -10,28 +10,34 @@ module M = Messages (* Some helper functions to avoid flagging race warnings on atomic types, and * other irrelevant stuff, such as mutexes and functions. *) -let rec is_ignorable_type (t: typ): bool = - (* efficient pattern matching first *) - match t with - | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true - | TComp ({ cname = "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"; _}, _) -> true - | TComp ({ cname; _}, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> +let is_ignorable_comp_name = function + | "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE" -> true + | cname when String.starts_with_stdlib ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (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 (* TODO: fall back to attrs case? *) + | _ -> false end - | TComp ({ cname = "lock_class_key"; _ }, _) -> true (* kernel? *) + | "lock_class_key" -> true (* kernel? *) + | _ -> false + +let is_ignorable_attrs attrs = + let is_ignorable_attr = function + | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) + | Attr ("atomic", _) -> true (* C11 _Atomic *) + | _ -> false + in + List.exists is_ignorable_attr attrs + +let rec is_ignorable_type (t: typ): bool = + (* efficient pattern matching first *) + match t with + | TNamed ({ tname = "atomic_t" | "pthread_mutex_t" | "pthread_rwlock_t" | "pthread_spinlock_t" | "spinlock_t" | "pthread_cond_t" | "atomic_flag" | "FILE" | "__FILE"; _ }, _) -> true + | TComp ({ cname; _}, _) when is_ignorable_comp_name cname -> true | TInt (IInt, attr) when hasAttribute "mutex" attr -> true (* kernel? *) | TFun _ -> true - | _ -> (* check attrs *) - let attrs = typeAttrsOuter t in (* only outer because we unroll TNamed ourselves *) - let is_ignorable_attr = function - | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Attr ("atomic", _) -> true (* C11 _Atomic *) - | _ -> false - in - if List.exists is_ignorable_attr attrs then + | _ -> + if is_ignorable_attrs (typeAttrsOuter t) then (* only outer because we unroll TNamed ourselves *) true else ( (* unroll TNamed once *) @@ -71,24 +77,10 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = let is_ignorable_typsig (ts: typsig): bool = (* efficient pattern matching first *) match ts with - | TSComp (_, ("__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE"), _) -> true - | TSComp (_, cname, _) when String.starts_with_stdlib ~prefix:"__anon" cname -> - begin match Cilfacade.split_anoncomp_name cname with - | (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 (* TODO: fall back to attrs case? *) - end - | TSComp (_, "lock_class_key", _) -> true (* kernel? *) + | TSComp (_, cname, _) when is_ignorable_comp_name cname -> true | TSFun _ -> true | TSBase t -> is_ignorable_type t - | _ -> (* check attrs *) - let attrs = typeSigAttrs ts in (* only outer because we unroll TNamed ourselves *) - let is_ignorable_attr = function - | Attr ("volatile", _) when not (get_bool "ana.race.volatile") -> true (* volatile & races on volatiles should not be reported *) - | Attr ("atomic", _) -> true (* C11 _Atomic *) - | _ -> false - in - List.exists is_ignorable_attr attrs + | _ -> is_ignorable_attrs (typeSigAttrs ts) let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) From 52eac2fbf9287db299514a2b72846cb060c8f234 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:16:20 +0300 Subject: [PATCH 378/401] Clean up typsig race ignoring --- src/domains/access.ml | 25 +++++++++---------------- src/util/cilfacade.ml | 9 +++++++++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/domains/access.ml b/src/domains/access.ml index 832c6a762e..8907ccbc32 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -52,28 +52,25 @@ let rec is_ignorable_type_offset (t: typ) (o: _ Offset.t): bool = if is_ignorable_type t then true (* type at offset so far ignorable, no need to recurse *) else ( - let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) - let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in - typeAddAttributes contageous - in match o with | `NoOffset -> false (* already checked t *) | `Index (_, o') -> begin match unrollType t with | TArray (et, _, attrs) -> - let t' = blendAttributes attrs et in + let t' = Cilfacade.typeBlendAttributes attrs et in is_ignorable_type_offset t' o' | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match unrollType t with | TComp (_, attrs) -> - let t' = blendAttributes attrs f.ftype in + let t' = Cilfacade.typeBlendAttributes attrs f.ftype in is_ignorable_type_offset t' o' | _ -> false (* field on non-compound *) end ) +(** {!is_ignorable_type} for {!typsig}. *) let is_ignorable_typsig (ts: typsig): bool = (* efficient pattern matching first *) match ts with @@ -82,29 +79,25 @@ let is_ignorable_typsig (ts: typsig): bool = | TSBase t -> is_ignorable_type t | _ -> is_ignorable_attrs (typeSigAttrs ts) +(** {!is_ignorable_type_offset} for {!typsig}. *) let rec is_ignorable_typsig_offset (ts: typsig) (o: _ Offset.t): bool = - (* similar to Cilfacade.typeOffset but we want to check types at all intermediate offsets as well *) if is_ignorable_typsig ts then true (* type at offset so far ignorable, no need to recurse *) else ( - let blendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) - let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in - typeSigAddAttrs contageous - in match o with | `NoOffset -> false (* already checked t *) | `Index (_, o') -> begin match ts with - | TSArray (et, _, attrs) -> - let t' = blendAttributes attrs et in - is_ignorable_typsig_offset t' o' + | TSArray (ets, _, attrs) -> + let ts' = Cilfacade.typeSigBlendAttributes attrs ets in + is_ignorable_typsig_offset ts' o' | _ -> false (* index on non-array *) end | `Field (f, o') -> begin match ts with | TSComp (_, _, attrs) -> - let t' = blendAttributes attrs (typeSig f.ftype) in - is_ignorable_typsig_offset t' o' + let t' = Cilfacade.typeBlendAttributes attrs f.ftype in + is_ignorable_type_offset t' o' (* switch to type because it is more precise with TNamed *) | _ -> false (* field on non-compound *) end ) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index eb7330aa19..6d55211c8d 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -322,6 +322,15 @@ and typeOffset basetyp = | t -> raise (TypeOfError (Field_NonCompound (fi, t))) +let typeBlendAttributes baseAttrs = (* copied from Cilfacade.typeOffset *) + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeAddAttributes contageous + +let typeSigBlendAttributes baseAttrs = + let (_, _, contageous) = partitionAttributes ~default:AttrName baseAttrs in + typeSigAddAttrs contageous + + (** {!Cil.mkCast} using our {!typeOf}. *) let mkCast ~(e: exp) ~(newt: typ) = let oldt = From 01ad122f7d6f0e345d40868a7ecfe253ce57a4a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Sep 2023 17:25:19 +0300 Subject: [PATCH 379/401] Change 05-lval_ls/23-race-null-type-deep to not use now-ignored FILE struct --- .../05-lval_ls/23-race-null-type-deep.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/regression/05-lval_ls/23-race-null-type-deep.c b/tests/regression/05-lval_ls/23-race-null-type-deep.c index 6f29964d1e..f7de758d8f 100644 --- a/tests/regression/05-lval_ls/23-race-null-type-deep.c +++ b/tests/regression/05-lval_ls/23-race-null-type-deep.c @@ -1,9 +1,15 @@ +// PARAM: --disable sem.unknown_function.invalidate.globals --disable sem.unknown_function.spawn #include -#include + +struct s { + int f; +}; + +extern void magic(struct s *p); void *t_fun(void *arg) { void *top; - fclose(top); // RACE + magic(top); // RACE return NULL; } @@ -21,31 +27,31 @@ int main(void) { void *top; switch (r) { case 0: - feof(NULL); // NORACE + magic(NULL); // NORACE break; case 1: - feof(0); // NORACE + magic(0); // NORACE break; case 2: - feof(zero); // NORACE + magic(zero); // NORACE break; case 3: - feof(1); // RACE + magic(1); // RACE break; case 4: - feof(one); // RACE + magic(one); // RACE break; case 5: - feof(r); // RACE + magic(r); // RACE break; case 6: - feof(null); // NORACE + magic(null); // NORACE break; case 7: - feof(unknown); // RACE + magic(unknown); // RACE break; case 8: - feof(top); // RACE + magic(top); // RACE break; default: break; From 391c6ce91678ba93085d6f8081fa4ee1aa54936d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:41:42 +0200 Subject: [PATCH 380/401] Change memset/memcpy count warning from must to may --- 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 49f61fdd1e..605d0f0993 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2096,7 +2096,7 @@ struct | false, false -> if dest_size < eval_n then begin AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n end From fae72563a48b8b7a344395e8f6d75d76630bc8c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:51:05 +0200 Subject: [PATCH 381/401] Use ID.lt to compare dest size with count --- src/analyses/base.ml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 605d0f0993..9e35694ff8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2086,17 +2086,27 @@ struct let cwe_number = 823 in let dest_size = get_min_size_of_dest ctx dest in let eval_n = ctx.ask (Queries.EvalInt n) in - match ValueDomainQueries.ID.is_top dest_size, ValueDomainQueries.ID.is_top eval_n with - | true, _ -> + match dest_size, eval_n with + | `Top, _ -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, true -> + | _, `Top -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name - | false, false -> - if dest_size < eval_n then begin - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + | `Bot, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Bot -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name + | `Lifted ds, `Lifted en -> + begin match ID.to_bool (ID.lt ds en) with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty ds ID.pretty en fun_name end From 655362e70a7d0c2a42c4dba910e5c67de1aee1c9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 16:53:39 +0200 Subject: [PATCH 382/401] Use join to combine all points-to elements sizes --- src/analyses/base.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9e35694ff8..22fce826f5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2072,9 +2072,7 @@ struct begin match pts_sizes with | [] -> `Bot | [x] -> x - | x::xs -> List.fold_left (fun acc elem -> - if ValueDomainQueries.ID.compare acc elem >= 0 then elem else acc - ) x xs + | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; From 2534945eec78d99658cc2bf5b420af35580b7fe3 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:03:45 +0200 Subject: [PATCH 383/401] Do not ingore offsets when calling BlobSize for memset/memcpy --- src/analyses/base.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 22fce826f5..255f82cb99 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2041,8 +2041,7 @@ struct | _ -> false in if points_to_heap_only then - (* Ask for BlobSize from the base address (the second field set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize {exp = dest; base_address = true}) + ctx.ask (Queries.BlobSize {exp = dest; base_address = false}) else match ctx.ask (Queries.MayPointTo dest) with | a when not (Queries.AD.is_top a) -> From 033913b760c0220d7057c830c3bfe2bc3a964069 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:05:39 +0200 Subject: [PATCH 384/401] Change name to get_size_of_dest which makes more sense --- src/analyses/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 255f82cb99..d510389338 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,8 +2026,8 @@ struct 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 - (* Get the size of the smallest memory that dest points-to *) - let get_min_size_of_dest ctx dest = + (* Get the size of the memory that dest points-to *) + let get_size_of_dest ctx dest = let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) in From 4ec98951e641b5862b1caebd88514e3bd4a064f0 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:20:41 +0200 Subject: [PATCH 385/401] Add exception handling for ID operations --- src/analyses/base.ml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d510389338..01fb24d862 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2054,7 +2054,11 @@ struct let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength dest) with - | `Lifted arr_len -> `Lifted (ID.mul item_typ_size_in_bytes arr_len) + | `Lifted arr_len -> + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Bot -> `Bot | `Top -> `Top end @@ -2096,7 +2100,13 @@ struct | _, `Bot -> M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> - begin match ID.to_bool (ID.lt ds en) with + let dest_size_lt_count = + begin + try ID.lt ds en + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n From 1e69b64cbf7b3d93c92129e61a1173340d94125d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Fri, 29 Sep 2023 17:21:10 +0200 Subject: [PATCH 386/401] Fix wrong name use --- 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 01fb24d862..26959e6aa3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2085,7 +2085,7 @@ struct let check_count ctx fun_name dest n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let dest_size = get_min_size_of_dest ctx dest in + let dest_size = get_size_of_dest ctx dest in let eval_n = ctx.ask (Queries.EvalInt n) in match dest_size, eval_n with | `Top, _ -> From 49dcac98eac518e8bd2070b542e05f1730c48970 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 30 Sep 2023 00:38:35 +0200 Subject: [PATCH 387/401] Fix incompatible ikinds --- src/analyses/base.ml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 26959e6aa3..d56d6653c6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2055,8 +2055,9 @@ struct let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength dest) with | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in begin - try `Lifted (ID.mul item_typ_size_in_bytes arr_len) + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) with IntDomain.ArithmeticOnIntegerBot _ -> `Bot end | `Bot -> `Bot @@ -2100,9 +2101,11 @@ struct | _, `Bot -> M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name | `Lifted ds, `Lifted en -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in let dest_size_lt_count = begin - try ID.lt ds en + try ID.lt casted_ds casted_en with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end in From 87dadd265c5a8af8f8087307ef31aa41de51ff56 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 30 Sep 2023 23:43:27 +0200 Subject: [PATCH 388/401] Add more and more sophisticated memset and memcpy tests --- .../77-mem-oob/06-memset-memcpy-oob.c | 21 -------- tests/regression/77-mem-oob/06-memset-oob.c | 54 +++++++++++++++++++ tests/regression/77-mem-oob/07-memcpy-oob.c | 53 ++++++++++++++++++ 3 files changed, 107 insertions(+), 21 deletions(-) delete mode 100644 tests/regression/77-mem-oob/06-memset-memcpy-oob.c create mode 100644 tests/regression/77-mem-oob/06-memset-oob.c create mode 100644 tests/regression/77-mem-oob/07-memcpy-oob.c diff --git a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c b/tests/regression/77-mem-oob/06-memset-memcpy-oob.c deleted file mode 100644 index 1050c199e0..0000000000 --- a/tests/regression/77-mem-oob/06-memset-memcpy-oob.c +++ /dev/null @@ -1,21 +0,0 @@ -// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info -// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed -#include -#include - -int main(int argc, char const *argv[]) { - int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int - int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int - - memset(a, 0, 40); //NOWARN - memset(a, 0, 10 * sizeof(int)); //NOWARN - memset(a, 0, 41); //WARN - memset(a, 0, 40000000); //WARN - - memcpy(a, b, 40); //NOWARN - memcpy(a, b, 10 * sizeof(int)); //NOWARN - memcpy(a, b, 41); //WARN - memcpy(a, b, 40000000); //WARN - memcpy(a, b, 15 * sizeof(int)); //WARN - return 0; -} diff --git a/tests/regression/77-mem-oob/06-memset-oob.c b/tests/regression/77-mem-oob/06-memset-oob.c new file mode 100644 index 0000000000..931f7eaa8c --- /dev/null +++ b/tests/regression/77-mem-oob/06-memset-oob.c @@ -0,0 +1,54 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include +#include + +typedef struct s { + int a; + char b; +} s; + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memset(a, 0, 10 * sizeof(int)); //NOWARN + memset(a, 0, 41); //WARN + memset(a, 0, 40000000); //WARN + + int d; + + if (argc == 15) { + int c = 55; + a = &c; + memset(a, 0, argv[5]); //WARN + } else if (argv[2] == 2) { + a = &d; + } + + memset(a, 0, 40); //WARN + + int input; + scanf("%d", &input); + memset(a, 0, input); //WARN + + + + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + memset(b, 0, 60); //NOWARN + b += 1; + memset(b, 0, 60); //WARN + + + + s *s_ptr = malloc(sizeof(s)); + memset(s_ptr, 0, sizeof(s)); //NOWARN + memset(s_ptr->a, 0, sizeof(s)); //WARN + memset(s_ptr->b, 0, sizeof(s)); //WARN + + s_ptr = s_ptr->a; + memset(s_ptr, 0, sizeof(s)); //WARN + + return 0; +} diff --git a/tests/regression/77-mem-oob/07-memcpy-oob.c b/tests/regression/77-mem-oob/07-memcpy-oob.c new file mode 100644 index 0000000000..012f92996e --- /dev/null +++ b/tests/regression/77-mem-oob/07-memcpy-oob.c @@ -0,0 +1,53 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +typedef struct s { + int a; + char b; +} s; + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memcpy(a, b, 40); //NOWARN + memcpy(a, b, 10 * sizeof(int)); //NOWARN + memcpy(a, b, 41); //WARN + memcpy(a, b, 40000000); //WARN + memcpy(a, b, 15 * sizeof(int)); //WARN + + int d; + + if (*argv == 42) { + a = &d; + } else if (*(argv + 5)) { + int random = rand(); + a = &random; + memcpy(a, b, 40); //WARN + } + + memcpy(a, b, 40); //WARN + memcpy(a, b, sizeof(a)); //WARN + + memcpy(b, a, 60); //NOWARN + b += 1; + memcpy(b, a, 60); //WARN + + + s *s_ptr = malloc(sizeof(s)); + memcpy(s_ptr, a, sizeof(s)); //NOWARN + memcpy(s_ptr->a, 0, sizeof(s)); //WARN + memcpy(s_ptr->b, 0, sizeof(s)); //WARN + + memcpy(s_ptr, a, 40); //WARN + memcpy(s_ptr, a, 60); //WARN + memcpy(s_ptr, b, 40); //WARN + memcpy(s_ptr, b, 60); //WARN + + s_ptr = s_ptr->b; + memcpy(s_ptr, a, sizeof(s)); //WARN + + return 0; +} From 9e9b5e35d395a6b8e79fb186d9c5fcea68ca8db9 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 00:14:58 +0200 Subject: [PATCH 389/401] Add memset/memcpy test case with arrays --- .../77-mem-oob/08-memset-memcpy-array.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/regression/77-mem-oob/08-memset-memcpy-array.c diff --git a/tests/regression/77-mem-oob/08-memset-memcpy-array.c b/tests/regression/77-mem-oob/08-memset-memcpy-array.c new file mode 100644 index 0000000000..f231ba2dc4 --- /dev/null +++ b/tests/regression/77-mem-oob/08-memset-memcpy-array.c @@ -0,0 +1,43 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +int main(int argc, char const *argv[]) { + int arr[42]; // Size should be 168 bytes (with 4 byte ints) + int *b = arr; + + + memset(b, 0, 168); //NOWARN + memset(b, 0, sizeof(arr)); //NOWARN + memset(b, 0, 169); //WARN + memset(b, 0, sizeof(arr) + 1); //WARN + + int *c = malloc(sizeof(arr)); // Size should be 168 bytes (with 4 byte ints) + memcpy(b, c, 168); //NOWARN + memcpy(b, c, sizeof(arr)); //NOWARN + memcpy(b, c, 169); //WARN + memcpy(b, c, sizeof(arr) + 1); //WARN + + int d; + + if (*argv == 42) { + b = &d; + memset(b, 0, 168); //WARN + memcpy(b, c, 168); //WARN + } else if (*(argv + 5)) { + int random = rand(); + b = &random; + memset(b, 0, 168); //WARN + memcpy(b, c, 168); //WARN + } + + memset(b, 0, sizeof(arr)); //WARN + memcpy(b, c, sizeof(arr)); //WARN + memset(b, 0, sizeof(int)); //NOWARN + memcpy(b, c, sizeof(int)); //NOWARN + memset(b, 0, sizeof(int) + 1); //WARN + memcpy(b, c, sizeof(int) + 1); //WARN + + return 0; +} From 5ca357d3ee1412ef0d9907b966b732fda99cb927 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:14:55 +0200 Subject: [PATCH 390/401] Fix some bugs in memOutOfBounds and move memset/memcpy checks there --- src/analyses/base.ml | 97 ------------------- src/analyses/memOutOfBounds.ml | 170 ++++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 140 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index d56d6653c6..57c32fa3bc 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2026,99 +2026,6 @@ struct 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 - (* Get the size of the memory that dest points-to *) - let get_size_of_dest ctx dest = - let intdom_of_int x = - ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) - in - let points_to_heap_only = - match ctx.ask (Queries.MayPointTo dest) with - | a when not (AD.is_top a) -> - AD.for_all (function - | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) - | _ -> false - ) a - | _ -> false - in - if points_to_heap_only then - ctx.ask (Queries.BlobSize {exp = dest; base_address = false}) - else - match ctx.ask (Queries.MayPointTo dest) with - | a when not (Queries.AD.is_top a) -> - let pts_list = Queries.AD.elements a in - let pts_elems_to_sizes (addr: Queries.AD.elt) = - begin match addr with - | Addr (v, _) -> - begin match v.vtype with - | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in - begin match ctx.ask (Queries.EvalLength dest) with - | `Lifted arr_len -> - let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in - begin - try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) - with IntDomain.ArithmeticOnIntegerBot _ -> `Bot - end - | `Bot -> `Bot - | `Top -> `Top - end - | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) - end - | _ -> `Top - end - in - (* Map each points-to-set element to its size *) - let pts_sizes = List.map pts_elems_to_sizes pts_list in - (* Take the smallest of all sizes that ptr's contents may have *) - begin match pts_sizes with - | [] -> `Bot - | [x] -> x - | x::xs -> List.fold_left ValueDomainQueries.ID.join x xs - end - | _ -> - M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp dest; - `Top - - (* Used for memset() and memcpy() out-of-bounds checks *) - let check_count ctx fun_name dest n = - let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in - let cwe_number = 823 in - let dest_size = get_size_of_dest ctx dest in - let eval_n = ctx.ask (Queries.EvalInt n) in - match dest_size, eval_n with - | `Top, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, `Top -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name - | `Bot, _ -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name - | _, `Bot -> - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name - | `Lifted ds, `Lifted en -> - let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in - let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in - let dest_size_lt_count = - begin - try ID.lt casted_ds casted_en - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in - begin match ID.to_bool dest_size_lt_count with - | Some true -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ValueDomainQueries.ID.pretty dest_size ValueDomainQueries.ID.pretty eval_n - | Some false -> () - | None -> - AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty ds ID.pretty en fun_name - end - let special ctx (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with @@ -2187,8 +2094,6 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> - (* Check count *) - check_count ctx f.vname dest count; let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = @@ -2206,8 +2111,6 @@ struct let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value | Memcpy { dest = dst; src; n; }, _ -> - (* Check n *) - check_count ctx f.vname dst n; memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 94c16e9c94..ae0faedb9a 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -6,6 +6,7 @@ open MessageCategory module AS = AnalysisState module VDQ = ValueDomainQueries +module ID = IntDomain.IntDomTuple (* Note: @@ -27,11 +28,11 @@ struct (* HELPER FUNCTIONS *) let intdom_of_int x = - IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) let to_index ?typ offs = let idx_of_int x = - IntDomain.IntDomTuple.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) + ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) in let rec offset_to_index_offset ?typ offs = match offs with | `NoOffset -> idx_of_int 0 @@ -40,7 +41,7 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bits_offset = idx_of_int bits_offset in let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - IntDomain.IntDomTuple.add bits_offset remaining_offset + ID.add bits_offset remaining_offset | `Index (x, o) -> let (item_typ, item_size_in_bits) = match Option.map unrollType typ with @@ -48,11 +49,11 @@ struct let item_size_in_bits = bitsSizeOf item_typ in (Some item_typ, idx_of_int item_size_in_bits) | _ -> - (None, IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind ()) + (None, ID.top_of @@ Cilfacade.ptrdiff_ikind ()) in - let bits_offset = IntDomain.IntDomTuple.mul item_size_in_bits x in + let bits_offset = ID.mul item_size_in_bits x in let remaining_offset = offset_to_index_offset ?typ:item_typ o in - IntDomain.IntDomTuple.add bits_offset remaining_offset + ID.add bits_offset remaining_offset in offset_to_index_offset ?typ offs @@ -115,30 +116,33 @@ struct let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in begin match ctx.ask (Queries.EvalLength ptr) with - | `Lifted arr_len -> `Lifted (IntDomain.IntDomTuple.mul item_typ_size_in_bytes arr_len) - | `Bot -> VDQ.ID.bot () - | `Top -> VDQ.ID.top () + | `Lifted arr_len -> + let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in + begin + try `Lifted (ID.mul item_typ_size_in_bytes arr_len_casted) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end + | `Bot -> `Bot + | `Top -> `Top end | _ -> let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in `Lifted (intdom_of_int type_size_in_bytes) end - | _ -> VDQ.ID.top () + | _ -> `Top end in (* Map each points-to-set element to its size *) let pts_sizes = List.map pts_elems_to_sizes pts_list in (* Take the smallest of all sizes that ptr's contents may have *) begin match pts_sizes with - | [] -> VDQ.ID.bot () + | [] -> `Bot | [x] -> x - | x::xs -> List.fold_left (fun acc elem -> - if VDQ.ID.compare acc elem >= 0 then elem else acc - ) x xs + | x::xs -> List.fold_left VDQ.ID.join x xs end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - VDQ.ID.top () + `Top let get_ptr_deref_type ptr_typ = match ptr_typ with @@ -155,7 +159,7 @@ struct let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with - | `Lifted i -> `Lifted (IntDomain.IntDomTuple.mul i ptr_contents_typ_size_in_bytes) + | `Lifted i -> `Lifted (ID.mul i ptr_contents_typ_size_in_bytes) | `Top -> `Top | `Bot -> `Bot @@ -167,12 +171,12 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bytes_offset = intdom_of_int (bits_offset / 8) in let remaining_offset = offs_to_idx field.ftype o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + ID.add bytes_offset remaining_offset | `Index (x, o) -> let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = IntDomain.IntDomTuple.mul typ_size_in_bytes x in + let bytes_offset = ID.mul typ_size_in_bytes x in let remaining_offset = offs_to_idx typ o in - IntDomain.IntDomTuple.add bytes_offset remaining_offset + ID.add bytes_offset remaining_offset let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with @@ -183,17 +187,17 @@ struct begin match VDQ.AD.is_empty a with | true -> M.warn "Pointer %a has an empty points-to-set" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () | false -> if VDQ.AD.exists (function - | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( (* TODO: Uncomment once staging-memsafety branch changes are applied *) (* set_mem_safety_flag InvalidDeref; *) M.warn "Pointer %a has a bot address offset. An invalid memory access may occur" d_exp ptr ) else if VDQ.AD.exists (function - | Addr (_, o) -> IntDomain.IntDomTuple.is_bot @@ offs_to_idx t o + | Addr (_, o) -> ID.is_bot @@ offs_to_idx t o | _ -> false ) a then ( (* TODO: Uncomment once staging-memsafety branch changes are applied *) @@ -204,16 +208,16 @@ struct (* Hence, we can just pick one element and obtain its offset *) begin match VDQ.AD.choose a with | Addr (_, o) -> offs_to_idx t o - | _ -> IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () end end | None -> M.error "Expression %a doesn't have pointer type" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () end | _ -> M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; - IntDomain.IntDomTuple.top_of @@ Cilfacade.ptrdiff_ikind () + ID.top_of @@ Cilfacade.ptrdiff_ikind () and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = if not @@ lval_contains_a_ptr lval then () @@ -242,15 +246,30 @@ struct let ptr_contents_type = get_ptr_deref_type ptr_type in match ptr_contents_type with | Some t -> - begin match VDQ.ID.is_top ptr_size with - | true -> + begin match ptr_size, addr_offs with + | `Top, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a not known. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp - | false -> - let offs = `Lifted addr_offs in - if ptr_size < offs then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" VDQ.ID.pretty ptr_size VDQ.ID.pretty offs + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is top. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | `Bot, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a is bot. Memory out-of-bounds access might occur due to pointer arithmetic" d_exp lval_exp + | `Lifted ps, ao -> + let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ao in + let ptr_size_lt_offs = + begin + try ID.lt casted_ps casted_ao + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao end end | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp @@ -296,27 +315,87 @@ struct let offset_size = eval_ptr_offset_in_binop ctx e2 t in (* Make sure to add the address offset to the binop offset *) let offset_size_with_addr_size = match offset_size with - | `Lifted os -> `Lifted (IntDomain.IntDomTuple.add os addr_offs) + | `Lifted os -> + let casted_os = ID.cast_to (Cilfacade.ptrdiff_ikind ()) os in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in + `Lifted (ID.add casted_os casted_ao) | `Top -> `Top | `Bot -> `Bot in - begin match VDQ.ID.is_top ptr_size, VDQ.ID.is_top offset_size_with_addr_size with - | true, _ -> + begin match ptr_size, offset_size_with_addr_size with + | `Top, _ -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is top. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, `Top -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a not known. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp - | _, true -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is top. Memory out-of-bounds access might occur" d_exp binopexp + | `Bot, _ -> AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a not known. Memory out-of-bounds access might occur" d_exp binopexp - | false, false -> - if ptr_size < offset_size_with_addr_size then begin - AS.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp VDQ.ID.pretty ptr_size VDQ.ID.pretty offset_size_with_addr_size + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer %a in expression %a is bottom. Memory out-of-bounds access might occur" d_exp e1 d_exp binopexp + | _, `Bot -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Operand value for pointer arithmetic in expression %a is bottom. Memory out-of-bounds access might occur" d_exp binopexp + | `Lifted ps, `Lifted o -> + let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in + let casted_o = ID.cast_to (Cilfacade.ptrdiff_ikind ()) o in + let ptr_size_lt_offs = + begin + try ID.lt casted_ps casted_o + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool ptr_size_lt_offs with + | Some true -> + AS.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o end end | _ -> M.error "Binary expression %a doesn't have a pointer" d_exp binopexp end | _ -> () + let check_count ctx fun_name dest n = + let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in + let cwe_number = 823 in + let dest_size = get_size_of_ptr_target ctx dest in + let eval_n = ctx.ask (Queries.EvalInt n) in + let addr_offs = get_addr_offs ctx dest in + match dest_size, eval_n with + | `Top, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is unknown. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Top -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is unknown. Memory out-of-bounds access might occur" fun_name + | `Bot, _ -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest %a in function %s is bottom. Memory out-of-bounds access might occur" d_exp dest fun_name + | _, `Bot -> + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Count parameter, passed to function %s is bottom" fun_name + | `Lifted ds, `Lifted en -> + let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in + let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in + let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in + let dest_size_lt_count = + begin + try ID.lt casted_ds (ID.add casted_en casted_ao) + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end + in + begin match ID.to_bool dest_size_lt_count with + | Some true -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + | Some false -> () + | None -> + AnalysisState.svcomp_may_invalid_deref := true; + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of dest (%a) with address offset (%a) count (%a) in function %s. Memory out-of-bounds access may occur" ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en fun_name + end + (* TRANSFER FUNCTIONS *) @@ -344,6 +423,11 @@ struct in Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; List.iter (fun arg -> check_exp_for_oob_access ctx ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; + (* Check calls to memset and memcpy for out-of-bounds-accesses *) + match desc.special arglist with + | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; + | Memcpy { dest; src; n = count; } -> check_count ctx f.vname dest count; + | _ -> (); ctx.local let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = From 8bf3705240f1ae479f71f4eaf9346c87768d856b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:16:14 +0200 Subject: [PATCH 391/401] Remove unused function --- src/analyses/memOutOfBounds.ml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index ae0faedb9a..f9a0439333 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -30,33 +30,6 @@ struct let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) - let to_index ?typ offs = - let idx_of_int x = - ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int (x / 8)) - in - let rec offset_to_index_offset ?typ offs = match offs with - | `NoOffset -> idx_of_int 0 - | `Field (field, o) -> - let field_as_offset = Field (field, NoOffset) in - let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in - let bits_offset = idx_of_int bits_offset in - let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - ID.add bits_offset remaining_offset - | `Index (x, o) -> - let (item_typ, item_size_in_bits) = - match Option.map unrollType typ with - | Some TArray(item_typ, _, _) -> - let item_size_in_bits = bitsSizeOf item_typ in - (Some item_typ, idx_of_int item_size_in_bits) - | _ -> - (None, ID.top_of @@ Cilfacade.ptrdiff_ikind ()) - in - let bits_offset = ID.mul item_size_in_bits x in - let remaining_offset = offset_to_index_offset ?typ:item_typ o in - ID.add bits_offset remaining_offset - in - offset_to_index_offset ?typ offs - let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ From 30279106115eed26c94a2b84bc5cf14d153c3400 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:42:51 +0200 Subject: [PATCH 392/401] Add further exception handling --- src/analyses/memOutOfBounds.ml | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index f9a0439333..f2b2780f58 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -128,11 +128,14 @@ struct let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = let eval_offset = ctx.ask (Queries.EvalInt exp) in - let eval_offset = Option.get @@ VDQ.ID.to_int eval_offset in - let eval_offset = VDQ.ID.of_int (Cilfacade.ptrdiff_ikind ()) eval_offset in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with - | `Lifted i -> `Lifted (ID.mul i ptr_contents_typ_size_in_bytes) + | `Lifted eo -> + let casted_eo = ID.cast_to (Cilfacade.ptrdiff_ikind ()) eo in + begin + try `Lifted (ID.mul casted_eo ptr_contents_typ_size_in_bytes) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot @@ -144,12 +147,18 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bytes_offset = intdom_of_int (bits_offset / 8) in let remaining_offset = offs_to_idx field.ftype o in - ID.add bytes_offset remaining_offset + begin + try ID.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end | `Index (x, o) -> - let typ_size_in_bytes = size_of_type_in_bytes typ in - let bytes_offset = ID.mul typ_size_in_bytes x in - let remaining_offset = offs_to_idx typ o in - ID.add bytes_offset remaining_offset + begin try + let typ_size_in_bytes = size_of_type_in_bytes typ in + let bytes_offset = ID.mul typ_size_in_bytes x in + let remaining_offset = offs_to_idx typ o in + ID.add bytes_offset remaining_offset + with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () + end let rec get_addr_offs ctx ptr = match ctx.ask (Queries.MayPointTo ptr) with @@ -291,7 +300,10 @@ struct | `Lifted os -> let casted_os = ID.cast_to (Cilfacade.ptrdiff_ikind ()) os in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in - `Lifted (ID.add casted_os casted_ao) + begin + try `Lifted (ID.add casted_os casted_ao) + with IntDomain.ArithmeticOnIntegerBot _ -> `Bot + end | `Top -> `Top | `Bot -> `Bot in @@ -331,6 +343,7 @@ struct end | _ -> () + (* For memset() and memcpy() *) let check_count ctx fun_name dest n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in From 62b20a2839a4df6b24b1e2908893e51daa2e2e7b Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:43:49 +0200 Subject: [PATCH 393/401] Add further memset/memcpy test --- .../77-mem-oob/09-memset-memcpy-addr-offs.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c diff --git a/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c b/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c new file mode 100644 index 0000000000..725024946e --- /dev/null +++ b/tests/regression/77-mem-oob/09-memset-memcpy-addr-offs.c @@ -0,0 +1,20 @@ +// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval --disable warn.info +// TODO: The "--disable warn.info" part is a temporary fix and needs to be removed once the MacOS CI job is fixed +#include +#include + +int main(int argc, char const *argv[]) { + int *a = malloc(10 * sizeof(int)); //Size is 40 bytes, assuming a 4-byte int + int *b = malloc(15 * sizeof(int)); //Size is 60 bytes, assuming a 4-byte int + + memset(a, 0, 40); //NOWARN + memcpy(a, b, 40); //NOWARN + + a += 3; + + memset(a, 0, 40); //WARN + memcpy(a, b, 40); //WARN + + memset(a, 0, 37); //NOWARN + memcpy(a, b, 37); //NOWARN +} \ No newline at end of file From 5e683d45c059370eb2fd496073378bcb6232b015 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 17:46:14 +0200 Subject: [PATCH 394/401] Fix comment --- src/analyses/memOutOfBounds.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index f2b2780f58..fe4b854923 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -375,7 +375,7 @@ struct begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; - M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access may occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en + M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of dest in function %s is %a (in bytes) with an address offset of %a (in bytes). Count is %a (in bytes). Memory out-of-bounds access must occur" fun_name ID.pretty casted_ds ID.pretty casted_ao ID.pretty casted_en | Some false -> () | None -> AnalysisState.svcomp_may_invalid_deref := true; From f03f81ff2ba28715e677c49507b2edea168f3b37 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 18:16:00 +0200 Subject: [PATCH 395/401] Remove some bot checks for ID arithmetic --- src/analyses/memOutOfBounds.ml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index fe4b854923..45048acc93 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -238,12 +238,7 @@ struct | `Lifted ps, ao -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ao in - let ptr_size_lt_offs = - begin - try ID.lt casted_ps casted_ao - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let ptr_size_lt_offs = ID.lt casted_ps casted_ao in begin match ID.to_bool ptr_size_lt_offs with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; @@ -323,12 +318,7 @@ struct | `Lifted ps, `Lifted o -> let casted_ps = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ps in let casted_o = ID.cast_to (Cilfacade.ptrdiff_ikind ()) o in - let ptr_size_lt_offs = - begin - try ID.lt casted_ps casted_o - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let ptr_size_lt_offs = ID.lt casted_ps casted_o in begin match ID.to_bool ptr_size_lt_offs with | Some true -> AS.svcomp_may_invalid_deref := true; @@ -366,12 +356,7 @@ struct let casted_ds = ID.cast_to (Cilfacade.ptrdiff_ikind ()) ds in let casted_en = ID.cast_to (Cilfacade.ptrdiff_ikind ()) en in let casted_ao = ID.cast_to (Cilfacade.ptrdiff_ikind ()) addr_offs in - let dest_size_lt_count = - begin - try ID.lt casted_ds (ID.add casted_en casted_ao) - with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () - end - in + let dest_size_lt_count = ID.lt casted_ds (ID.add casted_en casted_ao) in begin match ID.to_bool dest_size_lt_count with | Some true -> AnalysisState.svcomp_may_invalid_deref := true; From 3da92055ae48f55f827aed5e4f8eae0b2102e009 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 18:33:38 +0200 Subject: [PATCH 396/401] Use size_of_type_in_bytes where possible --- src/analyses/memOutOfBounds.ml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 45048acc93..7015e6f143 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -30,6 +30,10 @@ struct let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) + let size_of_type_in_bytes typ = + let typ_size_in_bytes = (bitsSizeOf typ) / 8 in + intdom_of_int typ_size_in_bytes + let rec exp_contains_a_ptr (exp:exp) = match exp with | Const _ @@ -86,8 +90,7 @@ struct | Addr (v, _) -> begin match v.vtype with | TArray (item_typ, _, _) -> - let item_typ_size_in_bytes = (bitsSizeOf item_typ) / 8 in - let item_typ_size_in_bytes = intdom_of_int item_typ_size_in_bytes in + let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in begin match ctx.ask (Queries.EvalLength ptr) with | `Lifted arr_len -> let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in @@ -99,8 +102,8 @@ struct | `Top -> `Top end | _ -> - let type_size_in_bytes = (bitsSizeOf v.vtype) / 8 in - `Lifted (intdom_of_int type_size_in_bytes) + let type_size_in_bytes = size_of_type_in_bytes v.vtype in + `Lifted type_size_in_bytes end | _ -> `Top end @@ -122,10 +125,6 @@ struct | TPtr (t, _) -> Some t | _ -> None - let size_of_type_in_bytes typ = - let typ_size_in_bytes = (bitsSizeOf typ) / 8 in - intdom_of_int typ_size_in_bytes - let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = let eval_offset = ctx.ask (Queries.EvalInt exp) in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in From 89abb1a9ade5352f19fc2c042c4ef2b387c5fa38 Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sun, 1 Oct 2023 20:56:16 +0200 Subject: [PATCH 397/401] Keep TODO about count of memset in base.ml --- src/analyses/base.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 57c32fa3bc..232ee36d68 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2094,6 +2094,7 @@ struct in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> + (* TODO: check count *) let eval_ch = eval_rv (Analyses.ask_of_ctx ctx) gs st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = From 15e6c3d636bde630a5df20d31af2c812355b68a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 10:37:21 +0300 Subject: [PATCH 398/401] Simplify is_not_heap_alloc_var and add TODO --- src/analyses/base.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6442ed4f8a..ea595ad96d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1123,7 +1123,8 @@ struct (* interpreter end *) let is_not_heap_alloc_var ctx v = - (not (ctx.ask (Queries.IsAllocVar v))) || (ctx.ask (Queries.IsAllocVar v) && not (ctx.ask (Queries.IsHeapVar v))) + let is_alloc = ctx.ask (Queries.IsAllocVar v) in + not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = let cpa = ctx.local.BaseDomain.cpa in @@ -2114,7 +2115,7 @@ struct let dest_a, dest_typ = addr_type_of_exp dest in let value = VD.zero_init_value dest_typ in set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ value - | Memcpy { dest = dst; src; n; }, _ -> + | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) memory_copying dst src (* strcpy(dest, src); *) | Strcpy { dest = dst; src; n = None }, _ -> From 7857a683163de4cc6e1df31f4b25dd80a99b2189 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 16:34:03 +0300 Subject: [PATCH 399/401] Fix incorrect unlock in witness/tm-inv-transfer tests --- .../regression/56-witness/60-tm-inv-transfer-protection.c | 6 +++--- tests/regression/56-witness/61-tm-inv-transfer-mine.c | 8 ++++---- .../56-witness/62-tm-inv-transfer-protection-witness.c | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/regression/56-witness/60-tm-inv-transfer-protection.c b/tests/regression/56-witness/60-tm-inv-transfer-protection.c index 3d5bcbc871..07260adbdd 100644 --- a/tests/regression/56-witness/60-tm-inv-transfer-protection.c +++ b/tests/regression/56-witness/60-tm-inv-transfer-protection.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); // UNKNOWN (lacks expressivity) pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); __goblint_check(g >= 40); __goblint_check(g <= 42); // UNKNOWN (widen) pthread_mutex_unlock(&C); - + return 0; } diff --git a/tests/regression/56-witness/61-tm-inv-transfer-mine.c b/tests/regression/56-witness/61-tm-inv-transfer-mine.c index 8f912bc2d9..cd8301fb39 100644 --- a/tests/regression/56-witness/61-tm-inv-transfer-mine.c +++ b/tests/regression/56-witness/61-tm-inv-transfer-mine.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); - __goblint_check(g >= 40); + __goblint_check(g >= 40); // TODO why? __goblint_check(g <= 42); pthread_mutex_unlock(&C); - + return 0; } \ No newline at end of file diff --git a/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c b/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c index 7be5bcf53e..68aada7394 100644 --- a/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c +++ b/tests/regression/56-witness/62-tm-inv-transfer-protection-witness.c @@ -35,12 +35,12 @@ int main(void) { __goblint_check(g >= 40); __goblint_check(g <= 41); // UNKNOWN (lacks expressivity) pthread_mutex_unlock(&C); - pthread_mutex_unlock(&C); - + pthread_mutex_unlock(&B); + pthread_mutex_lock(&C); __goblint_check(g >= 40); __goblint_check(g <= 42); pthread_mutex_unlock(&C); - + return 0; } \ No newline at end of file From 6c8a05b223858413b2ca695473f9c0a9ada9eb09 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 16:39:40 +0300 Subject: [PATCH 400/401] Revert "Disable pins for v2.2.0 release" This reverts commit b46aeda9eaecd3ec42ec14c977d1550faad0de23. --- goblint.opam | 2 +- goblint.opam.locked | 5 +++++ goblint.opam.template | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/goblint.opam b/goblint.opam index c84e7a56f0..d019379dd4 100644 --- a/goblint.opam +++ b/goblint.opam @@ -77,7 +77,7 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # 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 8556e9eebb..ebe024dadd 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,9 +128,14 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] + [ + "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 d9e1ebf477..a493861e96 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -4,7 +4,7 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.2" "git+https://github.com/goblint/cil.git#398dca3d94a06a9026b3737aabf100ee3498229f" ] # 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 7e57562220e049434db9af6a095c7da1312b4618 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Oct 2023 17:05:18 +0300 Subject: [PATCH 401/401] Add final message for exp.single-threaded --- src/analyses/mCP.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 1b6a7e5a1d..9eb21b77ef 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -144,7 +144,9 @@ struct let spawn_one v d = List.iter (fun (lval, args) -> ctx.spawn lval v args) d in - if not (get_bool "exp.single-threaded") then + if get_bool "exp.single-threaded" then + M.msg_final Error ~category:Unsound "Thread not spawned" + else iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs let do_sideg ctx (xs:(V.t * (WideningTokens.TS.t * G.t)) list) =