From 96c777d9325641d7d70248769d3ea4ec719a7daf Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 24 Jan 2023 08:45:41 +0100 Subject: [PATCH 01/52] 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 02/52] 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 03/52] 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 04/52] 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 05/52] 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 06/52] 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 07/52] 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 08/52] 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 09/52] 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 10/52] 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 11/52] 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 12/52] 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 13/52] 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 14/52] 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 15/52] 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 16/52] 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 17/52] 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 18/52] 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 a42b9645785acc38d7aa93fb9757230368c627c0 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 13 Jun 2023 14:03:04 +0300 Subject: [PATCH 19/52] 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 20/52] 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 21/52] 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 22/52] 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 23/52] 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 24/52] 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 511fd366c0181999023f57e0e8a592d5f745f319 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Tue, 27 Jun 2023 16:32:03 +0200 Subject: [PATCH 25/52] changed the invalidation of equivalences to use VarEq.may_change --- src/analyses/tmpSpecial.ml | 92 ++++++++++---------------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index a9b65189b2..9b88ab0317 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -1,5 +1,7 @@ (* Analysis that tracks which variables hold the results of calls to math library functions. - For each equivalence a set of lvals is tracked, that contains all lvals on which the arguments of the corresponding call depend, so an equivalence can be removed if one of the lvals is written.*) + For each equivalence a set of expressions is tracked, that contains the arguments of the corresponding call as well as the Lval it is assigned to, so an equivalence can be removed if one of these expressions may be changed.*) + +module VarEq = VarEq.Spec open GoblintCil open Analyses @@ -10,9 +12,9 @@ struct let name () = "tmpSpecial" module ML = LibraryDesc.MathLifted - module LS = SetDomain.ToppedSet(Lval.CilLval) (struct let topname = "All" end) - module MlLsProd = Lattice.Prod (ML) (LS) - module D = MapDomain.MapBot (Lval.CilLval) (MlLsProd) + module Deps = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) + module MLDeps = Lattice.Prod (ML) (Deps) + module D = MapDomain.MapBot (Lval.CilLval) (MLDeps) module C = Lattice.Unit let rec resolve (offs : offset) : (CilType.Fieldinfo.t, Basetype.CilExp.t) Lval.offs = @@ -20,40 +22,17 @@ struct | NoOffset -> `NoOffset | Field (f_info, f_offs) -> `Field (f_info, (resolve f_offs)) | Index (i_exp, i_offs) -> `Index (i_exp, (resolve i_offs)) - - let ls_of_lv ctx (lval:lval) : LS.t = - match lval with - | (Var v, offs) -> LS.of_list [(v, resolve offs)] - | (Mem e, _) -> (ctx.ask (Queries.MayPointTo e)) - - let rec ls_of_exp ctx (exp:exp) : LS.t = - match exp with - | Const _ -> LS.empty () - | Lval lv -> ls_of_lv ctx lv - | SizeOf _ -> LS.empty () - | Real e -> ls_of_exp ctx e - | Imag e -> ls_of_exp ctx e - | SizeOfE e -> ls_of_exp ctx e - | SizeOfStr _ -> LS.empty () - | AlignOf _ -> LS.empty () - | AlignOfE e -> ls_of_exp ctx e - | UnOp (_,e,_) -> ls_of_exp ctx e - | BinOp (_,e1,e2,_) -> LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2) - | Question (q,e1,e2,_) -> LS.union (ls_of_exp ctx q) (LS.union (ls_of_exp ctx e1) (ls_of_exp ctx e2)) - | CastE (_,e) -> ls_of_exp ctx e - | AddrOf _ -> ctx.ask (Queries.MayPointTo exp) - | AddrOfLabel _ -> LS.empty () - | StartOf _ -> ctx.ask (Queries.MayPointTo exp) - + + let invalidate ask exp_w st = + D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st let context _ _ = () (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lval in - if M.tracing then M.trace "tmpSpecial" "lvalsWritten %a\n" LS.pretty lvalsWritten; - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) ctx.local + if M.tracing then M.tracel "tmpSpecial" "assignment of %a\n" d_lval lval; + (* Invalidate all entrys from the map that are possibly written by the assignment *) + invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local let branch ctx (exp:exp) (tv:bool) : D.t = ctx.local @@ -74,6 +53,7 @@ struct let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let d = ctx.local in + let ask = Analyses.ask_of_ctx ctx in (* Just dbg prints *) (if M.tracing then @@ -83,55 +63,31 @@ struct let desc = LibraryFunctions.find f in + (* remove entrys, dependent on lvals that were possibly written by the special function *) - let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in - let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in - let deep_addrs = - if List.mem LibraryDesc.InvalidateGlobals desc.attrs then ( - foldGlobals !Cilfacade.current_file (fun acc global -> - match global with - | GVar (vi, _, _) when not (BaseUtil.is_static vi) -> - mkAddrOf (Var vi, NoOffset) :: acc - (* TODO: what about GVarDecl? (see "base.ml -> special_unknown_invalidate")*) - | _ -> acc - ) deep_addrs - ) - else - deep_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.MayPointTo addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d shallow_addrs - in - let d = List.fold_left (fun accD addr -> - let lvalsWritten = ctx.ask (Queries.ReachableFrom addr) in - D.filter (fun _ (_, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) accD) d deep_addrs - in + let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in + (* TODO similar to symbLocks->Spec->special: why doesn't invalidate involve any reachable for deep write? *) + let d = List.fold_left (fun d e -> invalidate ask e d) d write_args in (* same for lval assignment of the call*) let d = match lval with - | Some lv -> ( - (* get the set of all lvals written by the assign. Then remove all entrys from the map where the dependencies overlap with the set of written lvals *) - let lvalsWritten = ls_of_lv ctx lv in - D.filter (fun _ (ml, ls) -> LS.is_bot (LS.meet lvalsWritten ls) ) d - ) + | Some lv -> invalidate ask (mkAddrOf lv) ctx.local | None -> d in (* add new math fun desc*) let d = match lval, desc.special arglist with - | Some (Var v, offs), (Math { fun_args; }) -> - let argsDep = List.fold_left LS.union (LS.empty ()) (List.map (ls_of_exp ctx) arglist) in - let lvalsWritten = ls_of_lv ctx (Var v, offs) in - (* only add descriptor, if the set of lvals contained in the args is known and none is written by the assignment *) + | Some ((Var v, offs) as lv), (Math { fun_args; }) -> + (* only add descriptor, if none of the args is written by the assignment, invalidating the equivalence *) (* actually it would be necessary to check here, if one of the arguments is written by the call. However this is not the case for any of the math functions and no other functions are covered so far *) - if LS.is_top argsDep || not (LS.is_empty (LS.meet argsDep lvalsWritten)) then + if List.exists (fun arg -> VarEq.may_change ask (mkAddrOf lv) arg) arglist then d - else - D.add (v, resolve offs) ((ML.lift fun_args, LS.union argsDep lvalsWritten)) d + else + D.add (v, resolve offs) ((ML.lift fun_args, Deps.of_list ((Lval lv)::arglist))) d | _ -> d + in if M.tracing then M.tracel "tmpSpecial" "Result: %a\n\n" D.pretty d; From 1bff8fb0222ffe6ac77616bfe1a713aa7ec70f6d Mon Sep 17 00:00:00 2001 From: Stanimir Bozhilov Date: Sat, 8 Jul 2023 10:05:40 +0200 Subject: [PATCH 26/52] 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 27/52] 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 28/52] 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 29/52] 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 30/52] 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 5d5afffbe85250e3542c8c7e5adf82ed7427ff9d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jul 2023 13:46:28 +0300 Subject: [PATCH 31/52] Fix indentation --- src/analyses/base.ml | 8 ++++---- src/cdomains/valueDomain.ml | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a231ea396..0e766401d9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2348,10 +2348,10 @@ struct set ~ctx ~t_override:t ask ctx.global ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) | Rand, _ -> begin match lv with - | Some x -> - let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result - | None -> st + | Some x -> + let result:value = (Int (ID.starting IInt Z.zero)) in + set ~ctx (Analyses.ask_of_ctx ctx) gs st (eval_lv (Analyses.ask_of_ctx ctx) ctx.global st x) (Cilfacade.typeOfLval x) result + | None -> st end | _, _ -> let st = diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 4e40f3a287..11763ce5c7 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -963,16 +963,16 @@ struct Top end | JmpBuf _, _ -> - (* hack for jmp_buf variables *) - begin match value with - | JmpBuf t -> value (* if actually assigning jmpbuf, use value *) - | Blob(Bot, _, _) -> Bot (* TODO: Stopgap for malloced jmp_bufs, there is something fundamentally flawed somewhere *) - | _ -> - if !AnalysisState.global_initialization then - JmpBuf (JmpBufs.Bufs.empty (), false) (* if assigning global init, use empty set instead *) - else - Top - end + (* hack for jmp_buf variables *) + begin match value with + | JmpBuf t -> value (* if actually assigning jmpbuf, use value *) + | Blob(Bot, _, _) -> Bot (* TODO: Stopgap for malloced jmp_bufs, there is something fundamentally flawed somewhere *) + | _ -> + if !AnalysisState.global_initialization then + JmpBuf (JmpBufs.Bufs.empty (), false) (* if assigning global init, use empty set instead *) + else + Top + end | _ -> let result = match offs with From 9f1e5f96eb7f95fbc3e13c693e7874bf4b21db6b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jul 2023 13:47:57 +0300 Subject: [PATCH 32/52] Fix unused-rec-flag warning in ValueDomain --- src/cdomains/valueDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index 11763ce5c7..20c4f3bf21 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -564,7 +564,7 @@ struct warn_type "join" x y; Top - let rec widen x y = + let widen x y = match (x,y) with | (Top, _) -> Top | (_, Top) -> Top @@ -582,7 +582,7 @@ struct | (Struct x, Struct y) -> Struct (Structs.widen x y) | (Union x, Union y) -> Union (Unions.widen x y) | (Array x, Array y) -> Array (CArrays.widen x y) - | (Blob x, Blob y) -> Blob (Blobs.widen x y) + | (Blob x, Blob y) -> Blob (Blobs.widen x y) (* TODO: why no blob special cases like in join? *) | (Thread x, Thread y) -> Thread (Threads.widen x y) | (Int x, Thread y) | (Thread y, Int x) -> From 91388efe40f0ea7f432068bfa34c91cce00a82b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jul 2023 12:42:59 +0300 Subject: [PATCH 33/52] 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 34/52] 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 35/52] 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 36/52] 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 37/52] 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 38/52] Add check for UnknownPtr --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 3a2f4b52bd..d4ba662e97 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2003,7 +2003,7 @@ struct let points_to_set = addrToLvalSet a in if Q.LS.is_top points_to_set then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Points-to set for pointer %a in function %s is top. Potential free of non-dynamically allocated memory may occur" d_exp ptr special_fn.vname - else if Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set then + else if (Q.LS.exists (fun (v, _) -> not (ctx.ask (Q.IsHeapVar v))) points_to_set) || (AD.mem Addr.UnknownPtr a) then M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Free of non-dynamically allocated memory in function %s for pointer %a" special_fn.vname d_exp ptr | _ -> M.warn ~category:MessageCategory.Analyzer "Pointer %a in function %s doesn't evaluate to a valid address." d_exp ptr special_fn.vname From 49fa590a1b481b90de416fc93cc4f13b8f86b489 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Fri, 28 Jul 2023 20:02:28 +0200 Subject: [PATCH 39/52] 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 40/52] 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 41/52] 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 42/52] 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 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 43/52] 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 44/52] Implement comments from code review --- src/analyses/libraryFunctions.ml | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/analyses/libraryFunctions.ml b/src/analyses/libraryFunctions.ml index 242c3f37fd..2de54f9660 100644 --- a/src/analyses/libraryFunctions.ml +++ b/src/analyses/libraryFunctions.ml @@ -15,11 +15,9 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin_memcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); ("__builtin___memcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); - ("mempcpy", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []]); - ("__builtin___mempcpy_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "n" []; drop "os" []]); - ("memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin_memmove", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []]); - ("__builtin___memmove_chk", unknown [drop "dest" [w]; drop "src" [r]; drop "count" []; drop "os" []]); + ("memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin_memmove", special [__ "dest" [w]; __ "src" [r]; drop "count" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___memmove_chk", special [__ "dest" [w]; __ "src" [r]; drop "count" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ("strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin_strcpy", special [__ "dest" [w]; __ "src" [r]] @@ fun dest src -> Strcpy { dest; src; n = None; }); ("__builtin___strcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "os" []] @@ fun dest src -> Strcpy { dest; src; n = None; }); @@ -33,20 +31,15 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__builtin_strncat", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("__builtin___strncat_chk", special [__ "dest" [r; w]; __ "src" [r]; __ "n" []; drop "os" []] @@ fun dest src n -> Strcat { dest; src; n = Some n; }); ("asctime", unknown ~attrs:[ThreadUnsafe] [drop "time_ptr" [r_deep]]); - ("free", unknown [drop "ptr" [f]]); ("fclose", unknown [drop "stream" [r_deep; w_deep; f_deep]]); ("feof", unknown [drop "stream" [r_deep; w_deep]]); ("ferror", unknown [drop "stream" [r_deep; w_deep]]); ("fflush", unknown [drop "stream" [r_deep; w_deep]]); ("fgetc", unknown [drop "stream" [r_deep; w_deep]]); - ("getc", unknown [drop "stream" [r_deep; w_deep]]); - ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); - ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); - ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); - ("printf", unknown (drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) - ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' []))); (* TODO: why not r for VarArgs?*) + ("printf", unknown (drop "format" [r] :: VarArgs (drop' [r]))); + ("fprintf", unknown (drop "stream" [r_deep; w_deep] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("sprintf", unknown (drop "buffer" [w] :: drop "format" [r] :: VarArgs (drop' [r]))); + ("snprintf", unknown (drop "buffer" [w] :: drop "bufsz" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("fputc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("putc", unknown [drop "ch" []; drop "stream" [r_deep; w_deep]]); ("fputs", unknown [drop "str" [r]; drop "stream" [r_deep; w_deep]]); @@ -87,6 +80,10 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putchar", unknown [drop "ch" []]); ("puts", unknown [drop "s" [r]]); ("rand", unknown ~attrs:[ThreadUnsafe] []); + ("setgrent", unknown ~attrs:[ThreadUnsafe] []); + ("setpwent", unknown ~attrs:[ThreadUnsafe] []); + ("setutxent", unknown ~attrs:[ThreadUnsafe] []); + ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strcspn", unknown [drop "s" [r]; drop "accept" [r]]); ("strftime", unknown [drop "str" [w]; drop "count" []; drop "format" [r]; drop "tp" [r]]); @@ -107,7 +104,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("ctime", unknown ~attrs:[ThreadUnsafe] [drop "rm" [r]]); ("clearerr", unknown [drop "stream" [w]]); ("setbuf", unknown [drop "stream" [w]; drop "buf" [w]]); - ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' []))); + ("swprintf", unknown (drop "wcs" [w] :: drop "maxlen" [] :: drop "fmt" [r] :: VarArgs (drop' [r]))); ("assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if assert is used without include, e.g. in transformed files *) ("difftime", unknown [drop "time1" []; drop "time2" []]); ("system", unknown ~attrs:[ThreadUnsafe] [drop "command" [r]]); @@ -185,16 +182,16 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("putenv", unknown ~attrs:[ThreadUnsafe] [drop "string" [r; w]]); ("readdir", unknown ~attrs:[ThreadUnsafe] [drop "dirp" [r_deep]]); ("setenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]; drop "name" [r]; drop "overwrite" []]); - ("setgrent", unknown ~attrs:[ThreadUnsafe] []); - ("setpwent", unknown ~attrs:[ThreadUnsafe] []); - ("setutxent", unknown ~attrs:[ThreadUnsafe] []); - ("strerror", unknown ~attrs:[ThreadUnsafe] [drop "errnum" []]); ("strsignal", unknown ~attrs:[ThreadUnsafe] [drop "sig" []]); ("unsetenv", unknown ~attrs:[ThreadUnsafe] [drop "name" [r]]); ("lseek", unknown [drop "fd" []; drop "offset" []; drop "whence" []]); ("fcntl", unknown (drop "fd" [] :: drop "cmd" [] :: VarArgs (drop' [r; w]))); ("fseeko", unknown [drop "stream" [r_deep; w_deep]; drop "offset" []; drop "whence" []]); ("fileno", unknown [drop "stream" [r_deep; w_deep]]); + ("fgets", unknown [drop "str" [w]; drop "count" []; drop "stream" [r_deep; w_deep]]); + ("fopen", unknown [drop "pathname" [r]; drop "mode" [r]]); + ("fdopen", unknown [drop "fd" []; drop "mode" [r]]); + ("getc", unknown [drop "stream" [r_deep; w_deep]]); ("getopt", unknown ~attrs:[ThreadUnsafe] [drop "argc" []; drop "argv" [r_deep]; drop "optstring" [r]]); ("iconv_open", unknown [drop "tocode" [r]; drop "fromcode" [r]]); ("iconv", unknown [drop "cd" [r]; drop "inbuf" [r]; drop "inbytesleft" [r;w]; drop "outbuf" [w]; drop "outbytesleft" [r;w]]); @@ -438,6 +435,8 @@ let glibc_desc_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("strcasestr", unknown [drop "haystack" [r]; drop "needle" [r]]); ("inet_aton", unknown [drop "cp" [r]; drop "inp" [w]]); ("fopencookie", unknown [drop "cookie" []; drop "mode" [r]; drop "io_funcs" [s_deep]]); (* doesn't access cookie but passes it to io_funcs *) + ("mempcpy", special [__ "dest" [w]; __ "src" [r]; drop "n" []] @@ fun dest src -> Memcpy { dest; src }); + ("__builtin___mempcpy_chk", special [__ "dest" [w]; __ "src" [r]; drop "n" []; drop "os" []] @@ fun dest src -> Memcpy { dest; src }); ] let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -449,7 +448,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("epoll_create", unknown [drop "size" []]); ("epoll_ctl", unknown [drop "epfd" []; drop "op" []; drop "fd" []; drop "event" [w]]); ("epoll_wait", unknown [drop "epfd" []; drop "events" [w]; drop "maxevents" []; drop "timeout" []]); - ("__fprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]]); + ("__fprintf_chk", unknown (drop "stream" [r_deep; w_deep] :: drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("sysinfo", unknown [drop "info" [w_deep]]); ("__xpg_basename", unknown [drop "path" [r]]); ("ptrace", unknown (drop "request" [] :: VarArgs (drop' [r_deep; w_deep]))); (* man page has 4 arguments, but header has varargs and real-world programs may call with <4 *) From f2fd9b5e5bdebc92631088c22e6b43401903b85f Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 2 Aug 2023 22:32:05 +0300 Subject: [PATCH 45/52] 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 ab339c5e5fdaf68cd4e7a836adfc1ff720e79bd6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 3 Aug 2023 11:26:40 +0300 Subject: [PATCH 46/52] 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 47/52] 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 48/52] 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 49/52] 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 933b4e3482175aab6336b0308dab2ccb1a9f7960 Mon Sep 17 00:00:00 2001 From: FelixKrayer Date: Thu, 3 Aug 2023 17:27:16 +0200 Subject: [PATCH 50/52] 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 f2b7d1c0f3ac1dcec8107a67eb6bb19869943e9a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 4 Aug 2023 17:24:29 +0300 Subject: [PATCH 51/52] 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 52/52] 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