From 3bbe28851e5c4684821a429c686babbd850ed9d1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Apr 2024 15:19:07 +0200 Subject: [PATCH 001/160] Enable int refinement in `traces.json` --- conf/traces.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/traces.json b/conf/traces.json index 7ba21811c0..459edf8c6f 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -3,7 +3,8 @@ "int": { "def_exc": true, "interval": false, - "enums": true + "enums": true, + "refinement" : "fixpoint" }, "malloc": { "wrappers": [ From f62f74f29c243219f0c45176d3d72a7eaa8d3174 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Apr 2024 15:36:50 +0200 Subject: [PATCH 002/160] Typo --- src/util/apron/relationPrecCompareUtil.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/apron/relationPrecCompareUtil.apron.ml b/src/util/apron/relationPrecCompareUtil.apron.ml index 15b59c2e22..f820081547 100644 --- a/src/util/apron/relationPrecCompareUtil.apron.ml +++ b/src/util/apron/relationPrecCompareUtil.apron.ml @@ -1,4 +1,4 @@ -(** {!RelationPriv} precison comparison. *) +(** {!RelationPriv} precision comparison. *) open PrecCompareUtil From 075505701e974e910bf834f8dd04379f418b2c0f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Apr 2024 15:42:33 +0200 Subject: [PATCH 003/160] Enable `def_exc_widen_by_join` --- conf/traces.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/traces.json b/conf/traces.json index 459edf8c6f..b25a34863f 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -4,7 +4,7 @@ "def_exc": true, "interval": false, "enums": true, - "refinement" : "fixpoint" + "def_exc_widen_by_join": true }, "malloc": { "wrappers": [ From 4c3a0a3462fa50f94a8c24c12d06a4e257618d50 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 17 Apr 2024 13:08:33 +0200 Subject: [PATCH 004/160] Typo --- src/util/precCompare.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index a04e395e83..46026c558e 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -1,4 +1,4 @@ -(** Precison comparison. *) +(** Precision comparison. *) open Batteries module Pretty = GoblintCil.Pretty From 35427ae227fd7dd9f459f924353ea05152d14004 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 17 Apr 2024 13:08:57 +0200 Subject: [PATCH 005/160] Typo --- src/util/privPrecCompareUtil.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/privPrecCompareUtil.ml b/src/util/privPrecCompareUtil.ml index 8f0a24db3b..0e857c9063 100644 --- a/src/util/privPrecCompareUtil.ml +++ b/src/util/privPrecCompareUtil.ml @@ -1,4 +1,4 @@ -(** {!BasePriv} precison comparison. *) +(** {!BasePriv} precision comparison. *) open GoblintCil open PrecCompareUtil From 6e176414e61b3477de44cce17d7f88d728c406c3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 17 Apr 2024 13:10:06 +0200 Subject: [PATCH 006/160] Typos --- src/util/precCompareUtil.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/precCompareUtil.ml b/src/util/precCompareUtil.ml index e00447fd60..98ba1dfede 100644 --- a/src/util/precCompareUtil.ml +++ b/src/util/precCompareUtil.ml @@ -28,10 +28,10 @@ sig (** Module for the result hash map *) module RH : Hashtbl.S with type key = Key.t - (** Type to which the result hashmap is actually convered before storing it to disc *) + (** Type to which the result hashmap is actually converted before storing it to disc *) type marshal - (** Wrapper of marhshal type, together with a name for the analysis + privatization combination *) + (** Wrapper of marshal type, together with a name for the analysis + privatization combination *) type dump = marshal dump_gen (** Type of the actually usable result. To be obtained from the dump via the unmarshal function below *) From a11f7137d984cad80df87aaf4327335d9b274a10 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 17 Apr 2024 13:43:38 +0200 Subject: [PATCH 007/160] Add `exp.priv-prec-dump-suffix` --- src/analyses/basePriv.ml | 2 +- src/config/options.schema.json | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 527d11b1de..8bf310fe7c 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1762,7 +1762,7 @@ struct (* LVH.iter (fun (l, x) v -> Logs.debug "%a %a = %a" CilType.Location.pretty l CilType.Varinfo.pretty x VD.pretty v ) lvh; *) - Marshal.output f ({name = get_string "ana.base.privatization"; results = lvh}: result); + Marshal.output f ({name = get_string "ana.base.privatization" ^ get_string "exp.priv-prec-dump-suffix"; results = lvh}: result); close_out_noerr f let finalize () = diff --git a/src/config/options.schema.json b/src/config/options.schema.json index db93e74ff4..7b2203554b 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1634,6 +1634,12 @@ "type": "string", "default": "" }, + "priv-prec-dump-suffix": { + "title": "exp.priv-prec-dump-suffix", + "description": "Suffix for name of precision dump. Useful if one has several configurations for one privatization.", + "type": "string", + "default": "" + }, "priv-distr-init": { "title": "exp.priv-distr-init", "description": From 811c18380ded500121358280b2e61366cfaff014 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 19 Apr 2024 14:09:37 +0200 Subject: [PATCH 008/160] Hotfix for #1421 --- src/analyses/base.ml | 74 +++++++++++++------------ tests/regression/03-practical/33-axel.c | 20 +++++++ 2 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 tests/regression/03-practical/33-axel.c diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8e79a5c969..fe00982040 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -979,41 +979,45 @@ struct (* Evaluate structurally using base at first. *) let a1 = eval_rv ~ctx st e1 in let a2 = eval_rv ~ctx st e2 in - let t1 = Option.default_delayed (fun () -> Cilfacade.typeOf e1) t1 in - let t2 = Option.default_delayed (fun () -> Cilfacade.typeOf e2) t2 in - let r = evalbinop_base ~ctx op t1 a1 t2 a2 t in - if Cil.isIntegralType t then ( - match r with - | Int i when ID.to_int i <> None -> r (* Avoid fallback, cannot become any more precise. *) - | _ -> - (* Fallback to MustBeEqual query, could get extra precision from exprelation/var_eq. *) - let must_be_equal () = - let r = Q.must_be_equal (Analyses.ask_of_ctx ctx) e1 e2 in - if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b" d_exp e1 d_exp e2 r; - r - in - match op with - | MinusA when must_be_equal () -> - let ik = Cilfacade.get_ikind t in - Int (ID.of_int ik Z.zero) - | MinusPI (* TODO: untested *) - | MinusPP when must_be_equal () -> - let ik = Cilfacade.ptrdiff_ikind () in - Int (ID.of_int ik Z.zero) - (* Eq case is unnecessary: Q.must_be_equal reconstructs BinOp (Eq, _, _, _) and repeats EvalInt query for that, yielding a top from query cycle and never being must equal *) - | Le - | Ge when must_be_equal () -> - let ik = Cilfacade.get_ikind t in - Int (ID.of_bool ik true) - | Ne - | Lt - | Gt when must_be_equal () -> - let ik = Cilfacade.get_ikind t in - Int (ID.of_bool ik false) - | _ -> r (* Fallback didn't help. *) - ) - else - r (* Avoid fallback, above cases are for ints only. *) + try + let t1 = Option.default_delayed (fun () -> Cilfacade.typeOf e1) t1 in + let t2 = Option.default_delayed (fun () -> Cilfacade.typeOf e2) t2 in + let r = evalbinop_base ~ctx op t1 a1 t2 a2 t in + if Cil.isIntegralType t then ( + match r with + | Int i when ID.to_int i <> None -> r (* Avoid fallback, cannot become any more precise. *) + | _ -> + (* Fallback to MustBeEqual query, could get extra precision from exprelation/var_eq. *) + let must_be_equal () = + let r = Q.must_be_equal (Analyses.ask_of_ctx ctx) e1 e2 in + if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b" d_exp e1 d_exp e2 r; + r + in + match op with + | MinusA when must_be_equal () -> + let ik = Cilfacade.get_ikind t in + Int (ID.of_int ik Z.zero) + | MinusPI (* TODO: untested *) + | MinusPP when must_be_equal () -> + let ik = Cilfacade.ptrdiff_ikind () in + Int (ID.of_int ik Z.zero) + (* Eq case is unnecessary: Q.must_be_equal reconstructs BinOp (Eq, _, _, _) and repeats EvalInt query for that, yielding a top from query cycle and never being must equal *) + | Le + | Ge when must_be_equal () -> + let ik = Cilfacade.get_ikind t in + Int (ID.of_bool ik true) + | Ne + | Lt + | Gt when must_be_equal () -> + let ik = Cilfacade.get_ikind t in + Int (ID.of_bool ik false) + | _ -> r (* Fallback didn't help. *) + ) + else + r (* Avoid fallback, above cases are for ints only. *) + with Cilfacade.TypeOfError _ -> + (* Emit top value of corresponding type when there are issues *) + VD.top_value t (* A hackish evaluation of expressions that should immediately yield an * address, e.g. when calling functions. *) diff --git a/tests/regression/03-practical/33-axel.c b/tests/regression/03-practical/33-axel.c new file mode 100644 index 0000000000..f8150a804f --- /dev/null +++ b/tests/regression/03-practical/33-axel.c @@ -0,0 +1,20 @@ +// Should not crash (issue 1421) +#include +struct a { + pthread_mutex_t b; +}; +struct c { + struct a *conn; +}; + +int main() { + int x; + struct a str = {0}; + struct c axel = {0}; + axel.conn = &str; + pthread_mutex_t* ptr = &((axel.conn + 0)->b); + x = 4; + pthread_mutex_lock(ptr); + pthread_mutex_unlock(ptr); + pthread_mutex_lock(&((axel.conn + 0)->b)); +} From 34411aabbf215931ab733ba633b52a7a0d9c2a57 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 21 Apr 2024 16:23:57 +0200 Subject: [PATCH 009/160] precComp: Fix stderr output colorization --- src/util/precCompare.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 46026c558e..bee273ad6c 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -120,6 +120,8 @@ struct let main () = Util.init (); + (* Do not colorize stderr output *) + AnsiColors.stderr := false; let filenames = List.tl (Array.to_list Sys.argv) in let dumps = List.map load filenames in let (locations_count, location_vars_count) = count_locations dumps in From af887b2f63a00ca5474a3b09d92daf044344d23d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 21 Apr 2024 17:25:41 +0200 Subject: [PATCH 010/160] Add `exp.priv-prec-dump-proj` option --- conf/traces.json | 3 ++- src/analyses/basePriv.ml | 6 ++++-- src/config/options.schema.json | 6 ++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/conf/traces.json b/conf/traces.json index b25a34863f..6977518f5a 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -39,6 +39,7 @@ } }, "exp": { - "priv-distr-init": false + "priv-distr-init": false, + "priv-prec-dump-proj" : true } } diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 8bf310fe7c..d08f007ffd 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1753,8 +1753,10 @@ struct let read_global ask getg st x = let v = Priv.read_global ask getg st x in - if !AnalysisState.postsolving && !is_dumping then - LVH.modify_def (VD.bot ()) (!Goblint_tracing.current_loc, x) (VD.join v) lvh; + if !AnalysisState.postsolving && !is_dumping then ( + let v_proj = if get_bool "exp.priv-prec-dump-proj" then VD.project (Queries.to_value_domain_ask ask) (Some (true,true,true,false,false)) None v else v in + LVH.modify_def (VD.bot ()) (!Goblint_tracing.current_loc, x) (VD.join v_proj) lvh + ); v let dump () = diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 7b2203554b..5f54f92713 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1640,6 +1640,12 @@ "type": "string", "default": "" }, + "priv-prec-dump-proj": { + "title": "exp.priv-prec-dump-proj", + "description": "Project to (enums * def_exc * interval) precision for privatization precision dump.", + "type": "boolean", + "default": false + }, "priv-distr-init": { "title": "exp.priv-distr-init", "description": From 2b7e509f08c52c3939d23799576ddc2019133c2b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 21 Apr 2024 18:07:10 +0200 Subject: [PATCH 011/160] Just warn instead of crash if an argument to a string function does not evaluate to an address --- src/analyses/base.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index fe00982040..135b03967e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2342,7 +2342,9 @@ struct | Addr.Addr (v, o) -> Addr.Addr (v, lo o) | other -> other in AD.map rmLastOffset a - | _ -> raise (Failure "String function: not an address") + | _ -> + M.debug ~category:Analyzer "Argument to string function did not evaluate to an address!"; + AD.top () in let string_manipulation s1 s2 lv all op_addr op_array = let s1_v = eval_rv ~ctx st s1 in From 7cfa3b1191e3bb213585f5b88ef6006beba1fd23 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 22 Apr 2024 18:03:06 +0200 Subject: [PATCH 012/160] Initiate buckets --- src/util/precCompare.ml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index bee273ad6c..be2a3827ab 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -103,8 +103,12 @@ struct module CompareDump = MakeHashtbl (Key) (Dom) (RH) + let comparisons = ref [] + let compare_dumps ({name = name1; results = lvh1}: result) ({name = name2; results = lvh2}: result) = - CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 + let (c, d) = CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 in + comparisons := (name1, name2, c) :: !comparisons; + (c, d) let count_locations (dumps: result list) = let module LH = Hashtbl.Make (CilType.Location) in @@ -118,6 +122,29 @@ struct ) dumps; (LH.length locations, RH.length location_vars) + let group () = + let new_bucket_id = ref 0 in + let equality_buckets = Hashtbl.create 113 in + let sorted = List.sort (fun (n1, _, _) (n2, _, _) -> String.compare n1 n2) !comparisons in + List.iter (fun (name1, name2, (c:Comparison.t)) -> + (if not (Hashtbl.mem equality_buckets name1) then + (* Make its own bucket if it does not appear yet *) + (let bucket_id = !new_bucket_id in + incr new_bucket_id; + Hashtbl.add equality_buckets name1 bucket_id)); + if c.more_precise = 0 && c.less_precise = 0 && c.incomparable = 0 then + Hashtbl.replace equality_buckets name2 (Hashtbl.find equality_buckets name1) + else + () + ) sorted; + let bindings = Hashtbl.bindings equality_buckets in + let buckets = List.group (fun (_, b) (_, b') -> compare b b') bindings in + List.iter (fun bucket -> + Logs.result "Bucket %d:" (snd (List.hd bucket)); + List.iter (fun (name, _) -> Logs.result " %s" name) bucket + ) buckets + + let main () = Util.init (); (* Do not colorize stderr output *) @@ -133,5 +160,6 @@ struct |> List.map (uncurry compare_dumps) |> List.iter (fun (_, msg) -> Logs.result "%t" (fun () -> msg)); Logs.newline (); - Logs.result "Total locations: %d\nTotal %s: %d" locations_count (Key.name ()) location_vars_count + Logs.result "Total locations: %d\nTotal %s: %d" locations_count (Key.name ()) location_vars_count; + group () end From b9a110d4fb26bc15ef55f0f29369e4e22c7ed262 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 10:28:29 +0300 Subject: [PATCH 013/160] Change to macos-13 in GitHub Actions for x86_64 OSX --- .github/workflows/locked.yml | 2 +- .github/workflows/unlocked.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index edd251b66f..18935725ca 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -18,7 +18,7 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + - macos-13 ocaml-compiler: - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 907ff14386..5e586ee038 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -16,7 +16,7 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + - macos-13 ocaml-compiler: - 5.0.x - ocaml-variants.4.14.0+options,ocaml-option-flambda @@ -91,7 +91,7 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + - macos-13 ocaml-compiler: - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file, downgrade deps step @@ -189,7 +189,7 @@ jobs: matrix: os: - ubuntu-latest - - macos-latest + - macos-13 ocaml-compiler: - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file From d33ccbfa404d68335dd6026b62f5755ddefb9cb0 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 24 Apr 2024 10:23:47 +0200 Subject: [PATCH 014/160] Output more sophisticated stats --- src/util/precCompare.ml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index be2a3827ab..c9b735de2d 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -107,7 +107,7 @@ struct let compare_dumps ({name = name1; results = lvh1}: result) ({name = name2; results = lvh2}: result) = let (c, d) = CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 in - comparisons := (name1, name2, c) :: !comparisons; + comparisons := (name1, name2, c, d) :: !comparisons; (c, d) let count_locations (dumps: result list) = @@ -125,8 +125,8 @@ struct let group () = let new_bucket_id = ref 0 in let equality_buckets = Hashtbl.create 113 in - let sorted = List.sort (fun (n1, _, _) (n2, _, _) -> String.compare n1 n2) !comparisons in - List.iter (fun (name1, name2, (c:Comparison.t)) -> + let sorted = List.sort (fun (n1, _, _, _) (n2, _, _, _) -> String.compare n1 n2) !comparisons in + List.iter (fun (name1, name2, (c:Comparison.t), _) -> (if not (Hashtbl.mem equality_buckets name1) then (* Make its own bucket if it does not appear yet *) (let bucket_id = !new_bucket_id in @@ -142,8 +142,24 @@ struct List.iter (fun bucket -> Logs.result "Bucket %d:" (snd (List.hd bucket)); List.iter (fun (name, _) -> Logs.result " %s" name) bucket - ) buckets - + ) buckets; + let comparison_produced = Hashtbl.create 113 in + List.iter (fun (name1, name2, c,d) -> + let bucket1 = Hashtbl.find equality_buckets name1 in + let bucket2 = Hashtbl.find equality_buckets name2 in + if bucket1 = bucket2 then + () + else + begin + let comp_tumple = (min bucket1 bucket2, max bucket1 bucket2) in + if not @@ Hashtbl.mem comparison_produced comp_tumple then + begin + Hashtbl.add comparison_produced comp_tumple (); + Logs.result "Comparison between bucket %d and %d: %t" (fst comp_tumple) (snd comp_tumple) (fun () -> d); + end + end + ) sorted; + () let main () = Util.init (); From 22fdef68328223ec98454fd9ffed518cc6d68582 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 25 Apr 2024 11:21:51 +0200 Subject: [PATCH 015/160] Make `traces` and `traces-rel` more similar --- conf/traces-rel.json | 3 ++- conf/traces.json | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/conf/traces-rel.json b/conf/traces-rel.json index 2b82a57554..405fb593cf 100644 --- a/conf/traces-rel.json +++ b/conf/traces-rel.json @@ -3,7 +3,8 @@ "int": { "def_exc": true, "interval": false, - "enums": true + "enums": true, + "def_exc_widen_by_join": true }, "malloc": { "wrappers": [ diff --git a/conf/traces.json b/conf/traces.json index 6977518f5a..0e4548ab4f 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -25,6 +25,14 @@ "ldv_xzalloc", "ldv_calloc" ] + }, + "base" : { + "invariant": { + "enabled": false + }, + "eval": { + "deep-query": false + } } }, "sem": { @@ -36,10 +44,18 @@ }, "builtin_unreachable": { "dead_code": true + }, + "int": { + "signed_overflow": "assume_none" } }, "exp": { "priv-distr-init": false, "priv-prec-dump-proj" : true + }, + "solvers" : { + "td3": { + "side_widen" : "sides-pp" + } } } From 1b180d46133db724a3d34228c4f1200134944915 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 25 Apr 2024 14:38:16 +0200 Subject: [PATCH 016/160] Add `bot_in_blob_leq_bot` so `bot` and `Blob(bot)` comapre equal in privPrecCompare --- src/cdomain/value/cdomains/valueDomain.ml | 8 +++++++- src/common/framework/analysisState.ml | 3 +++ src/privPrecCompare.ml | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 52bcbd2c7d..f28fe2b2ad 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -532,7 +532,13 @@ struct | (_, Top) -> true | (Top, _) -> false | (Bot, _) -> true - | (_, Bot) -> false + | (x, Bot) -> + if !AnalysisState.bot_in_blob_leq_bot then + match x with + | Blob (x,s,o) -> leq x Bot + | _ -> false + else + false | (Int x, Int y) -> ID.leq x y | (Float x, Float y) -> FD.leq x y | (Int x, Address y) when ID.to_int x = Some Z.zero && not (AD.is_not_null y) -> true diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index fd76e1bb67..cd9571a8ce 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -30,3 +30,6 @@ let postsolving = ref false (* None if verification is disabled, Some true if verification succeeded, Some false if verification failed *) let verified : bool option ref = ref None + +(* Comparison mode where blobs with bot content that are not zero-initalized are considered equivalent to top-level bot *) +let bot_in_blob_leq_bot = ref false diff --git a/src/privPrecCompare.ml b/src/privPrecCompare.ml index b4588e25bd..3b2e385383 100644 --- a/src/privPrecCompare.ml +++ b/src/privPrecCompare.ml @@ -4,4 +4,5 @@ open Goblint_lib module A = PrecCompare.MakeDump (PrivPrecCompareUtil) let () = + AnalysisState.bot_in_blob_leq_bot := true; A.main () From 549f152883d2cab0e84914ba4ce6b3605c0a1281 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Apr 2024 17:48:34 +0300 Subject: [PATCH 017/160] Find more catch-all exception handlers --- src/analyses/base.ml | 2 +- src/util/backtrace/goblint_backtrace.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 135b03967e..bb538c41ae 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1266,7 +1266,7 @@ struct (* TODO: deduplicate https://github.com/goblint/analyzer/pull/1297#discussion_r1477804502 *) let rec exp_may_signed_overflow ctx exp = let res = match Cilfacade.get_ikind_exp exp with - | exception _ -> BoolDomain.MayBool.top () + | exception _ -> BoolDomain.MayBool.top () (* TODO: do not catch all! *) | ik -> let checkBinop e1 e2 binop = match ctx.ask (EvalInt e1), ctx.ask (EvalInt e2) with diff --git a/src/util/backtrace/goblint_backtrace.ml b/src/util/backtrace/goblint_backtrace.ml index a4355ac921..52fe5a91dc 100644 --- a/src/util/backtrace/goblint_backtrace.ml +++ b/src/util/backtrace/goblint_backtrace.ml @@ -47,7 +47,7 @@ let apply_mark_printers m = match f m with | Some s -> Some s | None - | exception _ -> None + | exception _ -> None (* TODO: do not catch all? *) ) !mark_printers let mark_to_string_default m = From 097a1378e372403ea0370c0183efac64576eb8f9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Apr 2024 18:00:01 +0300 Subject: [PATCH 018/160] Fix catch-all in LoopUnrolling --- src/util/loopUnrolling.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 02cce23f42..e433c34b4a 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -292,7 +292,7 @@ let fixedLoopSize loopStatement func = Logs.debug "iterations:"; Logs.debug "%d" s'; Some s' - with _ -> Logs.debug "iterations too big for integer"; None + with Z.Overflow -> Logs.debug "iterations too big for integer"; None class arrayVisitor = object From 868e7d9c75d7a7059a34bcaef731053c61652596 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Apr 2024 18:02:30 +0300 Subject: [PATCH 019/160] Fix catch-all in base exp_may_signed_overflow --- src/analyses/base.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index bb538c41ae..b931b50c9c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1266,7 +1266,9 @@ struct (* TODO: deduplicate https://github.com/goblint/analyzer/pull/1297#discussion_r1477804502 *) let rec exp_may_signed_overflow ctx exp = let res = match Cilfacade.get_ikind_exp exp with - | exception _ -> BoolDomain.MayBool.top () (* TODO: do not catch all! *) + | exception (Cilfacade.TypeOfError _) (* Cilfacade.typeOf *) + | exception (Invalid_argument _) -> (* get_ikind *) + BoolDomain.MayBool.top () | ik -> let checkBinop e1 e2 binop = match ctx.ask (EvalInt e1), ctx.ask (EvalInt e2) with From ec6ada394c3f284a8b5377b537eee6c955db789c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Apr 2024 18:05:39 +0300 Subject: [PATCH 020/160] Fix catch-all in AddressDomain --- src/cdomain/value/cdomains/addressDomain.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index b153e71d4e..b764b89d7a 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -239,9 +239,7 @@ struct ID.top_of ik let type_of xs = - try Addr.type_of (choose xs) - with (* WTF? Returns TVoid when it is unknown and stuff??? *) - | _ -> voidType + Addr.type_of (choose xs) (* TODO: what if ambiguous type? what if chooses NullPtr but also contains Addr with proper type? *) let of_var x = singleton (Addr.of_var x) let of_mval x = singleton (Addr.of_mval x) From a9e19b045e24031f2a863727161a9baf393a6b26 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Apr 2024 18:07:57 +0300 Subject: [PATCH 021/160] Fix two AD.type_of crashes in base --- src/analyses/base.ml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b931b50c9c..1e11f8d6b8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -468,7 +468,6 @@ struct * For the exp argument it is always ok to put None. This means not using precise information about * which part of an array is involved. *) let rec get ~ctx ?(top=VD.top ()) ?(full=false) (st: store) (addrs:address) (exp:exp option): value = - let at = AD.type_of addrs in let firstvar = if M.tracing then match AD.to_var_may addrs with [] -> "" | x :: _ -> x.vname else "" in if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a" AD.pretty addrs CPA.pretty st.cpa; (* Finding a single varinfo*offset pair *) @@ -495,7 +494,12 @@ struct in (* We form the collecting function by joining *) let c (x:value) = match x with (* If address type is arithmetic, and our value is an int, we cast to the correct ik *) - | Int _ when Cil.isArithmeticType at -> VD.cast at x + | Int _ -> + let at = AD.type_of addrs in + if Cil.isArithmeticType at then + VD.cast at x + else + x | _ -> x in let f x a = VD.join (c @@ f x) a in (* Finally we join over all the addresses in the set. *) @@ -2025,7 +2029,7 @@ struct (* To invalidate a single address, we create a pair with its corresponding * top value. *) let invalidate_address st a = - let t = AD.type_of a in + let t = try AD.type_of a with Not_found -> voidType in (* TODO: why is this called with empty a to begin with? *) let v = get ~ctx st a None in (* None here is ok, just causes us to be a bit less precise *) let nv = VD.invalidate_value (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) t v in (a, t, nv) From 7cdfa74ba5d0a7985133d8f66cfa3c8db514e329 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:09:10 +0300 Subject: [PATCH 022/160] Add @raise documentation to some functions --- src/cdomain/value/cdomains/addressDomain.ml | 1 + src/common/util/cilfacade.ml | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index b764b89d7a..dc1ebfff7d 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -238,6 +238,7 @@ struct else ID.top_of ik + (** @raise Not_found if set is empty. *) let type_of xs = Addr.type_of (choose xs) (* TODO: what if ambiguous type? what if chooses NullPtr but also contains Addr with proper type? *) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 2a4e73d588..99430ee8b6 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -199,7 +199,8 @@ let getFuns fileAST : startfuns = let getFirstStmt fd = List.hd fd.sbody.bstmts -(* Returns the ikind of a TInt(_) and TEnum(_). Unrolls typedefs. *) +(** Returns the ikind of a TInt(_) and TEnum(_). Unrolls typedefs. + @raise Invalid_argument if not integral type. *) let rec get_ikind t = (* important to unroll the type here, otherwise problems with typedefs *) match Cil.unrollType t with @@ -208,6 +209,7 @@ let rec get_ikind t = | TPtr _ -> get_ikind !Cil.upointType | _ -> invalid_arg ("Cilfacade.get_ikind: non-integer type " ^ CilType.Typ.show t) +(** @raise Invalid_argument if not floating-point type. *) let get_fkind t = (* important to unroll the type here, otherwise problems with typedefs *) match Cil.unrollType t with @@ -245,6 +247,7 @@ let () = Printexc.register_printer (function (* Cil doesn't expose this *) let stringLiteralType = ref charPtrType +(** @raise TypeOfError *) let typeOfRealAndImagComponents t = match unrollType t with | TInt _ -> t @@ -272,6 +275,7 @@ let isComplexFKind = function | FComplexLongDouble | FComplexFloat128 -> true +(** @raise TypeOfError *) let rec typeOf (e: exp) : typ = match e with | Const(CInt (_, ik, _)) -> TInt(ik, []) @@ -308,11 +312,13 @@ let rec typeOf (e: exp) : typ = | _ -> raise (TypeOfError StartOf_NonArray) end +(** @raise TypeOfError *) and typeOfInit (i: init) : typ = match i with SingleInit e -> typeOf e | CompoundInit (t, _) -> t +(** @raise TypeOfError *) and typeOfLval = function Var vi, off -> typeOffset vi.vtype off | Mem addr, off -> begin @@ -321,6 +327,7 @@ and typeOfLval = function | _ -> raise (TypeOfError (Mem_NonPointer addr)) end +(** @raise TypeOfError *) and typeOffset basetyp = let blendAttributes baseAttrs = let (_, _, contageous) = @@ -363,7 +370,12 @@ let mkCast ~(e: exp) ~(newt: typ) = in Cil.mkCastT ~e ~oldt ~newt +(** @raise TypeOfError + @raise Invalid_argument if not integral type. *) let get_ikind_exp e = get_ikind (typeOf e) + +(** @raise TypeOfError + @raise Invalid_argument if not floating-point type. *) let get_fkind_exp e = get_fkind (typeOf e) (** Make {!Cil.BinOp} with correct implicit casts inserted. *) From c59429ec87fbfa6e323f1d1b98c33f607ea3161b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:20:00 +0300 Subject: [PATCH 023/160] Fix catch-all in GobConfig --- src/config/gobConfig.ml | 31 ++++++++++++++++++++++++------- src/config/jsonSchema.ml | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/config/gobConfig.ml b/src/config/gobConfig.ml index 355a999fbf..1a3daf616d 100644 --- a/src/config/gobConfig.ml +++ b/src/config/gobConfig.ml @@ -168,7 +168,8 @@ struct Index (parse_index idx, parse_path' pth) | _ -> raise PathParseError - (** Parse a string path, but you may ignore the first dot. *) + (** Parse a string path, but you may ignore the first dot. + @raise Failure if path couldn't be parsed. *) let parse_path (s:string) : path = let s = String.trim s in try @@ -180,7 +181,7 @@ struct end with PathParseError -> Logs.error "Error: Couldn't parse the json path '%s'" s; - failwith "parsing" + failwith "parsing" (* TODO: return ParsePathError still? *) (** Here we store the actual configuration. *) let json_conf : Yojson.Safe.t ref = ref `Null @@ -252,7 +253,11 @@ struct Fun.protect ~finally:(fun () -> set_immutable false) f ) - (** The main function to write new values into the conf. Use [set_value] to properly invalidate cache and check immutability. *) + (** The main function to write new values into the conf. Use [set_value] to properly invalidate cache and check immutability. + @raise Failure + @raise TypeError + @raise Invalid_argument + @raise Json_encoding.Cannot_destruct *) let unsafe_set_value v o orig_pth = let rec set_value v o pth = match o, pth with @@ -343,13 +348,19 @@ struct (** Helper functions for writing values. *) - (** Sets a value, preventing changes when the configuration is immutable and invalidating the cache. *) + (** Sets a value, preventing changes when the configuration is immutable and invalidating the cache. + @raise Immutable *) let set_value v o pth = if is_immutable () then raise (Immutable pth); drop_memo (); unsafe_set_value v o pth - (** Helper function for writing values. Handles the tracing. *) + (** Helper function for writing values. Handles the tracing. + @raise Failure if path couldn't be parsed. + @raise Immutable + @raise TypeError + @raise Invalid_argument + @raise Json_encoding.Cannot_destruct *) let set_path_string st v = if Goblint_tracing.tracing then Goblint_tracing.trace "conf" "Setting '%s' to %a." st GobYojson.pretty v; set_value v json_conf (parse_path st) @@ -377,9 +388,15 @@ struct set_path_string st v with Yojson.Json_error _ | TypeError _ -> set_string st s - with e -> + with + | Failure _ + | Immutable _ + | TypeError _ + | Invalid_argument _ + | Json_encoding.Cannot_destruct _ as e -> + let bt = Printexc.get_raw_backtrace () in Logs.error "Cannot set %s to '%s'." st s; - raise e + Printexc.raise_with_backtrace e bt let merge json = Validator.validate_exn json; diff --git a/src/config/jsonSchema.ml b/src/config/jsonSchema.ml index 59b9733ca2..b2a0ef8735 100644 --- a/src/config/jsonSchema.ml +++ b/src/config/jsonSchema.ml @@ -153,7 +153,7 @@ module Validator (Schema: Schema) = struct let schema_encoding = encoding_of_schema Schema.schema - (** Raises [Json_encoding.Cannot_destruct] if invalid. *) + (** @raise Json_encoding.Cannot_destruct if invalid. *) let validate_exn json = JE.destruct schema_encoding json (* TODO: bool-returning validate? *) From f990912bd9421a4039cb62d8b5b604ed48dbddc4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:24:53 +0300 Subject: [PATCH 024/160] Update catch-all TODO in Goblint_backtrace --- src/util/backtrace/goblint_backtrace.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/backtrace/goblint_backtrace.ml b/src/util/backtrace/goblint_backtrace.ml index 52fe5a91dc..29198c27ea 100644 --- a/src/util/backtrace/goblint_backtrace.ml +++ b/src/util/backtrace/goblint_backtrace.ml @@ -47,7 +47,7 @@ let apply_mark_printers m = match f m with | Some s -> Some s | None - | exception _ -> None (* TODO: do not catch all? *) + | exception _ -> None (* TODO: do not catch all? Stdlib.Printexc also catches all *) ) !mark_printers let mark_to_string_default m = From 1b9222e4374eadf9f462c8fc3ffdda15bf6b9505 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:37:01 +0300 Subject: [PATCH 025/160] Add GobExn.catch_all_filter --- src/transform/expressionEvaluation.ml | 2 +- src/util/gobExn.ml | 9 +++++++++ src/util/server.ml | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 src/util/gobExn.ml diff --git a/src/transform/expressionEvaluation.ml b/src/transform/expressionEvaluation.ml index 88b273ab6d..66fc85bafa 100644 --- a/src/transform/expressionEvaluation.ml +++ b/src/transform/expressionEvaluation.ml @@ -31,7 +31,7 @@ struct let (~?) exception_function = try Some (exception_function ()) with - | _ -> None + | exn when GobExn.catch_all_filter exn -> None (* TODO: don't catch all *) let (~!) value_option = match value_option with | Some value -> value diff --git a/src/util/gobExn.ml b/src/util/gobExn.ml new file mode 100644 index 0000000000..a43621b2b5 --- /dev/null +++ b/src/util/gobExn.ml @@ -0,0 +1,9 @@ +let catch_all_filter = function + | Out_of_memory + | Stack_overflow -> false (* OCaml runtime *) + | Assert_failure _ + | Match_failure _ + | Undefined_recursive_module _ -> false (* Broken program *) + | Sys.Break -> false (* Ctrl-C *) + | Timeout.Timeout -> false (* Goblint timeout *) + | _ -> true diff --git a/src/util/server.ml b/src/util/server.ml index 1b67dbf8a3..80ddc81d31 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -338,7 +338,7 @@ let () = try GobConfig.set_auto conf (Yojson.Safe.to_string json); Maingoblint.handle_options (); - with exn -> (* TODO: Be more specific in what we catch. *) + with exn when GobExn.catch_all_filter exn -> (* TODO: Be more specific in what we catch. *) Response.Error.(raise (of_exn exn)) end); @@ -350,7 +350,7 @@ let () = try GobConfig.set_conf Options.defaults; Maingoblint.parse_arguments (); - with exn -> (* TODO: Be more specific in what we catch. *) + with exn when GobExn.catch_all_filter exn -> (* TODO: Be more specific in what we catch. *) Response.Error.(raise (of_exn exn)) end); @@ -362,7 +362,7 @@ let () = try GobConfig.merge json; Maingoblint.handle_options (); - with exn -> (* TODO: Be more specific in what we catch. *) + with exn when GobExn.catch_all_filter exn -> (* TODO: Be more specific in what we catch. *) Response.Error.(raise (of_exn exn)) end); @@ -374,7 +374,7 @@ let () = try GobConfig.merge_file (Fpath.v fname); Maingoblint.handle_options (); - with exn -> (* TODO: Be more specific in what we catch. *) + with exn when GobExn.catch_all_filter exn -> (* TODO: Be more specific in what we catch. *) Response.Error.(raise (of_exn exn)) end); From 3f7e5e82b3cfe8dd02a6fb3a640280a017d7b18d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:45:11 +0300 Subject: [PATCH 026/160] Use 1s interval for timeout timer for robustness --- src/util/timeout.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/timeout.ml b/src/util/timeout.ml index ce1352ef67..99fca2adcc 100644 --- a/src/util/timeout.ml +++ b/src/util/timeout.ml @@ -4,7 +4,7 @@ module Unix = struct let timeout f arg tsecs timeout_fn = let oldsig = Sys.signal Sys.sigprof (Sys.Signal_handle (fun _ -> timeout_fn ())) in (* https://ocaml.org/api/Unix.html#TYPEinterval_timer ITIMER_PROF is user (ITIMER_VIRTUAL) + system time; sends sigprof *) - ignore Unix.(setitimer ITIMER_PROF { it_interval = 0.0; it_value = tsecs }); + ignore Unix.(setitimer ITIMER_PROF { it_interval = 1.0; it_value = tsecs }); (* Keep signaling timeout with 1s interval, so if we happen to be inside a catch-all exception handler (which we shouldn't have to begin with...) then we'll have more opportinities for timing out. *) let res = f arg in Sys.set_signal Sys.sigprof oldsig; res From a4b5c8d3678ac2bff227aa24792a26a1f6649fd5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 29 Apr 2024 11:49:23 +0300 Subject: [PATCH 027/160] Document GobExn --- src/goblint_lib.ml | 2 ++ src/util/gobExn.ml | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 62abcbcb60..a8f0c71912 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -384,6 +384,8 @@ module MessageUtil = MessageUtil module AnsiColors = AnsiColors module XmlUtil = XmlUtil +module GobExn = GobExn + (** {2 CIL} *) module CilType = CilType diff --git a/src/util/gobExn.ml b/src/util/gobExn.ml index a43621b2b5..f14df57c2d 100644 --- a/src/util/gobExn.ml +++ b/src/util/gobExn.ml @@ -1,3 +1,7 @@ +(** Exception utilities. *) + +(** Filter for catching and swallowing all exceptions. + This avoids swallowing some very important exceptions that shouldn't be caught to begin with. *) let catch_all_filter = function | Out_of_memory | Stack_overflow -> false (* OCaml runtime *) @@ -7,3 +11,5 @@ let catch_all_filter = function | Sys.Break -> false (* Ctrl-C *) | Timeout.Timeout -> false (* Goblint timeout *) | _ -> true + +(* TODO: can't move to goblint.std because Timeout.Timeout *) From accdd77e89dd4d80f2bde8703f1e1fade289f8eb Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 5 May 2024 14:36:20 +0200 Subject: [PATCH 028/160] addresDomain.ml: add catch to get pre-#1435 behavior --- src/cdomain/value/cdomains/addressDomain.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index dc1ebfff7d..c62bfc843e 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -240,7 +240,14 @@ struct (** @raise Not_found if set is empty. *) let type_of xs = - Addr.type_of (choose xs) (* TODO: what if ambiguous type? what if chooses NullPtr but also contains Addr with proper type? *) + try + Addr.type_of (choose xs) (* TODO: what if ambiguous type? what if chooses NullPtr but also contains Addr with proper type? *) + with + (* to be consistent with pre-#1435 behavior *) + | Not_found + | Cilfacade.TypeOfError _ + | Offset.Type_of_error _ -> + voidPtrType let of_var x = singleton (Addr.of_var x) let of_mval x = singleton (Addr.of_mval x) From 86b50791e715cc9b2432298d9a63c599815b6719 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 11:20:07 +0200 Subject: [PATCH 029/160] Turn on precise output in privPrecCompare --- src/util/precCompare.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index c9b735de2d..180e2e8f88 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -163,6 +163,7 @@ struct let main () = Util.init (); + GobConfig.set_bool "dbg.full-output" true; (* Do not colorize stderr output *) AnsiColors.stderr := false; let filenames = List.tl (Array.to_list Sys.argv) in From d4a740f09800b74adeac269ab7940cf215769159 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 14:08:31 +0200 Subject: [PATCH 030/160] Add refine to traces config --- conf/traces.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/traces.json b/conf/traces.json index 0e4548ab4f..b8f061566f 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -4,7 +4,8 @@ "def_exc": true, "interval": false, "enums": true, - "def_exc_widen_by_join": true + "def_exc_widen_by_join": true, + "refinement" : "once" }, "malloc": { "wrappers": [ From 5ed5c37e883c74a1ab5b498d4ff2f7a2fee61678 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 14:12:21 +0200 Subject: [PATCH 031/160] Add refine to traces-rel --- conf/traces-rel.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/traces-rel.json b/conf/traces-rel.json index 405fb593cf..984b24c494 100644 --- a/conf/traces-rel.json +++ b/conf/traces-rel.json @@ -4,7 +4,8 @@ "def_exc": true, "interval": false, "enums": true, - "def_exc_widen_by_join": true + "def_exc_widen_by_join": true, + "refinement" : "once" }, "malloc": { "wrappers": [ From db6f47737ba5b2af04af1e682a075c8287f88844 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 14:19:51 +0200 Subject: [PATCH 032/160] Refine until fixpoint --- conf/traces-rel.json | 2 +- conf/traces.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/traces-rel.json b/conf/traces-rel.json index 984b24c494..7015d0394f 100644 --- a/conf/traces-rel.json +++ b/conf/traces-rel.json @@ -5,7 +5,7 @@ "interval": false, "enums": true, "def_exc_widen_by_join": true, - "refinement" : "once" + "refinement" : "fixpoint" }, "malloc": { "wrappers": [ diff --git a/conf/traces.json b/conf/traces.json index b8f061566f..053a0ff1ab 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -5,7 +5,7 @@ "interval": false, "enums": true, "def_exc_widen_by_join": true, - "refinement" : "once" + "refinement" : "fixpoint" }, "malloc": { "wrappers": [ From ac756f6feca4919268d98eb13bf01e165f12775f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 14:43:32 +0200 Subject: [PATCH 033/160] Undo enabling refinement --- conf/traces-rel.json | 3 +-- conf/traces.json | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/conf/traces-rel.json b/conf/traces-rel.json index 7015d0394f..405fb593cf 100644 --- a/conf/traces-rel.json +++ b/conf/traces-rel.json @@ -4,8 +4,7 @@ "def_exc": true, "interval": false, "enums": true, - "def_exc_widen_by_join": true, - "refinement" : "fixpoint" + "def_exc_widen_by_join": true }, "malloc": { "wrappers": [ diff --git a/conf/traces.json b/conf/traces.json index 053a0ff1ab..0e4548ab4f 100644 --- a/conf/traces.json +++ b/conf/traces.json @@ -4,8 +4,7 @@ "def_exc": true, "interval": false, "enums": true, - "def_exc_widen_by_join": true, - "refinement" : "fixpoint" + "def_exc_widen_by_join": true }, "malloc": { "wrappers": [ From 9e894bb2337f3849366b04e8b039d6f02dcb0ee5 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 14:55:26 +0200 Subject: [PATCH 034/160] `force_refine` on dumps --- src/analyses/basePriv.ml | 2 +- src/cdomain/value/cdomains/intDomain.ml | 28 +++++++++++++---------- src/cdomain/value/cdomains/intDomain.mli | 4 ++-- src/cdomain/value/cdomains/valueDomain.ml | 28 +++++++++++------------ 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index d08f007ffd..63e494b5c9 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1754,7 +1754,7 @@ struct let read_global ask getg st x = let v = Priv.read_global ask getg st x in if !AnalysisState.postsolving && !is_dumping then ( - let v_proj = if get_bool "exp.priv-prec-dump-proj" then VD.project (Queries.to_value_domain_ask ask) (Some (true,true,true,false,false)) None v else v in + let v_proj = if get_bool "exp.priv-prec-dump-proj" then VD.project ~force_refine:true (Queries.to_value_domain_ask ask) (Some (true,true,true,false,false)) None v else v in LVH.modify_def (VD.bot ()) (!Goblint_tracing.current_loc, x) (VD.join v_proj) lvh ); v diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 6c67279044..4194788dec 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -228,7 +228,7 @@ sig val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - val project: Cil.ikind -> int_precision -> t -> t + val project: ?force_refine:bool -> Cil.ikind -> int_precision -> t -> t val arbitrary: Cil.ikind -> t QCheck.arbitrary end @@ -275,7 +275,7 @@ sig val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t val is_top_of: Cil.ikind -> t -> bool - val project: int_precision -> t -> t + val project: ?force_refine:bool -> int_precision -> t -> t val invariant: Cil.exp -> t -> Invariant.t end @@ -381,7 +381,7 @@ struct let relift x = { v = I.relift x.v; ikind = x.ikind } - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } + let project ?(force_refine=false) p v = { v = I.project ~force_refine v.ikind p v.v; ikind = v.ikind } end module type Ikind = @@ -982,7 +982,7 @@ struct | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) | _, _-> intv - let project ik p t = t + let project ?(force_refine=false) ik p t = t end (** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) @@ -1515,7 +1515,7 @@ struct let excl_list = List.map (excl_to_intervalset ik range) xs in List.fold_left (meet ik) intv excl_list - let project ik p t = t + let project ?(force_refine=false) ik p t = t let arbitrary ik = let open QCheck.Iter in @@ -2322,7 +2322,7 @@ struct | _ -> a let refine_with_incl_list ik a b = a - let project ik p t = t + let project ?(force_refine=false) ik p t = t end (* BOOLEAN DOMAINS *) @@ -2743,7 +2743,7 @@ module Enums : S with type int_t = Z.t = struct | Inc x, Some (ls) -> meet ik (Inc x) (Inc (BISet.of_list ls)) | _ -> a - let project ik p t = t + let project ?(force_refine=false) ik p t = t end module Congruence : S with type int_t = Z.t and type t = (Z.t * Z.t) option = @@ -3239,7 +3239,7 @@ struct let refine_with_excl_list ik a b = a let refine_with_incl_list ik a b = a - let project ik p t = t + let project ?(force_refine=false) ik p t = t end module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct @@ -3520,8 +3520,11 @@ module IntDomTupleImpl = struct (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); (fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)] - let refine ik ((a, b, c, d, e) : t ) : t = + let refine ?(force_refine=false) ik ((a, b, c, d, e) : t ) : t = let dt = ref (a, b, c, d, e) in + if force_refine then + List.iter (fun f -> dt := f !dt) (refine_functions ik) + else (match get_refinement () with | "never" -> () | "once" -> @@ -3536,7 +3539,8 @@ module IntDomTupleImpl = struct if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a" pretty old_dt pretty !dt; done; | _ -> () - ); !dt + ); + !dt (* map with overflow check *) @@ -3645,9 +3649,9 @@ module IntDomTupleImpl = struct * ~keep:true will keep elements that are `Some x` but should be set to `None` by p. * This way we won't loose any information for the refinement. * ~keep:false will set the elements to `None` as defined by p *) - let project ik (p: int_precision) t = + let project ?(force_refine=false) ik (p: int_precision) t = let t_padded = map ~keep:true { f3 = fun (type a) (module I:SOverflow with type t = a) -> Some (I.top_of ik) } t p in - let t_refined = refine ik t_padded in + let t_refined = refine ~force_refine ik t_padded in map ~keep:false { f3 = fun (type a) (module I:SOverflow with type t = a) -> None } t_refined p diff --git a/src/cdomain/value/cdomains/intDomain.mli b/src/cdomain/value/cdomains/intDomain.mli index ca64692290..15a7ecf8bb 100644 --- a/src/cdomain/value/cdomains/intDomain.mli +++ b/src/cdomain/value/cdomains/intDomain.mli @@ -270,7 +270,7 @@ sig val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - val project: Cil.ikind -> PrecisionUtil.int_precision -> t -> t + val project: ?force_refine:bool -> Cil.ikind -> PrecisionUtil.int_precision -> t -> t val arbitrary: Cil.ikind -> t QCheck.arbitrary end (** Interface of IntDomain implementations taking an ikind for arithmetic operations *) @@ -330,7 +330,7 @@ sig val is_top_of: Cil.ikind -> t -> bool - val project: PrecisionUtil.int_precision -> t -> t + val project: ?force_refine:bool -> PrecisionUtil.int_precision -> t -> t val invariant: Cil.exp -> t -> Invariant.t end (** The signature of integral value domains keeping track of ikind information *) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index f28fe2b2ad..3a7147c132 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -40,7 +40,7 @@ sig include ArrayDomain.Null with type t := t - val project: VDQ.t -> int_precision option-> ( attributes * attributes ) option -> t -> t + val project: ?force_refine:bool -> VDQ.t -> int_precision option-> ( attributes * attributes ) option -> t -> t val mark_jmpbufs_as_copied: t -> t end @@ -1223,36 +1223,36 @@ struct let arbitrary () = QCheck.always Bot (* S TODO: other elements *) (*Changes the value: if p is present, change all Integer precisions. If array_attr=(varAttr, typeAttr) is present, change the top level array domain according to the attributes *) - let rec project ask p array_attr (v: t): t = + let rec project ?(force_refine=false) ask p array_attr (v: t): t = match v, p, array_attr with | _, None, None -> v (*Nothing to change*) (* as long as we only have one representation, project is a nop*) | Float n, _, _ -> Float n - | Int n, Some p, _-> Int (ID.project p n) - | Address n, Some p, _-> Address (project_addr p n) - | Struct n, _, _ -> Struct (Structs.map (fun (x: t) -> project ask p None x) n) - | Union (f, v), _, _ -> Union (f, project ask p None v) - | Array n , _, _ -> Array (project_arr ask p array_attr n) - | Blob (v, s, z), Some p', _ -> Blob (project ask p None v, ID.project p' s, z) + | Int n, Some p, _-> Int (ID.project ~force_refine p n) + | Address n, Some p, _-> Address (project_addr ~force_refine p n) + | Struct n, _, _ -> Struct (Structs.map (fun (x: t) -> project ~force_refine ask p None x) n) + | Union (f, v), _, _ -> Union (f, project ~force_refine ask p None v) + | Array n , _, _ -> Array (project_arr ~force_refine ask p array_attr n) + | Blob (v, s, z), Some p', _ -> Blob (project ~force_refine ask p None v, ID.project ~force_refine p' s, z) | Thread n, _, _ -> Thread n | Bot, _, _ -> Bot | Top, _, _ -> Top | _, _, _ -> v (*Nothing to change*) - and project_addr p a = + and project_addr ?(force_refine=false) p a = AD.map (fun addr -> match addr with - | Addr.Addr (v, o) -> Addr.Addr (v, project_offs p o) + | Addr.Addr (v, o) -> Addr.Addr (v, project_offs ~force_refine p o) | ptr -> ptr) a - and project_offs p offs = Offs.map_indices (ID.project p) offs - and project_arr ask p array_attr n = + and project_offs ?(force_refine=false) p offs = Offs.map_indices (ID.project ~force_refine p) offs + and project_arr ?(force_refine=false) ask p array_attr n = let n = match array_attr with | Some (varAttr,typAttr) -> CArrays.project ~varAttr ~typAttr ask n | _ -> n - in let n' = CArrays.map (fun (x: t) -> project ask p None x) n in + in let n' = CArrays.map (fun (x: t) -> project ~force_refine ask p None x) n in match CArrays.length n, p with | None, _ | _, None -> n' - | Some l, Some p -> CArrays.update_length (ID.project p l) n' + | Some l, Some p -> CArrays.update_length (ID.project ~force_refine p l) n' let relift state = match state with From 36748bdc3f038f84d9a846d74f1c31c10b35a82a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 15:53:49 +0200 Subject: [PATCH 035/160] Allow more cases in bot comparison --- src/cdomain/value/cdomains/valueDomain.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 3a7147c132..b8389ed775 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -536,6 +536,7 @@ struct if !AnalysisState.bot_in_blob_leq_bot then match x with | Blob (x,s,o) -> leq x Bot + | Array x when CArrays.is_bot x -> true | _ -> false else false From ebf5ffba80ffaedfd97c409bf90c666f08468533 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 10 May 2024 15:59:52 +0200 Subject: [PATCH 036/160] Even more cases --- src/cdomain/value/cdomains/valueDomain.ml | 68 ++++++++++++----------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index b8389ed775..c2ede35a34 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -527,39 +527,6 @@ struct let warn_type op x y = Logs.debug "warn_type %s: incomparable abstr. values %a and %a at %a: %a and %a" op pretty_tag x pretty_tag y CilType.Location.pretty !Goblint_tracing.current_loc pretty x pretty y - let rec leq x y = - match (x,y) with - | (_, Top) -> true - | (Top, _) -> false - | (Bot, _) -> true - | (x, Bot) -> - if !AnalysisState.bot_in_blob_leq_bot then - match x with - | Blob (x,s,o) -> leq x Bot - | Array x when CArrays.is_bot x -> true - | _ -> false - else - false - | (Int x, Int y) -> ID.leq x y - | (Float x, Float y) -> FD.leq x y - | (Int x, Address y) when ID.to_int x = Some Z.zero && not (AD.is_not_null y) -> true - | (Int _, Address y) when AD.may_be_unknown y -> true - | (Address _, Int y) when ID.is_top_of (Cilfacade.ptrdiff_ikind ()) y -> true - | (Address x, Address y) -> AD.leq x y - | (Struct x, Struct y) -> Structs.leq x y - | (Union x, Union y) -> Unions.leq x y - | (Array x, Array y) -> CArrays.leq x y - | (Blob x, Blob y) -> Blobs.leq x y - | Blob (x,s,o), y -> leq (x:t) y - | x, Blob (y,s,o) -> leq x (y:t) - | (Thread x, Thread y) -> Threads.leq x y - | (Int x, Thread y) -> true - | (Address x, Thread y) -> true - | (JmpBuf x, JmpBuf y) -> JmpBufs.leq x y - | (Mutex, Mutex) -> true - | (MutexAttr x, MutexAttr y) -> MutexAttr.leq x y - | _ -> warn_type "leq" x y; false - let rec join x y = match (x,y) with | (Top, _) -> Top @@ -592,6 +559,41 @@ struct warn_type "join" x y; Top + let rec leq x y = + match (x,y) with + | (_, Top) -> true + | (Top, _) -> false + | (Bot, _) -> true + | (x, Bot) -> + if !AnalysisState.bot_in_blob_leq_bot then + match x with + | Blob (x,s,o) -> leq x Bot + | Array x -> + let contents = CArrays.fold_left join Bot x in + leq contents Bot + | _ -> false + else + false + | (Int x, Int y) -> ID.leq x y + | (Float x, Float y) -> FD.leq x y + | (Int x, Address y) when ID.to_int x = Some Z.zero && not (AD.is_not_null y) -> true + | (Int _, Address y) when AD.may_be_unknown y -> true + | (Address _, Int y) when ID.is_top_of (Cilfacade.ptrdiff_ikind ()) y -> true + | (Address x, Address y) -> AD.leq x y + | (Struct x, Struct y) -> Structs.leq x y + | (Union x, Union y) -> Unions.leq x y + | (Array x, Array y) -> CArrays.leq x y + | (Blob x, Blob y) -> Blobs.leq x y + | Blob (x,s,o), y -> leq (x:t) y + | x, Blob (y,s,o) -> leq x (y:t) + | (Thread x, Thread y) -> Threads.leq x y + | (Int x, Thread y) -> true + | (Address x, Thread y) -> true + | (JmpBuf x, JmpBuf y) -> JmpBufs.leq x y + | (Mutex, Mutex) -> true + | (MutexAttr x, MutexAttr y) -> MutexAttr.leq x y + | _ -> warn_type "leq" x y; false + let widen x y = match (x,y) with | (Top, _) -> Top From 8bcbd2bbdb9a3af4afc86042e56ef3dad6667260 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 12 May 2024 13:52:14 +0200 Subject: [PATCH 037/160] Limit `Blob(\Bot) \leq \Bot` to malloced blobs --- src/cdomain/value/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index c2ede35a34..869cacdd4e 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -567,7 +567,7 @@ struct | (x, Bot) -> if !AnalysisState.bot_in_blob_leq_bot then match x with - | Blob (x,s,o) -> leq x Bot + | Blob (x,s,o) when o -> leq x Bot (* allow only in the case of not zero-initialized blobs *) | Array x -> let contents = CArrays.fold_left join Bot x in leq contents Bot From f48348e1e5e5788c10e08f95322de07cc4eb04f9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 12 May 2024 14:08:16 +0200 Subject: [PATCH 038/160] message for top --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1e11f8d6b8..82b3d4ab04 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -503,6 +503,8 @@ struct | _ -> x in let f x a = VD.join (c @@ f x) a in (* Finally we join over all the addresses in the set. *) + if !AnalysisState.postsolving && get_string "exp.priv-prec-dump" <> "" && AD.mem Addr.UnknownPtr addrs then + M.warn "DDDDDDebug: top_pointer at %a" d_loc !Goblint_tracing.current_loc; AD.fold f addrs (VD.bot ()) in if M.tracing then M.traceu "get" "Result: %a" VD.pretty res; From 08cc1a5e2287d76be82659d80d568dd5b2839f74 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 12 May 2024 14:19:52 +0200 Subject: [PATCH 039/160] topsify locations where unknown pointers are accessed --- src/analyses/base.ml | 3 ++- src/analyses/basePriv.ml | 2 ++ src/common/framework/analysisState.ml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 82b3d4ab04..a8d03e1a72 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -504,7 +504,8 @@ struct in let f x a = VD.join (c @@ f x) a in (* Finally we join over all the addresses in the set. *) if !AnalysisState.postsolving && get_string "exp.priv-prec-dump" <> "" && AD.mem Addr.UnknownPtr addrs then - M.warn "DDDDDDebug: top_pointer at %a" d_loc !Goblint_tracing.current_loc; + (Hashtbl.replace AnalysisState.location_to_topsify !Goblint_tracing.current_loc (); + M.warn "DDDDDDebug: top_pointer at %a" d_loc !Goblint_tracing.current_loc); AD.fold f addrs (VD.bot ()) in if M.tracing then M.traceu "get" "Result: %a" VD.pretty res; diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 63e494b5c9..23ef7e33d2 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1764,6 +1764,8 @@ struct (* LVH.iter (fun (l, x) v -> Logs.debug "%a %a = %a" CilType.Location.pretty l CilType.Varinfo.pretty x VD.pretty v ) lvh; *) + + LVH.map_inplace (fun (l,x) v -> if Hashtbl.mem AnalysisState.location_to_topsify l then VD.top () else v) lvh; Marshal.output f ({name = get_string "ana.base.privatization" ^ get_string "exp.priv-prec-dump-suffix"; results = lvh}: result); close_out_noerr f diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index cd9571a8ce..8ca89f8c28 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -33,3 +33,5 @@ let verified : bool option ref = ref None (* Comparison mode where blobs with bot content that are not zero-initalized are considered equivalent to top-level bot *) let bot_in_blob_leq_bot = ref false + +let location_to_topsify: (GoblintCil.location,unit) Hashtbl.t = Hashtbl.create 10 From 7a7f11c580656e461e3ecc91e46136216ffd3447 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 12 May 2024 14:40:17 +0200 Subject: [PATCH 040/160] Output stats about topsification --- src/analyses/basePriv.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 23ef7e33d2..ba5acd0911 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1764,8 +1764,9 @@ struct (* LVH.iter (fun (l, x) v -> Logs.debug "%a %a = %a" CilType.Location.pretty l CilType.Varinfo.pretty x VD.pretty v ) lvh; *) - - LVH.map_inplace (fun (l,x) v -> if Hashtbl.mem AnalysisState.location_to_topsify l then VD.top () else v) lvh; + let topisification_counter = ref 0 in + LVH.map_inplace (fun (l,x) v -> if Hashtbl.mem AnalysisState.location_to_topsify l then (incr topisification_counter; VD.top ()) else v) lvh; + Printf.printf "topsyfying %d variables\n\n\n" !topisification_counter; Marshal.output f ({name = get_string "ana.base.privatization" ^ get_string "exp.priv-prec-dump-suffix"; results = lvh}: result); close_out_noerr f From f6cf60e7f27be83da5eed8d4437c8d289aad909f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 12 May 2024 15:36:00 +0200 Subject: [PATCH 041/160] Add disregard table --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/basePriv.ml | 5 +---- src/util/precCompare.ml | 16 ++++++++++++++-- src/util/precCompareUtil.ml | 2 ++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index f1ea72d0a1..ed3cb29952 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -778,7 +778,7 @@ struct let store_data file = let results = PCU.RH.map (fun _ v -> RD.marshal v) results in let name = RD.name () ^ "(domain: " ^ (RD.name ()) ^ ", privatization: " ^ (Priv.name ()) ^ (if GobConfig.get_bool "ana.apron.threshold_widening" then ", th" else "" ) ^ ")" in - let results: PCU.dump = {marshalled = results; name } in + let results: PCU.dump = {marshalled = results; name; disregard = None } in Serialize.marshal results file let finalize () = diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index ba5acd0911..7e5bd9991f 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1764,10 +1764,7 @@ struct (* LVH.iter (fun (l, x) v -> Logs.debug "%a %a = %a" CilType.Location.pretty l CilType.Varinfo.pretty x VD.pretty v ) lvh; *) - let topisification_counter = ref 0 in - LVH.map_inplace (fun (l,x) v -> if Hashtbl.mem AnalysisState.location_to_topsify l then (incr topisification_counter; VD.top ()) else v) lvh; - Printf.printf "topsyfying %d variables\n\n\n" !topisification_counter; - Marshal.output f ({name = get_string "ana.base.privatization" ^ get_string "exp.priv-prec-dump-suffix"; results = lvh}: result); + Marshal.output f ({name = get_string "ana.base.privatization" ^ get_string "exp.priv-prec-dump-suffix"; results = lvh; disregard = Some AnalysisState.location_to_topsify}: result); close_out_noerr f let finalize () = diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 180e2e8f88..2386367967 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -97,7 +97,7 @@ struct let load filename = let f = open_in_bin filename in let dump: dump = Marshal.from_channel f in - let dump: result = {name = dump.name; results = unmarshal dump.marshalled } in + let dump: result = {name = dump.name; results = unmarshal dump.marshalled; disregard = dump.disregard } in close_in_noerr f; dump @@ -105,7 +105,19 @@ struct let comparisons = ref [] - let compare_dumps ({name = name1; results = lvh1}: result) ({name = name2; results = lvh2}: result) = + let compare_dumps ({name = name1; results = lvh1; disregard = disregard1}: result) ({name = name2; results = lvh2; disregard = disregard2}: result) = + let (lvh1:Dom.t RH.t) = + match disregard1, disregard2 with + | Some disregard1, Some disregard2 -> + RH.filter_map (fun k v -> if Hashtbl.mem disregard1 (Key.to_location k) || Hashtbl.mem disregard2 (Key.to_location k) then None else Some v) lvh1 + | _ -> lvh1 + in + let (lvh2:Dom.t RH.t) = + match disregard1, disregard2 with + | Some disregard1, Some disregard2 -> + RH.filter_map (fun k v -> if Hashtbl.mem disregard1 (Key.to_location k) || Hashtbl.mem disregard2 (Key.to_location k) then None else Some v) lvh2 + | _ -> lvh2 + in let (c, d) = CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 in comparisons := (name1, name2, c, d) :: !comparisons; (c, d) diff --git a/src/util/precCompareUtil.ml b/src/util/precCompareUtil.ml index 98ba1dfede..fd8f96fbb7 100644 --- a/src/util/precCompareUtil.ml +++ b/src/util/precCompareUtil.ml @@ -13,11 +13,13 @@ end type 'a dump_gen = { name: string; marshalled: 'a; + disregard: (GoblintCil.location,unit) Hashtbl.t option } type 'a result_gen = { name: string; results: 'a; + disregard: (GoblintCil.location,unit) Hashtbl.t option } module type R = From c923a89fbcd87b7aac617162f80d3c2029bb43ba Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 10:15:18 +0200 Subject: [PATCH 042/160] CReduce script to debug privatization precision differences --- reduce_difference.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 reduce_difference.sh diff --git a/reduce_difference.sh b/reduce_difference.sh new file mode 100755 index 0000000000..eb0a83a149 --- /dev/null +++ b/reduce_difference.sh @@ -0,0 +1,23 @@ +#!/bin/bash +grep "void (\*argmatch_die)(void) = & __argmatch_die;" reduce.c +if [ $? -eq 1 ]; then + exit 17 +fi +grep "static void __argmatch_die(void)" reduce.c +if [ $? -eq 1 ]; then + exit 17 +fi +grep "usage(1)" reduce.c +if [ $? -eq 1 ]; then + exit 17 +fi + + +/home/michael/Documents/goblint-cil/analyzer/goblint-2 --conf /home/michael/Documents/goblint-cil/analyzer/conf/traces.json --set dbg.timeout 900 --set ana.base.privatization protection 1.c --enable allglobs --enable dbg.timing.enabled --enable warn.debug -v --sets exp.priv-prec-dump level-ip.protection.prec +/home/michael/Documents/goblint-cil/analyzer/goblint-2 --conf /home/michael/Documents/goblint-cil/analyzer/conf/traces.json --set dbg.timeout 900 --set ana.base.privatization write 1.c --enable allglobs --enable dbg.timing.enabled --enable warn.debug -v --sets exp.priv-prec-dump level-ip.write.prec +/home/michael/Documents/goblint-cil/analyzer/privPrecCompare level-ip.protection.prec level-ip.write.prec 2> details.txt 1> res.txt +if [ $? -eq 0 ]; then + grep "protection more precise than write" res.txt >/dev/null 2>&1 +else + exit 5 +fi From 08e0ec25f05eded2f7a1ce11eb303dfca54be913 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 11:19:21 +0200 Subject: [PATCH 043/160] Minimized example --- 2.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 2.c diff --git a/2.c b/2.c new file mode 100644 index 0000000000..922f8acd85 --- /dev/null +++ b/2.c @@ -0,0 +1,35 @@ +#include +#include +struct a { + struct a *b; +}; + +struct a *ptr; + + +pthread_mutex_t m; + +void doit() { + void *newa = malloc(sizeof(struct a)); + + pthread_mutex_lock(&m); + ptr->b = newa; + ptr = newa; + pthread_mutex_unlock(&m); +} + +void* k(void *arg) { + doit(); + return NULL; +} + +int main() { + struct a other; + ptr = &other; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} From 57cdd43aa680d3349b6df0dd87504356863cbfcd Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 11:19:42 +0200 Subject: [PATCH 044/160] More --- 2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2.c b/2.c index 922f8acd85..6a60488335 100644 --- a/2.c +++ b/2.c @@ -10,7 +10,7 @@ struct a *ptr; pthread_mutex_t m; void doit() { - void *newa = malloc(sizeof(struct a)); + struct a *newa = (struct a *)malloc(sizeof(struct a)); pthread_mutex_lock(&m); ptr->b = newa; From 44eb176725b0fe5ab7e66b443e9e56d612a4d0e8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 12:45:20 +0200 Subject: [PATCH 045/160] Add problematic examples for #1457 --- .../13-privatized/80-treachery-and-lies.c | 54 ++++++++++++++++++ .../81-how-could-we-have-been-so-blind.c | 55 ++++++++++++++++++ .../13-privatized/82-well-laid-out-plans.c | 56 +++++++++++++++++++ .../83-treachery-and-lies-write.c | 54 ++++++++++++++++++ ...84-how-could-we-have-been-so-blind-write.c | 55 ++++++++++++++++++ .../85-well-laid-out-plans-write.c | 56 +++++++++++++++++++ .../86-treachery-and-lies-lock.c | 54 ++++++++++++++++++ .../87-how-could-we-have-been-so-blind-lock.c | 55 ++++++++++++++++++ .../88-well-laid-out-plans-lock.c | 56 +++++++++++++++++++ 9 files changed, 495 insertions(+) create mode 100644 tests/regression/13-privatized/80-treachery-and-lies.c create mode 100644 tests/regression/13-privatized/81-how-could-we-have-been-so-blind.c create mode 100644 tests/regression/13-privatized/82-well-laid-out-plans.c create mode 100644 tests/regression/13-privatized/83-treachery-and-lies-write.c create mode 100644 tests/regression/13-privatized/84-how-could-we-have-been-so-blind-write.c create mode 100644 tests/regression/13-privatized/85-well-laid-out-plans-write.c create mode 100644 tests/regression/13-privatized/86-treachery-and-lies-lock.c create mode 100644 tests/regression/13-privatized/87-how-could-we-have-been-so-blind-lock.c create mode 100644 tests/regression/13-privatized/88-well-laid-out-plans-lock.c diff --git a/tests/regression/13-privatized/80-treachery-and-lies.c b/tests/regression/13-privatized/80-treachery-and-lies.c new file mode 100644 index 0000000000..7670bad8f8 --- /dev/null +++ b/tests/regression/13-privatized/80-treachery-and-lies.c @@ -0,0 +1,54 @@ +// PARAM: --set ana.base.privatization protection --enable ana.int.enums +#include +#include +struct a { + int b; +}; + +struct a *ptr; +struct a *straightandnarrow; + +pthread_mutex_t m; + +void doit() { + void *newa = malloc(sizeof(struct a)); + + pthread_mutex_lock(&m); + ptr->b = 5; + + int fear = straightandnarrow->b; + __goblint_check(fear == 5); //UNKNOWN! + + ptr = newa; + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = straightandnarrow->b; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + doit(); + return NULL; +} + +int main() { + int top; + struct a other = { 0 }; + struct a other2 = { 42 }; + if(top) { + ptr = &other; + } else { + ptr = &other2; + } + + straightandnarrow = &other; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/81-how-could-we-have-been-so-blind.c b/tests/regression/13-privatized/81-how-could-we-have-been-so-blind.c new file mode 100644 index 0000000000..d6a33dece4 --- /dev/null +++ b/tests/regression/13-privatized/81-how-could-we-have-been-so-blind.c @@ -0,0 +1,55 @@ +// PARAM: --set ana.base.privatization protection --enable ana.int.enums +// Like 80-treachery-and-lies.c, but somewhat simplified to not use structs and malloc etc +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 5 or 0, depending on which one the pointer points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + int da_oane = 0; + int de_andre = 42; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/82-well-laid-out-plans.c b/tests/regression/13-privatized/82-well-laid-out-plans.c new file mode 100644 index 0000000000..8eb1cc11e1 --- /dev/null +++ b/tests/regression/13-privatized/82-well-laid-out-plans.c @@ -0,0 +1,56 @@ +// PARAM: --set ana.base.privatization protection --enable ana.int.enums +// Like 81-how-could-we-have-been-so-blind.c, but with syntactic globals instead of escaping ones. +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +int da_oane = 0; +int de_andre = 42; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 0 or 5, depending on which one ptr points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + // This works + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/83-treachery-and-lies-write.c b/tests/regression/13-privatized/83-treachery-and-lies-write.c new file mode 100644 index 0000000000..f317100825 --- /dev/null +++ b/tests/regression/13-privatized/83-treachery-and-lies-write.c @@ -0,0 +1,54 @@ +// PARAM: --set ana.base.privatization write --enable ana.int.enums +#include +#include +struct a { + int b; +}; + +struct a *ptr; +struct a *straightandnarrow; + +pthread_mutex_t m; + +void doit() { + void *newa = malloc(sizeof(struct a)); + + pthread_mutex_lock(&m); + ptr->b = 5; + + int fear = straightandnarrow->b; + __goblint_check(fear == 5); //UNKNOWN! + + ptr = newa; + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = straightandnarrow->b; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + doit(); + return NULL; +} + +int main() { + int top; + struct a other = { 0 }; + struct a other2 = { 42 }; + if(top) { + ptr = &other; + } else { + ptr = &other2; + } + + straightandnarrow = &other; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/84-how-could-we-have-been-so-blind-write.c b/tests/regression/13-privatized/84-how-could-we-have-been-so-blind-write.c new file mode 100644 index 0000000000..c62e49ba69 --- /dev/null +++ b/tests/regression/13-privatized/84-how-could-we-have-been-so-blind-write.c @@ -0,0 +1,55 @@ +// PARAM: --set ana.base.privatization write --enable ana.int.enums +// Like 80-treachery-and-lies.c, but somewhat simplified to not use structs and malloc etc +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 5 or 0, depending on which one the pointer points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + int da_oane = 0; + int de_andre = 42; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/85-well-laid-out-plans-write.c b/tests/regression/13-privatized/85-well-laid-out-plans-write.c new file mode 100644 index 0000000000..246804f82e --- /dev/null +++ b/tests/regression/13-privatized/85-well-laid-out-plans-write.c @@ -0,0 +1,56 @@ +// PARAM: --set ana.base.privatization write --enable ana.int.enums +// Like 81-how-could-we-have-been-so-blind.c, but with syntactic globals instead of escaping ones. +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +int da_oane = 0; +int de_andre = 42; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 0 or 5, depending on which one ptr points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + // This works + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/86-treachery-and-lies-lock.c b/tests/regression/13-privatized/86-treachery-and-lies-lock.c new file mode 100644 index 0000000000..aa21f89ea7 --- /dev/null +++ b/tests/regression/13-privatized/86-treachery-and-lies-lock.c @@ -0,0 +1,54 @@ +// PARAM: --set ana.base.privatization lock --enable ana.int.enums +#include +#include +struct a { + int b; +}; + +struct a *ptr; +struct a *straightandnarrow; + +pthread_mutex_t m; + +void doit() { + void *newa = malloc(sizeof(struct a)); + + pthread_mutex_lock(&m); + ptr->b = 5; + + int fear = straightandnarrow->b; + __goblint_check(fear == 5); //UNKNOWN! + + ptr = newa; + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = straightandnarrow->b; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + doit(); + return NULL; +} + +int main() { + int top; + struct a other = { 0 }; + struct a other2 = { 42 }; + if(top) { + ptr = &other; + } else { + ptr = &other2; + } + + straightandnarrow = &other; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/87-how-could-we-have-been-so-blind-lock.c b/tests/regression/13-privatized/87-how-could-we-have-been-so-blind-lock.c new file mode 100644 index 0000000000..02f5b721ba --- /dev/null +++ b/tests/regression/13-privatized/87-how-could-we-have-been-so-blind-lock.c @@ -0,0 +1,55 @@ +// PARAM: --set ana.base.privatization lock --enable ana.int.enums +// Like 80-treachery-and-lies.c, but somewhat simplified to not use structs and malloc etc +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 5 or 0, depending on which one the pointer points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + int da_oane = 0; + int de_andre = 42; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} diff --git a/tests/regression/13-privatized/88-well-laid-out-plans-lock.c b/tests/regression/13-privatized/88-well-laid-out-plans-lock.c new file mode 100644 index 0000000000..9caae2daf1 --- /dev/null +++ b/tests/regression/13-privatized/88-well-laid-out-plans-lock.c @@ -0,0 +1,56 @@ +// PARAM: --set ana.base.privatization lock --enable ana.int.enums +// Like 81-how-could-we-have-been-so-blind.c, but with syntactic globals instead of escaping ones. +#include +#include +struct a { + int b; +}; + +int *ptr; +int *immer_da_oane; + +int da_oane = 0; +int de_andre = 42; + +pthread_mutex_t m; + +void doit() { + pthread_mutex_lock(&m); + *ptr = 5; + + // Should be either 0 or 5, depending on which one ptr points to + int fear = *immer_da_oane; + __goblint_check(fear == 5); //UNKNOWN! + + pthread_mutex_unlock(&m); + + pthread_mutex_lock(&m); + // This works + int hope = *immer_da_oane; + __goblint_check(hope == 5); //UNKNOWN! + pthread_mutex_unlock(&m); + +} + +void* k(void *arg) { + // Force MT + return NULL; +} + +int main() { + int top; + + if(top) { + ptr = &da_oane; + } else { + ptr = &de_andre; + } + + immer_da_oane = &da_oane; + + pthread_t t1; + pthread_create(&t1, 0, k, 0); + + doit(); + return 0; +} From d033d7df643e857579aecb2b6612819467843a40 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 13:21:27 +0200 Subject: [PATCH 046/160] `set`: For non-definite AD, join over **all** components not just `cpa` Earlier `priv` was not joined over, which lead to unsound results #1457. On top, this also considers `dep` and `weak`. While not strictly necessary to pass tests, this also seems to be the right thing here. --- 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 a8d03e1a72..c3ce3b19ba 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1723,7 +1723,7 @@ struct (* if M.tracing then M.tracel "set" ~var:firstvar "new state1 %a" CPA.pretty nst; *) (* If the address was definite, then we just return it. If the address * was ambiguous, we have to join it with the initial state. *) - let nst = if AD.cardinal lval > 1 then { nst with cpa = CPA.join st.cpa nst.cpa } else nst in + let nst = if AD.cardinal lval > 1 then D.join st nst else nst in (* if M.tracing then M.tracel "set" ~var:firstvar "new state2 %a" CPA.pretty nst; *) nst with From febcf0929bcf0d1c9dae536cd847d6bee6030754 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 13 May 2024 14:47:12 +0200 Subject: [PATCH 047/160] Rm spurious file --- 2.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 2.c diff --git a/2.c b/2.c deleted file mode 100644 index 6a60488335..0000000000 --- a/2.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -struct a { - struct a *b; -}; - -struct a *ptr; - - -pthread_mutex_t m; - -void doit() { - struct a *newa = (struct a *)malloc(sizeof(struct a)); - - pthread_mutex_lock(&m); - ptr->b = newa; - ptr = newa; - pthread_mutex_unlock(&m); -} - -void* k(void *arg) { - doit(); - return NULL; -} - -int main() { - struct a other; - ptr = &other; - - pthread_t t1; - pthread_create(&t1, 0, k, 0); - - doit(); - return 0; -} From d2d01d2b356f8dcb600d1cc3259845a9feb4c1af Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 15 May 2024 10:27:57 +0200 Subject: [PATCH 048/160] Add example where write lack precision --- .../89-write-lacking-precision.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/regression/13-privatized/89-write-lacking-precision.c diff --git a/tests/regression/13-privatized/89-write-lacking-precision.c b/tests/regression/13-privatized/89-write-lacking-precision.c new file mode 100644 index 0000000000..f2ec583330 --- /dev/null +++ b/tests/regression/13-privatized/89-write-lacking-precision.c @@ -0,0 +1,30 @@ +// PARAM: --set ana.base.privatization write --disable exp.priv-distr-init +#include +struct a { + char* b; +}; + +struct a *c; +struct a h = {""}; +struct a i = {"string"}; + +void* d(void* args) { + struct a r; + if (c->b) { + __goblint_check(strlen(h.b) == 0); // Should also work for write! + } +} + +int main() { + int top; + + if(top) { + c = &h; + } else { + c = &i; + } + + pthread_t t; + pthread_create(&t, 0, d, 0); + return 0; +} From 0389355fb503ad1813970d3e5b984e6f984a0a7d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 15 May 2024 10:36:32 +0200 Subject: [PATCH 049/160] Rm unneeded argument --- tests/regression/13-privatized/89-write-lacking-precision.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/13-privatized/89-write-lacking-precision.c b/tests/regression/13-privatized/89-write-lacking-precision.c index f2ec583330..75ee78974d 100644 --- a/tests/regression/13-privatized/89-write-lacking-precision.c +++ b/tests/regression/13-privatized/89-write-lacking-precision.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.base.privatization write --disable exp.priv-distr-init +// PARAM: --set ana.base.privatization write #include struct a { char* b; From 2f66c5719446c04e833659e374fe47af1f1f24f1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 15 May 2024 15:49:59 +0200 Subject: [PATCH 050/160] Typo --- 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 c3ce3b19ba..2cd4aa1456 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1564,7 +1564,7 @@ struct let set ~(ctx: _ ctx) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = let update_variable x t y z = if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a" x.vname VD.pretty y CPA.pretty z; - let r = update_variable x t y z in (* refers to defintion that is outside of set *) + let r = update_variable x t y z in (* refers to definition that is outside of set *) if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a\nresults in\n%a" x.vname VD.pretty y CPA.pretty z CPA.pretty r; r in From 53f9a1ad26d0e60822525278f0d3d870400fb016 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 15 May 2024 15:53:12 +0200 Subject: [PATCH 051/160] Add `amenable_to_meet` and test for it --- src/cdomain/value/cdomains/addressDomain.ml | 5 +++++ src/cdomain/value/cdomains/addressDomain_intf.ml | 5 ++++- src/domain/disjointDomain.ml | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index c62bfc843e..957fd1c311 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -110,6 +110,10 @@ struct | StrPtr _, UnknownPtr -> None | _, _ -> Some false + let amenable_to_meet x y = match x,y with + | StrPtr _, StrPtr _ -> true + | _ -> false + let leq x y = match x, y with | StrPtr s1, StrPtr s2 -> SD.leq s1 s2 | Addr x, Addr y -> Mval.leq x y @@ -178,6 +182,7 @@ struct struct include SetDomain.Joined (Addr) let may_be_equal a b = Option.value (Addr.semantic_equal a b) ~default:true + let amenable_to_meet = Addr.amenable_to_meet end module OffsetSplit = DisjointDomain.ProjectiveSetPairwiseMeet (Addr) (J) (Addr.UnitOffsetRepr) diff --git a/src/cdomain/value/cdomains/addressDomain_intf.ml b/src/cdomain/value/cdomains/addressDomain_intf.ml index f65b2977c4..78c65dd98a 100644 --- a/src/cdomain/value/cdomains/addressDomain_intf.ml +++ b/src/cdomain/value/cdomains/addressDomain_intf.ml @@ -80,8 +80,11 @@ sig val semantic_equal: t -> t -> bool option (** Check semantic equality of two addresses. - @return [Some true] if definitely equal, [Some false] if definitely not equal, [None] if unknown. *) + + val amenable_to_meet: t -> t -> bool + (** Whether two addresses are amenable to meet operation, i.e., their lattice meet overapproximates the intersection + of concretizations. If true, meet is used instead of semantic_equal *) end (** Address lattice with sublattice representatives for {!DisjointDomain}. *) diff --git a/src/domain/disjointDomain.ml b/src/domain/disjointDomain.ml index d8e59c4ba7..00f875bb3a 100644 --- a/src/domain/disjointDomain.ml +++ b/src/domain/disjointDomain.ml @@ -182,16 +182,26 @@ module type MayEqualSetDomain = sig include SetDomain.S val may_be_equal: elt -> elt -> bool + val amenable_to_meet: elt -> elt -> bool end -module ProjectiveSetPairwiseMeet (E: Printable.S) (B: MayEqualSetDomain with type elt = E.t) (R: Representative with type elt = E.t): SetDomain.S with type elt = E.t = struct +module ProjectiveSetPairwiseMeet (E: Lattice.S) (B: MayEqualSetDomain with type elt = E.t) (R: Representative with type elt = E.t): SetDomain.S with type elt = E.t = struct include ProjectiveSet (E) (B) (R) let meet m1 m2 = let meet_buckets b1 b2 acc = B.fold (fun e1 acc -> B.fold (fun e2 acc -> - if B.may_be_equal e1 e2 then + if B.amenable_to_meet e1 e2 then + try + let m = E.meet e1 e2 in + if not (E.is_bot m) then + add m acc + else + acc + with Lattice.Uncomparable -> + failwith (GobPretty.sprintf "amenable_to_meet %a %a returned true, but meet throws!" E.pretty e1 E.pretty e2) + else if B.may_be_equal e1 e2 then add e1 (add e2 acc) else acc From a763a7f4d024b53ae43242d48927e26701eb6d2f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 15 May 2024 16:08:53 +0200 Subject: [PATCH 052/160] Make comparison of pointers amenable to `meet` if they only differ in offsets --- src/cdomain/value/cdomains/addressDomain.ml | 1 + .../27-inv_invariants/22-meet-ptrs.c | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/regression/27-inv_invariants/22-meet-ptrs.c diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index 957fd1c311..654f4ee2e1 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -112,6 +112,7 @@ struct let amenable_to_meet x y = match x,y with | StrPtr _, StrPtr _ -> true + | Addr x, Addr y when Mval.equal (Mval.top_indices x) (Mval.top_indices y) -> true | _ -> false let leq x y = match x, y with diff --git a/tests/regression/27-inv_invariants/22-meet-ptrs.c b/tests/regression/27-inv_invariants/22-meet-ptrs.c new file mode 100644 index 0000000000..33adfa879f --- /dev/null +++ b/tests/regression/27-inv_invariants/22-meet-ptrs.c @@ -0,0 +1,24 @@ +//PARAM: --enable ana.int.interval +#include +#include +#include + + +int main() { + int arr[20]; + + int top; + + int i = 2; + if(top) { + i = 8; + } + + int* imprecise = &arr[i]; + + if(imprecise == &arr[2]) { + __goblint_check(imprecise == &arr[2]); + } + + return 0; +} From 911da1783e4222f778c9ce85345f3983f511fd39 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 10:20:56 +0200 Subject: [PATCH 053/160] Add example where write is less precise --- 2.c | 58 +++++++++++++++++++++++ tests/regression/13-privatized/90-union.c | 29 ++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 2.c create mode 100644 tests/regression/13-privatized/90-union.c diff --git a/2.c b/2.c new file mode 100644 index 0000000000..fcd676ef35 --- /dev/null +++ b/2.c @@ -0,0 +1,58 @@ +struct a { + int (*c)(); +} q, *r[] = {&q}; +struct d { + char f; +}; +union g { + int h; + struct d i; +}; +struct j { + union g data; +} * aa; +struct k { + struct j e; +} * w; +struct l { + struct k *m; +} * ab; +int n, y; +char s; +void *memset(); +void *malloc(); +void o(); +void main() { o(); } +int(pthread_create)(); +void c(); +void o() { c(); } +void c() { + struct a b = **r; + b.c(s, n); +} +void t(); +int u(char *z, int ac) { t(); } +struct a q = {u}; +void v(struct j *z) { + if (z->data.i.f) + ; +} +void x(struct k **z, struct j *ac) { + void *ae = malloc(sizeof(struct k)); + w = ae; + w->e = *ac; + *z = ae; +} +void af() { + while (!y) { + void *ae = malloc(sizeof(struct l)), *ag = malloc(sizeof(struct j)); + ab = ae; + aa = ag; + memset(aa, 0, sizeof(struct j)); + x(&ab->m, aa); + struct l p = *ab; + struct k *ad = p.m; + v(&ad->e); + } +} +void t() { long ah = pthread_create(&ah, 0, af, 0); } diff --git a/tests/regression/13-privatized/90-union.c b/tests/regression/13-privatized/90-union.c new file mode 100644 index 0000000000..df5990d7a4 --- /dev/null +++ b/tests/regression/13-privatized/90-union.c @@ -0,0 +1,29 @@ +// PARAM: --set ana.base.privatization write +#include +#include +#include +union g { + int h; + int i; +}; +struct j { + union g data; +}; + + +void* af(void* arg) { + int top; + struct j* jptr = (struct j*) malloc(sizeof(struct j)); + memset(jptr, 0, sizeof(struct j)); + + while (top) { + if (jptr->data.i); + + __goblint_check(jptr->data.i == 0); // Should alo work for write! + } +} + +void main() { + pthread_t ah; + pthread_create(&ah, 0, af, 0); +} From ae72bdd7643f70fb8d8cb3d490fc629ac0470123 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 10:26:30 +0200 Subject: [PATCH 054/160] Add union direct --- .../13-privatized/91-union-direct.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/regression/13-privatized/91-union-direct.c diff --git a/tests/regression/13-privatized/91-union-direct.c b/tests/regression/13-privatized/91-union-direct.c new file mode 100644 index 0000000000..bf1127d389 --- /dev/null +++ b/tests/regression/13-privatized/91-union-direct.c @@ -0,0 +1,26 @@ +// PARAM: --set ana.base.privatization write +#include +#include +#include +union g { + int h; + int i; +}; + +void* af(void* arg) { + // Go MT +} + +void main() { + pthread_t ah; + pthread_create(&ah, 0, af, 0); + + int top; + union g* jptr = (union g*) malloc(sizeof(union g)); + memset(jptr, 0, sizeof(union g)); + + while (top) { + if (jptr->i); + __goblint_check(jptr->i == 0); // Should alo work for write! + } +} From a0f6bcd43bc32f7fb67b9f3e197cb65f7d5dc70e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 11:30:09 +0200 Subject: [PATCH 055/160] `meet` in `Union`: Do not raise uncomparable when one arg is Top --- src/cdomain/value/cdomains/unionDomain.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/unionDomain.ml b/src/cdomain/value/cdomains/unionDomain.ml index ad5c531061..cb61b93ab1 100644 --- a/src/cdomain/value/cdomains/unionDomain.ml +++ b/src/cdomain/value/cdomains/unionDomain.ml @@ -26,7 +26,11 @@ module Field = struct if equal f g then f else - raise Lattice.Uncomparable + match f, g with + | `Top, f + | f, `Top -> f + | _ -> raise Lattice.Uncomparable + end module Simple (Values: Arg) = From 63e5427e2b7683b6edd2b39957c65445bc9e9a67 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 12:48:25 +0200 Subject: [PATCH 056/160] Experiment without disregard --- src/util/precCompare.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 2386367967..50f9042050 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -97,7 +97,7 @@ struct let load filename = let f = open_in_bin filename in let dump: dump = Marshal.from_channel f in - let dump: result = {name = dump.name; results = unmarshal dump.marshalled; disregard = dump.disregard } in + let dump: result = {name = dump.name; results = unmarshal dump.marshalled; disregard = None } in close_in_noerr f; dump From 37e5784de028bf9fdf590827411338910e673f05 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 14:54:09 +0200 Subject: [PATCH 057/160] Revert "Experiment without disregard" This reverts commit 63e5427e2b7683b6edd2b39957c65445bc9e9a67. --- src/util/precCompare.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 50f9042050..2386367967 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -97,7 +97,7 @@ struct let load filename = let f = open_in_bin filename in let dump: dump = Marshal.from_channel f in - let dump: result = {name = dump.name; results = unmarshal dump.marshalled; disregard = None } in + let dump: result = {name = dump.name; results = unmarshal dump.marshalled; disregard = dump.disregard } in close_in_noerr f; dump From 1d63b11ce9f73bff8300e57eae0ac61c3f2522c3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 16:13:25 +0200 Subject: [PATCH 058/160] Add example where a difference due to widening can be observed --- example_widening_effects.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 example_widening_effects.c diff --git a/example_widening_effects.c b/example_widening_effects.c new file mode 100644 index 0000000000..ab0c9e5761 --- /dev/null +++ b/example_widening_effects.c @@ -0,0 +1,31 @@ +#include +struct s { + int occupied; + int closed; +}; + +struct s pqb; +pthread_mutex_t mtx; + +void pqueue_close() { + pthread_mutex_lock(&mtx); + pqb.closed = 1; + pthread_mutex_unlock(&mtx); +} + +void* thread(void* arg) { + pthread_mutex_lock(&mtx); + + if(pqb.occupied < 2) { + pqb.occupied++; + } + pthread_mutex_unlock(&mtx); + } + +int main() { + pthread_t worker; + + pthread_create(&worker, 0, &thread, 0); + pqueue_close(); + return 0; +} From b140ac280cf2ae91896d8e8f1b3040edc32473b5 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 17 May 2024 16:13:54 +0200 Subject: [PATCH 059/160] Comment command --- example_widening_effects.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example_widening_effects.c b/example_widening_effects.c index ab0c9e5761..9a4f642eed 100644 --- a/example_widening_effects.c +++ b/example_widening_effects.c @@ -29,3 +29,6 @@ int main() { pqueue_close(); return 0; } + + +// rm out.txt && ./goblint --conf conf/traces.json --set ana.base.privatization protection 2.c --enable allglobs --enable dbg.timing.enabled --enable warn.debug -v --sets exp.priv-prec-dump level-ip.protection.prec --enable ana.int.interval && ./goblint --conf conf/traces.json --set ana.base.privatization write 2.c --enable allglobs --enable dbg.timing.enabled --enable warn.debug -v --sets exp.priv-prec-dump level-ip.write.prec --enable ana.int.interval && ./privPrecCompare level-ip.protection.prec level-ip.write.prec &> out.txt From 755d87facdf84a53f82991963b3b3768c8dc884d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 14:05:39 +0200 Subject: [PATCH 060/160] Rm 2.c --- 2.c | 58 ---------------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 2.c diff --git a/2.c b/2.c deleted file mode 100644 index fcd676ef35..0000000000 --- a/2.c +++ /dev/null @@ -1,58 +0,0 @@ -struct a { - int (*c)(); -} q, *r[] = {&q}; -struct d { - char f; -}; -union g { - int h; - struct d i; -}; -struct j { - union g data; -} * aa; -struct k { - struct j e; -} * w; -struct l { - struct k *m; -} * ab; -int n, y; -char s; -void *memset(); -void *malloc(); -void o(); -void main() { o(); } -int(pthread_create)(); -void c(); -void o() { c(); } -void c() { - struct a b = **r; - b.c(s, n); -} -void t(); -int u(char *z, int ac) { t(); } -struct a q = {u}; -void v(struct j *z) { - if (z->data.i.f) - ; -} -void x(struct k **z, struct j *ac) { - void *ae = malloc(sizeof(struct k)); - w = ae; - w->e = *ac; - *z = ae; -} -void af() { - while (!y) { - void *ae = malloc(sizeof(struct l)), *ag = malloc(sizeof(struct j)); - ab = ae; - aa = ag; - memset(aa, 0, sizeof(struct j)); - x(&ab->m, aa); - struct l p = *ab; - struct k *ad = p.m; - v(&ad->e); - } -} -void t() { long ah = pthread_create(&ah, 0, af, 0); } From 8fae8ba8c3ee7b260d692a30e62fca005e1f7c22 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 14:14:22 +0200 Subject: [PATCH 061/160] Update example --- example_widening_effects.c | 1 + 1 file changed, 1 insertion(+) diff --git a/example_widening_effects.c b/example_widening_effects.c index 9a4f642eed..b71e0d73a6 100644 --- a/example_widening_effects.c +++ b/example_widening_effects.c @@ -19,6 +19,7 @@ void* thread(void* arg) { if(pqb.occupied < 2) { pqb.occupied++; } + pthread_mutex_unlock(&mtx); } From 1591132417c0fe3160283e29aa374a17571d7a3c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 14:18:40 +0200 Subject: [PATCH 062/160] Minimal widening effect example --- example_widening_effects.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/example_widening_effects.c b/example_widening_effects.c index b71e0d73a6..6074e60adc 100644 --- a/example_widening_effects.c +++ b/example_widening_effects.c @@ -1,25 +1,16 @@ #include -struct s { - int occupied; - int closed; -}; +int occupied; -struct s pqb; pthread_mutex_t mtx; -void pqueue_close() { - pthread_mutex_lock(&mtx); - pqb.closed = 1; - pthread_mutex_unlock(&mtx); -} void* thread(void* arg) { pthread_mutex_lock(&mtx); - if(pqb.occupied < 2) { - pqb.occupied++; + if(occupied < 2) { + occupied++; } - + pthread_mutex_unlock(&mtx); } @@ -27,7 +18,11 @@ int main() { pthread_t worker; pthread_create(&worker, 0, &thread, 0); - pqueue_close(); + + pthread_mutex_lock(&mtx); + occupied = 0; + pthread_mutex_unlock(&mtx); + return 0; } From 80c3ffedcc04b991e88f271dff406658389164ba Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 14:38:43 +0200 Subject: [PATCH 063/160] Add example where widening makes a difference --- .../13-privatized/92-widening-effect.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/regression/13-privatized/92-widening-effect.c diff --git a/tests/regression/13-privatized/92-widening-effect.c b/tests/regression/13-privatized/92-widening-effect.c new file mode 100644 index 0000000000..1b4a33fe3a --- /dev/null +++ b/tests/regression/13-privatized/92-widening-effect.c @@ -0,0 +1,29 @@ +// PARAM: --set ana.base.privatization write --enable ana.int.interval --set solvers.td3.side_widen never +// For write side_widen never is needed, for protection it is not. +#include +int occupied; +pthread_mutex_t mtx; + + +void* thread(void* arg) { + pthread_mutex_lock(&mtx); + if(occupied < 2) { + occupied++; + } + pthread_mutex_unlock(&mtx); + } + +int main() { + pthread_t worker; + + pthread_create(&worker, 0, &thread, 0); + + pthread_mutex_lock(&mtx); + occupied = 0; + pthread_mutex_unlock(&mtx); + + pthread_mutex_lock(&mtx); + __goblint_check(occupied >= 0); + pthread_mutex_lock(&mtx); + return 0; +} From 61d6289a522e36d038f01b7771cd56483ac223e4 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 15:03:25 +0200 Subject: [PATCH 064/160] Further simplify --- tests/regression/13-privatized/92-widening-effect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/13-privatized/92-widening-effect.c b/tests/regression/13-privatized/92-widening-effect.c index 1b4a33fe3a..0325a17c9a 100644 --- a/tests/regression/13-privatized/92-widening-effect.c +++ b/tests/regression/13-privatized/92-widening-effect.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.base.privatization write --enable ana.int.interval --set solvers.td3.side_widen never +// PARAM: --set ana.base.privatization write --enable ana.int.interval // For write side_widen never is needed, for protection it is not. #include int occupied; @@ -7,7 +7,7 @@ pthread_mutex_t mtx; void* thread(void* arg) { pthread_mutex_lock(&mtx); - if(occupied < 2) { + if(occupied < 1) { occupied++; } pthread_mutex_unlock(&mtx); From 64bd88fd3232afe765a3f33201f4004f978c986d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 19 May 2024 18:57:33 +0200 Subject: [PATCH 065/160] Explain intricacies --- tests/regression/13-privatized/92-widening-effect.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/regression/13-privatized/92-widening-effect.c b/tests/regression/13-privatized/92-widening-effect.c index 0325a17c9a..c5faa9fa21 100644 --- a/tests/regression/13-privatized/92-widening-effect.c +++ b/tests/regression/13-privatized/92-widening-effect.c @@ -1,5 +1,6 @@ -// PARAM: --set ana.base.privatization write --enable ana.int.interval +// PARAM: --set ana.base.privatization write --enable ana.int.interval --set solvers.td3.side_widen never // For write side_widen never is needed, for protection it is not. +// Alternatively, one can --disable ana.int.def_exc or --set ana.int.refinement once #include int occupied; pthread_mutex_t mtx; @@ -9,8 +10,10 @@ void* thread(void* arg) { pthread_mutex_lock(&mtx); if(occupied < 1) { occupied++; + pthread_mutex_unlock(&mtx); + } else { + pthread_mutex_unlock(&mtx); } - pthread_mutex_unlock(&mtx); } int main() { From 6108c7ba88031e9994030fefd07f4880f9a5a5f8 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 22 May 2024 13:58:42 +0200 Subject: [PATCH 066/160] Add problem --- tests/regression/46-apron2/85-fix.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/regression/46-apron2/85-fix.c diff --git a/tests/regression/46-apron2/85-fix.c b/tests/regression/46-apron2/85-fix.c new file mode 100644 index 0000000000..e0d7e8964b --- /dev/null +++ b/tests/regression/46-apron2/85-fix.c @@ -0,0 +1,13 @@ +// SKIP PARAM: --set ana.activated[+] apron --set sem.int.signed_overflow assume_none +#include + +int main() { + int d = 1; + while (d < 6) { + if (0) + return 0; //FIXPOINT: Earlier this fixpoint was not reached here + d++; + } + + return 0; +} From d65a835c2af0e57295543cfefbaacf88303c412e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 23 May 2024 11:22:52 +0200 Subject: [PATCH 067/160] Add example for unsoundness due to escape --- .../46-apron2/86-escape-cluster12.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/regression/46-apron2/86-escape-cluster12.c diff --git a/tests/regression/46-apron2/86-escape-cluster12.c b/tests/regression/46-apron2/86-escape-cluster12.c new file mode 100644 index 0000000000..283e45b728 --- /dev/null +++ b/tests/regression/46-apron2/86-escape-cluster12.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 +#include +#include +void *a; + +void* nothing(void* arg) { + // Go multithreaded! +} + +void main() { + pthread_t t; + pthread_create(&t, 0, nothing, 0); + + int d = 5; + a = &d; + + if (0) + ; + + __goblint_check(1); // Should be reachable! +} From 6d0a4f81fc353d4eb1fe473d98f04fccff73e759 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 23 May 2024 17:40:38 +0200 Subject: [PATCH 068/160] Fix gloabl_init for escaped globals in cluster configuration --- src/analyses/apron/relationPriv.apron.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 66548c117c..dc24247066 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -807,7 +807,9 @@ struct let g_var = V.global g in (* normal (strong) mapping: contains only still fully protected *) let g' = VS.singleton g in - let oct = LRD.find g' octs in + (* If there is no map entry yet which contains the global, default to top rather than bot *) + (* Happens e.g. in 46/86 because of escape *) + let oct = Option.default (RD.top ()) (LRD.find_opt g' octs) in LRD.singleton g' (RD.keep_vars oct [g_var]) let lock_get_m oct local_m get_m = From df01b46af85a597af3d5510f00650515a4527b02 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 May 2024 10:23:29 +0300 Subject: [PATCH 069/160] Add test 46-apron2/84-relation-extern (issue #1440) --- .../regression/46-apron2/84-relation-extern.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/regression/46-apron2/84-relation-extern.c diff --git a/tests/regression/46-apron2/84-relation-extern.c b/tests/regression/46-apron2/84-relation-extern.c new file mode 100644 index 0000000000..ffd8f9e2ce --- /dev/null +++ b/tests/regression/46-apron2/84-relation-extern.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron +#include +#include + +extern int g; + +void *t_fun(void *arg) { + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + if (g) // NOWARN + __goblint_check(1); // reachable + else + __goblint_check(1); // reachable + + return 0; +} From 8fcdb50522de7229246d54dd4584f0d11208bcf1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 6 May 2024 10:32:57 +0300 Subject: [PATCH 070/160] Fix relation analysis ignoring extern inits (issue #1440) --- src/analyses/apron/relationAnalysis.apron.ml | 34 +++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ed3cb29952..4242dfaf2a 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -245,25 +245,21 @@ struct (* Basic transfer functions. *) let assign ctx (lv:lval) e = let st = ctx.local in - if !AnalysisState.global_initialization && e = MyCFG.unknown_exp then - st (* ignore extern inits because there's no body before assign, so env is empty... *) - else ( - let simplified_e = replace_deref_exps ctx.ask e in - if M.tracing then M.traceli "relation" "assign %a = %a (simplified to %a)" d_lval lv d_exp e d_exp simplified_e; - let ask = Analyses.ask_of_ctx ctx in - let r = assign_to_global_wrapper ask ctx.global ctx.sideg st lv (fun st v -> - assign_from_globals_wrapper ask ctx.global st simplified_e (fun apr' e' -> - if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; - if M.tracing then M.trace "relation" "st: %a" RD.pretty apr'; - let r = RD.assign_exp ask apr' (RV.local v) e' (no_overflow ask simplified_e) in - if M.tracing then M.traceu "relation" "-> %a" RD.pretty r; - r - ) - ) - in - if M.tracing then M.traceu "relation" "-> %a" D.pretty r; - r - ) + let simplified_e = replace_deref_exps ctx.ask e in + if M.tracing then M.traceli "relation" "assign %a = %a (simplified to %a)" d_lval lv d_exp e d_exp simplified_e; + let ask = Analyses.ask_of_ctx ctx in + let r = assign_to_global_wrapper ask ctx.global ctx.sideg st lv (fun st v -> + assign_from_globals_wrapper ask ctx.global st simplified_e (fun apr' e' -> + if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; + if M.tracing then M.trace "relation" "st: %a" RD.pretty apr'; + let r = RD.assign_exp ask apr' (RV.local v) e' (no_overflow ask simplified_e) in + if M.tracing then M.traceu "relation" "-> %a" RD.pretty r; + r + ) + ) + in + if M.tracing then M.traceu "relation" "-> %a" D.pretty r; + r let branch ctx e b = let st = ctx.local in From 6065687f26413a676c0722db8a805bd6117effec Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 May 2024 13:10:51 +0200 Subject: [PATCH 071/160] Add test --- tests/regression/46-apron2/87-wtf.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/regression/46-apron2/87-wtf.c diff --git a/tests/regression/46-apron2/87-wtf.c b/tests/regression/46-apron2/87-wtf.c new file mode 100644 index 0000000000..97ef8d3097 --- /dev/null +++ b/tests/regression/46-apron2/87-wtf.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --sets ana.apron.domain interval +// Checks that branching over extern or volatile variables does not yield to both branches being dead. +#include +#include +extern int d; +volatile int v; + +void* g(void* arg) { + // Just go multithreaded! +} + +void main() { + pthread_t t; + pthread_create(&t, 0, g, 0); + + if(v) + ; + + __goblint_check(1); // Reachable + + if (d) + ; + + __goblint_check(1); // Reachable + +} From b9bdbedd303fe8643bbb547d0fb86f10b9038b9d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 24 May 2024 14:14:35 +0200 Subject: [PATCH 072/160] Disable `exp.hide-std-globals` --- src/config/options.schema.json | 2 +- tests/regression/46-apron2/88-wtf2.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/regression/46-apron2/88-wtf2.c diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 5f54f92713..f83ea88e75 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1806,7 +1806,7 @@ "title": "exp.hide-std-globals", "description": "Hide standard extern globals (e.g. `stdout`) from cluttering local states.", "type": "boolean", - "default": true + "default": false }, "arg": { "title": "exp.arg", diff --git a/tests/regression/46-apron2/88-wtf2.c b/tests/regression/46-apron2/88-wtf2.c new file mode 100644 index 0000000000..d3cc38ac57 --- /dev/null +++ b/tests/regression/46-apron2/88-wtf2.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --sets ana.apron.domain interval +// Checks that branching over extern or volatile variables does not yield to both branches being dead. +// This fails unless exp.hide-std-globals is deactivated (which it is by default on this branch) +#include +extern int optind; + +void* a(void* arg) { + // Just go multi-threaded +} + +void main() { + pthread_t t; + + pthread_create(&t, 0, a, 0); + if (optind) + ; + + // __goblint_check(1); // Reachable +} From 54658781a67da0116703beaf70ab3b45fe020ab0 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 May 2024 17:45:58 +0200 Subject: [PATCH 073/160] Fix `mutex-meet` for malloc after thread creation --- src/analyses/apron/relationPriv.apron.ml | 9 +++++++-- tests/regression/46-apron2/89-malloc.c | 21 +++++++++++++++++++++ tests/regression/46-apron2/90-malloc2.c | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/regression/46-apron2/89-malloc.c create mode 100644 tests/regression/46-apron2/90-malloc2.c diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index dc24247066..3f5a462693 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -477,7 +477,7 @@ struct let get_mutex_inits' = keep_only_protected_globals ask m get_mutex_inits in RD.join get_m get_mutex_inits' - let get_mutex_global_g_with_mutex_inits ask getg g = + let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g = let g_var = AV.global g in let get_mutex_global_g = if Param.handle_atomic then ( @@ -485,7 +485,12 @@ struct RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] ) else - getg (V.global g) + let r = getg (V.global g) in + if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then + (* malloc'ed blobs may not have a value here yet *) + RD.top () + else + r in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in diff --git a/tests/regression/46-apron2/89-malloc.c b/tests/regression/46-apron2/89-malloc.c new file mode 100644 index 0000000000..8780568748 --- /dev/null +++ b/tests/regression/46-apron2/89-malloc.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} diff --git a/tests/regression/46-apron2/90-malloc2.c b/tests/regression/46-apron2/90-malloc2.c new file mode 100644 index 0000000000..36696956e7 --- /dev/null +++ b/tests/regression/46-apron2/90-malloc2.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} From e1f7a1b3243b6cae8af947e9459ca3c34738375c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 May 2024 17:55:00 +0200 Subject: [PATCH 074/160] Also fix atomic --- src/analyses/apron/relationPriv.apron.ml | 21 ++++++++++--------- tests/regression/46-apron2/91-malloc-atomic.c | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 tests/regression/46-apron2/91-malloc-atomic.c diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 3f5a462693..47ae75d107 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -480,17 +480,18 @@ struct let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g = let g_var = AV.global g in let get_mutex_global_g = - if Param.handle_atomic then ( - (* Unprotected invariant is one big relation. *) - RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] - ) - else - let r = getg (V.global g) in - if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then - (* malloc'ed blobs may not have a value here yet *) - RD.top () + let r = + if Param.handle_atomic then + (* Unprotected invariant is one big relation. *) + RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] else - r + getg (V.global g) + in + if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then + (* malloc'ed blobs may not have a value here yet *) + RD.top () + else + r in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in diff --git a/tests/regression/46-apron2/91-malloc-atomic.c b/tests/regression/46-apron2/91-malloc-atomic.c new file mode 100644 index 0000000000..b2f057f6a4 --- /dev/null +++ b/tests/regression/46-apron2/91-malloc-atomic.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} From db093cb309eb1dd3e61353685da84a5fd8fde56f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:23:32 +0300 Subject: [PATCH 075/160] Switch to Mval-based must locksets --- src/analyses/mutexAnalysis.ml | 64 +++++++++++--------- src/cdomains/lockDomain.ml | 50 +++++++++++++++ tests/regression/06-symbeq/21-mult_accs_rc.t | 2 - 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 138f65ab47..0a02fd60f9 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -3,6 +3,7 @@ module M = Messages module Addr = ValueDomain.Addr module Lockset = LockDomain.Lockset +module MLockset = LockDomain.MLockset module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open GoblintCil @@ -47,8 +48,8 @@ struct (add v (current - 1) x, current - 1 = 0) end - module D = struct include Lattice.Prod(Lockset)(Multiplicity) - let empty () = (Lockset.empty (), Multiplicity.empty ()) + module D = struct include Lattice.Prod(MLockset)(Multiplicity) + let empty () = (MLockset.empty (), Multiplicity.empty ()) end @@ -146,36 +147,43 @@ struct let create_protected protected = `Lifted2 protected end - let add ctx (l:Mutexes.elt*bool) = + let add ctx (l:Mutexes.elt*bool): D.t = let s,m = ctx.local in - let s' = Lockset.add l s in match Addr.to_mval (fst l) with | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> + let s' = MLockset.add (mval, snd l) s in (s', Multiplicity.increment (fst l) m) - | _ -> (s', m) + | Some mval -> + let s' = MLockset.add (mval, snd l) s in + (s', m) + | _ -> (s, m) - let remove' ctx ~warn l = - let s, m = ctx.local in - let rm s = Lockset.remove (l, true) (Lockset.remove (l, false) s) in - if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" Addr.pretty l; + let remove' ctx ~warn l: D.t = match Addr.to_mval l with - | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> - let m',rmed = Multiplicity.decrement l m in - if rmed then - (rm s, m') + | None -> ctx.local + | Some l -> + let s, m = ctx.local in + let rm s = MLockset.remove (l, true) (MLockset.remove (l, false) s) in + if warn && (not (MLockset.mem (l,true) s || MLockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty l; + if MutexTypeAnalysis.must_be_recursive ctx l then ( + let m',rmed = Multiplicity.decrement (Addr l) m in + if rmed then + (rm s, m') + else + (s, m') + ) else - (s, m') - | _ -> (rm s, m) + (rm s, m) let remove = remove' ~warn:true - let remove_all ctx = + let remove_all ctx: D.t = (* Mutexes.iter (fun m -> ctx.emit (MustUnlock m) ) (D.export_locks ctx.local); *) (* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *) M.warn "unlocking unknown mutex which may not be held"; - (Lockset.empty (), Multiplicity.empty ()) + (MLockset.empty (), Multiplicity.empty ()) let empty () = (Lockset.empty (), Multiplicity.empty ()) end @@ -201,24 +209,24 @@ struct num_mutexes := 0; sum_protected := 0 - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query (ctx: (D.t, _, _, _) ctx) (type a) (q: a Queries.t): a Queries.result = let ls, m = ctx.local in (* get the set of mutexes protecting the variable v in the given mode *) let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in let non_overlapping locks1 locks2 = Mutexes.is_empty @@ Mutexes.inter locks1 locks2 in match q with - | Queries.MayBePublic _ when Lockset.is_bot ls -> false + | Queries.MayBePublic _ when MLockset.is_bot ls -> false | Queries.MayBePublic {global=v; write; protection} -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MLockset.export_locks (MLockset.filter snd ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then false else *) non_overlapping held_locks protecting - | Queries.MayBePublicWithout _ when Lockset.is_bot ls -> false + | Queries.MayBePublicWithout _ when MLockset.is_bot ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = Lockset.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = MLockset.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then @@ -234,10 +242,10 @@ struct else *) Mutexes.leq mutex_lockset protecting | Queries.MustLockset -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MLockset.export_locks (MLockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MLockset.export_locks (MLockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in @@ -269,7 +277,7 @@ struct module A = struct - include Lockset + include MLockset let name () = "lock" let may_race ls1 ls2 = (* not mutually exclusive *) @@ -287,7 +295,7 @@ struct let access ctx (a: Queries.access) = fst ctx.local - let event ctx e octx = + let event (ctx: (D.t, _, _, _) ctx) e (octx: (D.t, _, _, _) ctx) = match e with | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in @@ -297,8 +305,8 @@ struct (*privatization*) match var_opt with | Some v -> - if not (Lockset.is_bot (fst octx.local)) then - let locks = Lockset.export_locks (Lockset.filter snd (fst octx.local)) in + if not (MLockset.is_bot (fst octx.local)) then + let locks = MLockset.export_locks (MLockset.filter snd (fst octx.local)) in let write = match kind with | Write | Free -> true | Read -> false diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index baee8a2ce6..056b62dc31 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -1,6 +1,7 @@ (** Lockset domains. *) module Addr = ValueDomain.Addr +module Mval = ValueDomain.Mval module Offs = ValueDomain.Offs module Exp = CilType.Exp module IdxDom = ValueDomain.IndexDomain @@ -60,6 +61,55 @@ struct fold f ls (Mutexes.empty ()) end +module MLockset = +struct + + (* true means exclusive lock and false represents reader lock*) + module RW = IntDomain.Booleans + + (* pair Addr and RW; also change pretty printing*) + module Lock = + struct + include Printable.Prod (Mval) (RW) + + let pretty () (a, write) = + if write then + Mval.pretty () a + else + Pretty.dprintf "read lock %a" Mval.pretty a + + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) + end + + include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) + let name () = "lockset" + + let add ((addr, _) as lock) set = + match addr with + | mv when Addr.Mval.is_definite mv -> (* avoids NULL *) + add lock set + | _ -> + set + + let remove ((addr, _) as lock) set = + match addr with + | mv when Addr.Mval.is_definite mv -> (* avoids NULL *) + remove lock set + | _ -> + filter (fun (addr', _) -> + Mval.semantic_equal addr addr' = Some false + ) set + + let export_locks ls = + let f (x,_) set = Mutexes.add (Addr.Addr x) set in + fold f ls (Mutexes.empty ()) +end + module MayLockset = struct include Lockset diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 90f0661f67..711205e38c 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -10,7 +10,6 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Race] Memory location (struct s).data (race with conf. 100): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) - [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) @@ -26,7 +25,6 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) - [Warning][Unknown] unlocking mutex (NULL) which may not be held (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) From f0be59f633d411e9597b700089e3e51fc4708988 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:25:01 +0300 Subject: [PATCH 076/160] Remove unused LockDomain.MayLocks --- src/cdomains/lockDomain.ml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 056b62dc31..4954ae9598 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -110,16 +110,6 @@ struct fold f ls (Mutexes.empty ()) end -module MayLockset = -struct - include Lockset - let leq x y = leq y x - let join = Lockset.meet - let meet = Lockset.join - let top = Lockset.bot - let bot = Lockset.top -end - module MayLocksetNoRW = struct include PreValueDomain.AD From dc21ca058947b877373cfd7200e5c4f7887cbe3d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:29:22 +0300 Subject: [PATCH 077/160] Remove unused set parts of LockDomain.Lockset --- src/analyses/mutexAnalysis.ml | 6 ++---- src/cdomains/lockDomain.ml | 23 ----------------------- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 0a02fd60f9..23ac4c8715 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -184,8 +184,6 @@ struct (* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *) M.warn "unlocking unknown mutex which may not be held"; (MLockset.empty (), Multiplicity.empty ()) - - let empty () = (Lockset.empty (), Multiplicity.empty ()) end include LocksetAnalysis.MakeMust (Arg) let name () = "mutex" @@ -233,8 +231,8 @@ struct false else *) non_overlapping held_locks protecting - | Queries.MustBeProtectedBy {mutex; global=v; write; protection} -> - let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (mutex, true) in + | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> + let mutex_lockset = MLockset.export_locks @@ MLockset.singleton (mutex, true) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 4954ae9598..0240665b91 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -36,29 +36,6 @@ struct end ) end - - include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) - let name () = "lockset" - - let add ((addr, _) as lock) set = - match addr with - | Addr.Addr mv when Addr.Mval.is_definite mv -> (* avoids NULL *) - add lock set - | _ -> - set - - let remove ((addr, _) as lock) set = - match addr with - | Addr.Addr mv when Addr.Mval.is_definite mv -> (* avoids NULL *) - remove lock set - | _ -> - filter (fun (addr', _) -> - Addr.semantic_equal addr addr' = Some false - ) set - - let export_locks ls = - let f (x,_) set = Mutexes.add x set in - fold f ls (Mutexes.empty ()) end module MLockset = From 8888069d45716bc7b81006ea20109fac9f49080c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:31:18 +0300 Subject: [PATCH 078/160] Remove LockDomain.Lockset --- src/analyses/deadlock.ml | 2 +- src/analyses/locksetAnalysis.ml | 2 +- src/analyses/mutexAnalysis.ml | 1 - src/analyses/mutexEventsAnalysis.ml | 1 - src/cdomains/lockDomain.ml | 42 +++++++++++++---------------- src/domains/events.ml | 4 +-- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/analyses/deadlock.ml b/src/analyses/deadlock.ml index c23d6f4294..23b9faa882 100644 --- a/src/analyses/deadlock.ml +++ b/src/analyses/deadlock.ml @@ -30,7 +30,7 @@ struct let part_access ctx: MCPAccess.A.t = Obj.obj (ctx.ask (PartAccess Point)) - let add ctx ((l, _): LockDomain.Lockset.Lock.t) = + let add ctx ((l, _): LockDomain.Lock.t) = let after: LockEvent.t = (l, ctx.prev_node, part_access ctx) in (* use octx for access to use locksets before event *) D.iter (fun before -> side_lock_event_pair ctx before after diff --git a/src/analyses/locksetAnalysis.ml b/src/analyses/locksetAnalysis.ml index 6a816b9e6c..c7c7395dc1 100644 --- a/src/analyses/locksetAnalysis.ml +++ b/src/analyses/locksetAnalysis.ml @@ -29,7 +29,7 @@ sig module G: Lattice.S module V: SpecSysVar - val add: (D.t, G.t, D.t, V.t) ctx -> LockDomain.Lockset.Lock.t -> D.t + val add: (D.t, G.t, D.t, V.t) ctx -> LockDomain.Lock.t -> D.t val remove: (D.t, G.t, D.t, V.t) ctx -> ValueDomain.Addr.t -> D.t end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 23ac4c8715..005316aff8 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -2,7 +2,6 @@ module M = Messages module Addr = ValueDomain.Addr -module Lockset = LockDomain.Lockset module MLockset = LockDomain.MLockset module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 7f544b0ffd..2252dae8a7 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -4,7 +4,6 @@ module M = Messages module Addr = ValueDomain.Addr -module Lockset = LockDomain.Lockset module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open Batteries diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 0240665b91..b1200959cb 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -12,30 +12,26 @@ module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" module Simple = Lattice.Reverse (Mutexes) module Priorities = IntDomain.Lifted -module Lockset = -struct - - (* true means exclusive lock and false represents reader lock*) - module RW = IntDomain.Booleans - - (* pair Addr and RW; also change pretty printing*) - module Lock = - struct - include Printable.Prod (Addr) (RW) - - let pretty () (a, write) = - if write then - Addr.pretty () a - else - Pretty.dprintf "read lock %a" Addr.pretty a +(* true means exclusive lock and false represents reader lock*) +module RW = IntDomain.Booleans - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end +(* pair Addr and RW; also change pretty printing*) +module Lock = +struct + include Printable.Prod (Addr) (RW) + + let pretty () (a, write) = + if write then + Addr.pretty () a + else + Pretty.dprintf "read lock %a" Addr.pretty a + + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) end module MLockset = diff --git a/src/domains/events.ml b/src/domains/events.ml index 06561bddbe..90eb7611be 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -4,7 +4,7 @@ open GoblintCil open Pretty type t = - | Lock of LockDomain.Lockset.Lock.t + | Lock of LockDomain.Lock.t | Unlock of LockDomain.Addr.t | Escape of EscapeDomain.EscapedVars.t | EnterMultiThreaded @@ -35,7 +35,7 @@ let emit_on_deadcode = function false let pretty () = function - | Lock m -> dprintf "Lock %a" LockDomain.Lockset.Lock.pretty m + | Lock m -> dprintf "Lock %a" LockDomain.Lock.pretty m | Unlock m -> dprintf "Unlock %a" LockDomain.Addr.pretty m | Escape escaped -> dprintf "Escape %a" EscapeDomain.EscapedVars.pretty escaped | EnterMultiThreaded -> text "EnterMultiThreaded" From c01bb0dcaa042771d71efc0472dbe49cb130e3a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:32:35 +0300 Subject: [PATCH 079/160] Rename LockDomain.MLockset -> Lockset --- src/analyses/mutexAnalysis.ml | 36 +++++++++++++++++------------------ src/cdomains/lockDomain.ml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 005316aff8..2375ff9535 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -2,7 +2,7 @@ module M = Messages module Addr = ValueDomain.Addr -module MLockset = LockDomain.MLockset +module Lockset = LockDomain.Lockset module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open GoblintCil @@ -47,8 +47,8 @@ struct (add v (current - 1) x, current - 1 = 0) end - module D = struct include Lattice.Prod(MLockset)(Multiplicity) - let empty () = (MLockset.empty (), Multiplicity.empty ()) + module D = struct include Lattice.Prod(Lockset)(Multiplicity) + let empty () = (Lockset.empty (), Multiplicity.empty ()) end @@ -150,10 +150,10 @@ struct let s,m = ctx.local in match Addr.to_mval (fst l) with | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> - let s' = MLockset.add (mval, snd l) s in + let s' = Lockset.add (mval, snd l) s in (s', Multiplicity.increment (fst l) m) | Some mval -> - let s' = MLockset.add (mval, snd l) s in + let s' = Lockset.add (mval, snd l) s in (s', m) | _ -> (s, m) @@ -162,8 +162,8 @@ struct | None -> ctx.local | Some l -> let s, m = ctx.local in - let rm s = MLockset.remove (l, true) (MLockset.remove (l, false) s) in - if warn && (not (MLockset.mem (l,true) s || MLockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty l; + let rm s = Lockset.remove (l, true) (Lockset.remove (l, false) s) in + if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty l; if MutexTypeAnalysis.must_be_recursive ctx l then ( let m',rmed = Multiplicity.decrement (Addr l) m in if rmed then @@ -182,7 +182,7 @@ struct ) (D.export_locks ctx.local); *) (* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *) M.warn "unlocking unknown mutex which may not be held"; - (MLockset.empty (), Multiplicity.empty ()) + (Lockset.empty (), Multiplicity.empty ()) end include LocksetAnalysis.MakeMust (Arg) let name () = "mutex" @@ -212,18 +212,18 @@ struct let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in let non_overlapping locks1 locks2 = Mutexes.is_empty @@ Mutexes.inter locks1 locks2 in match q with - | Queries.MayBePublic _ when MLockset.is_bot ls -> false + | Queries.MayBePublic _ when Lockset.is_bot ls -> false | Queries.MayBePublic {global=v; write; protection} -> - let held_locks = MLockset.export_locks (MLockset.filter snd ls) in + let held_locks = Lockset.export_locks (Lockset.filter snd ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then false else *) non_overlapping held_locks protecting - | Queries.MayBePublicWithout _ when MLockset.is_bot ls -> false + | Queries.MayBePublicWithout _ when Lockset.is_bot ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = MLockset.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = Lockset.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then @@ -231,7 +231,7 @@ struct else *) non_overlapping held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> - let mutex_lockset = MLockset.export_locks @@ MLockset.singleton (mutex, true) in + let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (mutex, true) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -239,10 +239,10 @@ struct else *) Mutexes.leq mutex_lockset protecting | Queries.MustLockset -> - let held_locks = MLockset.export_locks (MLockset.filter snd ls) in + let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> - let held_locks = MLockset.export_locks (MLockset.filter snd ls) in + let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in @@ -274,7 +274,7 @@ struct module A = struct - include MLockset + include Lockset let name () = "lock" let may_race ls1 ls2 = (* not mutually exclusive *) @@ -302,8 +302,8 @@ struct (*privatization*) match var_opt with | Some v -> - if not (MLockset.is_bot (fst octx.local)) then - let locks = MLockset.export_locks (MLockset.filter snd (fst octx.local)) in + if not (Lockset.is_bot (fst octx.local)) then + let locks = Lockset.export_locks (Lockset.filter snd (fst octx.local)) in let write = match kind with | Write | Free -> true | Read -> false diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index b1200959cb..d26a66c7b6 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -34,7 +34,7 @@ struct ) end -module MLockset = +module Lockset = struct (* true means exclusive lock and false represents reader lock*) From 04ee6ee74407a4ced665e5856ebe38a7e36a1235 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:35:18 +0300 Subject: [PATCH 080/160] Extract LockDomain.MakeLockRW --- src/cdomains/lockDomain.ml | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index d26a66c7b6..89e317f6b2 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -16,15 +16,15 @@ module Priorities = IntDomain.Lifted module RW = IntDomain.Booleans (* pair Addr and RW; also change pretty printing*) -module Lock = +module MakeLockRW (P: Printable.S) = struct - include Printable.Prod (Addr) (RW) + include Printable.Prod (P) (RW) let pretty () (a, write) = if write then - Addr.pretty () a + P.pretty () a else - Pretty.dprintf "read lock %a" Addr.pretty a + Pretty.dprintf "read lock %a" P.pretty a include Printable.SimplePretty ( struct @@ -34,30 +34,11 @@ struct ) end +module Lock = MakeLockRW (Addr) + module Lockset = struct - - (* true means exclusive lock and false represents reader lock*) - module RW = IntDomain.Booleans - - (* pair Addr and RW; also change pretty printing*) - module Lock = - struct - include Printable.Prod (Mval) (RW) - - let pretty () (a, write) = - if write then - Mval.pretty () a - else - Pretty.dprintf "read lock %a" Mval.pretty a - - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end + module Lock = MakeLockRW (Mval) include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) let name () = "lockset" From afcd253a6f5391ac9dd3773be9f43004f4853a36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:53:21 +0300 Subject: [PATCH 081/160] Clean up mutex analysis add and remove --- src/analyses/mutexAnalysis.ml | 45 +++++++++++++++++++---------------- src/cdomains/lockDomain.ml | 18 +++++++------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 2375ff9535..4ea8048e9b 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -2,6 +2,7 @@ module M = Messages module Addr = ValueDomain.Addr +module Lock = LockDomain.Lock module Lockset = LockDomain.Lockset module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions @@ -146,26 +147,30 @@ struct let create_protected protected = `Lifted2 protected end - let add ctx (l:Mutexes.elt*bool): D.t = - let s,m = ctx.local in - match Addr.to_mval (fst l) with - | Some mval when MutexTypeAnalysis.must_be_recursive ctx mval -> - let s' = Lockset.add (mval, snd l) s in - (s', Multiplicity.increment (fst l) m) - | Some mval -> - let s' = Lockset.add (mval, snd l) s in - (s', m) - | _ -> (s, m) - - let remove' ctx ~warn l: D.t = - match Addr.to_mval l with + let add ctx ((addr, rw): Lock.t): D.t = + match Addr.to_mval addr with + | Some mv -> + let (s, m) = ctx.local in + let s' = Lockset.add (mv, rw) s in + let m' = + if MutexTypeAnalysis.must_be_recursive ctx mv then + Multiplicity.increment addr m + else + m + in + (s', m') + | None -> ctx.local + + let remove' ctx ~warn (addr: Addr.t): D.t = + match Addr.to_mval addr with | None -> ctx.local - | Some l -> - let s, m = ctx.local in - let rm s = Lockset.remove (l, true) (Lockset.remove (l, false) s) in - if warn && (not (Lockset.mem (l,true) s || Lockset.mem (l,false) s)) then M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty l; - if MutexTypeAnalysis.must_be_recursive ctx l then ( - let m',rmed = Multiplicity.decrement (Addr l) m in + | Some mv -> + let (s, m) = ctx.local in + if warn && (not (Lockset.mem (mv, true) s || Lockset.mem (mv, false) s)) then + M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty mv; + let rm s = Lockset.remove (mv, true) (Lockset.remove (mv, false) s) in + if MutexTypeAnalysis.must_be_recursive ctx mv then ( + let (m', rmed) = Multiplicity.decrement (Addr mv) m in if rmed then (rm s, m') else @@ -182,7 +187,7 @@ struct ) (D.export_locks ctx.local); *) (* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *) M.warn "unlocking unknown mutex which may not be held"; - (Lockset.empty (), Multiplicity.empty ()) + D.empty () end include LocksetAnalysis.MakeMust (Arg) let name () = "mutex" diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 89e317f6b2..002c5fc2b4 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -43,20 +43,18 @@ struct include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) let name () = "lockset" - let add ((addr, _) as lock) set = - match addr with - | mv when Addr.Mval.is_definite mv -> (* avoids NULL *) + let add ((mv, _) as lock) set = + if Addr.Mval.is_definite mv then add lock set - | _ -> + else set - let remove ((addr, _) as lock) set = - match addr with - | mv when Addr.Mval.is_definite mv -> (* avoids NULL *) + let remove ((mv, _) as lock) set = + if Addr.Mval.is_definite mv then remove lock set - | _ -> - filter (fun (addr', _) -> - Mval.semantic_equal addr addr' = Some false + else + filter (fun (mv', _) -> + Mval.semantic_equal mv mv' = Some false ) set let export_locks ls = From 7b914c5d589293ced9ca5b961e2d0bcf74c85d3b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 16:55:13 +0300 Subject: [PATCH 082/160] Use Mval for mutex analysis multiplicity --- src/analyses/mutexAnalysis.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 4ea8048e9b..01b02fdee5 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -29,7 +29,7 @@ struct ) ) - include MapDomain.MapTop_LiftBot (ValueDomain.Addr) (Count) + include MapDomain.MapTop_LiftBot (LockDomain.Mval) (Count) let name () = "multiplicity" @@ -154,7 +154,7 @@ struct let s' = Lockset.add (mv, rw) s in let m' = if MutexTypeAnalysis.must_be_recursive ctx mv then - Multiplicity.increment addr m + Multiplicity.increment mv m else m in @@ -170,7 +170,7 @@ struct M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty mv; let rm s = Lockset.remove (mv, true) (Lockset.remove (mv, false) s) in if MutexTypeAnalysis.must_be_recursive ctx mv then ( - let (m', rmed) = Multiplicity.decrement (Addr mv) m in + let (m', rmed) = Multiplicity.decrement mv m in if rmed then (rm s, m') else From 6307cc170d2b5d97f4a8d1bbfbd558d2556501eb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 17:12:26 +0300 Subject: [PATCH 083/160] Use Mval for mutex analysis protecting --- src/analyses/mutexAnalysis.ml | 33 +++++++++++++++++---------------- src/cdomains/lockDomain.ml | 6 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 01b02fdee5..6d69e6887c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -4,6 +4,7 @@ module M = Messages module Addr = ValueDomain.Addr module Lock = LockDomain.Lock module Lockset = LockDomain.Lockset +module Simple = LockDomain.Simple module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open GoblintCil @@ -99,17 +100,17 @@ struct (** Collects information about which variables are protected by which mutexes *) module GProtecting: sig include Lattice.S - val make: write:bool -> recovered:bool -> Mutexes.t -> t - val get: write:bool -> Queries.Protection.t -> t -> Mutexes.t + val make: write:bool -> recovered:bool -> Simple.t -> t + val get: write:bool -> Queries.Protection.t -> t -> Simple.t end = struct - include MakeP (LockDomain.Simple) + include MakeP (Simple) let make ~write ~recovered locks = (* If the access is not a write, set to T so intersection with current write-protecting is identity *) - let wlocks = if write then locks else Mutexes.top () in + let wlocks = if write then locks else Simple.bot () in if recovered then (* If we are in single-threaded mode again, this does not need to be added to set of mutexes protecting in mt-mode only *) - ((locks, wlocks), (Mutexes.top (), Mutexes.top ())) + ((locks, wlocks), (Simple.bot (), Simple.bot ())) else ((locks, wlocks), (locks, wlocks)) end @@ -200,7 +201,7 @@ struct module GProtected = Arg.GProtected module G = Arg.G - module GM = Hashtbl.Make (ValueDomain.Addr) + module GM = Hashtbl.Make (LockDomain.Mval) let max_protected = ref 0 let num_mutexes = ref 0 @@ -215,7 +216,7 @@ struct let ls, m = ctx.local in (* get the set of mutexes protecting the variable v in the given mode *) let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in - let non_overlapping locks1 locks2 = Mutexes.is_empty @@ Mutexes.inter locks1 locks2 in + let non_overlapping locks1 locks2 = Simple.is_empty @@ Simple.inter locks1 locks2 in match q with | Queries.MayBePublic _ when Lockset.is_bot ls -> false | Queries.MayBePublic {global=v; write; protection} -> @@ -242,13 +243,13 @@ struct (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then true else *) - Mutexes.leq mutex_lockset protecting + Simple.subset mutex_lockset protecting | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) + Simple.fold (fun addr ls -> Queries.AD.add (Addr addr) ls) held_locks (Queries.AD.empty ()) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks + Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = m; write} -> let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> @@ -262,8 +263,8 @@ struct | `Left g' -> (* protecting *) if GobConfig.get_bool "dbg.print_protection" then ( let protecting = GProtecting.get ~write:false Strong (G.protecting (ctx.global g)) in (* readwrite protecting *) - let s = Mutexes.cardinal protecting in - M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s Mutexes.pretty protecting + let s = Simple.cardinal protecting in + M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s Simple.pretty protecting ) | `Right m -> (* protected *) if GobConfig.get_bool "dbg.print_protection" then ( @@ -297,7 +298,7 @@ struct let access ctx (a: Queries.access) = fst ctx.local - let event (ctx: (D.t, _, _, _) ctx) e (octx: (D.t, _, _, _) ctx) = + let event (ctx: (D.t, _, _, V.t) ctx) e (octx: (D.t, _, _, _) ctx) = match e with | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in @@ -324,10 +325,10 @@ struct let held_weak = protecting Weak in let vs = VarSet.singleton v in let protected = G.create_protected @@ GProtected.make ~write vs in - Mutexes.iter (fun addr -> ctx.sideg (V.protected addr) protected) held_strong; + Simple.iter (fun addr -> ctx.sideg (V.protected (Addr.Addr addr)) protected) held_strong; (* TODO: remove Addr *) (* If the mutex set here is top, it is actually not accessed *) - if is_recovered_to_st && not @@ Mutexes.is_top held_weak then - Mutexes.iter (fun addr -> ctx.sideg (V.protected addr) protected) held_weak; + if is_recovered_to_st && not @@ Simple.is_bot held_weak then + Simple.iter (fun addr -> ctx.sideg (V.protected (Addr.Addr addr)) protected) held_weak; (* TODO: remove Addr *) ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 002c5fc2b4..a7b7f9bec7 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -9,7 +9,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) -module Simple = Lattice.Reverse (Mutexes) +module Simple = SetDomain.Reverse (SetDomain.ToppedSet (Mval) (struct let topname = "All mutexes" end)) module Priorities = IntDomain.Lifted (* true means exclusive lock and false represents reader lock*) @@ -58,8 +58,8 @@ struct ) set let export_locks ls = - let f (x,_) set = Mutexes.add (Addr.Addr x) set in - fold f ls (Mutexes.empty ()) + let f (x,_) set = Simple.add x set in + fold f ls (Simple.empty ()) end module MayLocksetNoRW = From 4d27ca31dfb9606396d80851691db755c729dcdc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 18:03:33 +0300 Subject: [PATCH 084/160] Use Mval for mutex analysis protected --- src/analyses/mutexAnalysis.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 6d69e6887c..204b064c1c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -62,7 +62,7 @@ struct module V = struct - include Printable.Either (struct include CilType.Varinfo let name () = "protecting" end) (struct include ValueDomain.Addr let name () = "protected" end) + include Printable.Either (struct include CilType.Varinfo let name () = "protecting" end) (struct include LockDomain.Mval let name () = "protected" end) let name () = "mutex" let protecting x = `Left x let protected x = `Right x @@ -236,7 +236,7 @@ struct false else *) non_overlapping held_locks protecting - | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> + | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (mutex, true) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) @@ -250,7 +250,7 @@ struct | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) - | Queries.MustProtectedVars {mutex = m; write} -> + | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in VarSet.fold (fun v acc -> Queries.VS.add v acc @@ -273,7 +273,7 @@ struct max_protected := max !max_protected s; sum_protected := !sum_protected + s; incr num_mutexes; - M.info_noloc ~category:Race "Mutex %a read-write protects %d variable(s): %a" ValueDomain.Addr.pretty m s VarSet.pretty protected + M.info_noloc ~category:Race "Mutex %a read-write protects %d variable(s): %a" LockDomain.Mval.pretty m s VarSet.pretty protected ) end | _ -> Queries.Result.top q @@ -325,10 +325,10 @@ struct let held_weak = protecting Weak in let vs = VarSet.singleton v in let protected = G.create_protected @@ GProtected.make ~write vs in - Simple.iter (fun addr -> ctx.sideg (V.protected (Addr.Addr addr)) protected) held_strong; (* TODO: remove Addr *) + Simple.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_strong; (* If the mutex set here is top, it is actually not accessed *) if is_recovered_to_st && not @@ Simple.is_bot held_weak then - Simple.iter (fun addr -> ctx.sideg (V.protected (Addr.Addr addr)) protected) held_weak; (* TODO: remove Addr *) + Simple.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_weak; ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in From 1dedf97ee6d9bc84ae0acf9ec97a0d1d3d7b7ece Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 18:53:37 +0300 Subject: [PATCH 085/160] Add Mval.Z with definite indices --- src/cdomain/value/cdomains/mval.ml | 1 + src/cdomain/value/cdomains/mval_intf.ml | 3 +++ src/cdomain/value/cdomains/offset.ml | 30 +++++++++++++++++++++++ src/cdomain/value/cdomains/offset_intf.ml | 7 ++++++ 4 files changed, 41 insertions(+) diff --git a/src/cdomain/value/cdomains/mval.ml b/src/cdomain/value/cdomains/mval.ml index 2bc5658460..6dd64b68da 100644 --- a/src/cdomain/value/cdomains/mval.ml +++ b/src/cdomain/value/cdomains/mval.ml @@ -72,3 +72,4 @@ end module Unit = MakePrintable (Offset.Unit) module Exp = MakePrintable (Offset.Exp) +module Z = MakePrintable (Offset.Z) diff --git a/src/cdomain/value/cdomains/mval_intf.ml b/src/cdomain/value/cdomains/mval_intf.ml index 7b956bf8b8..18aa81c16b 100644 --- a/src/cdomain/value/cdomains/mval_intf.ml +++ b/src/cdomain/value/cdomains/mval_intf.ml @@ -57,4 +57,7 @@ sig (** Mvalue with {!Offset.Exp} indices in offset. *) module Exp: Printable with type idx = GoblintCil.exp + + (** Mvalue with {!Offset.Z} indices in offset. *) + module Z: Printable with type idx = Z.t end diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 7166e0642e..1329f461eb 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -43,6 +43,29 @@ struct let to_int _ = None (* TODO: more precise for definite indices *) let top () = Lazy.force any end + + module Z = + struct + include Printable.StdLeaf (* TODO: move to GobZ *) + include GobZ + let name () = "Z index" + + (* TODO: move to GobZ *) + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) + + let top () = failwith "Offset.Index.Z.top" (* TODO: remove from interface? *) + let to_int z = Some z + let equal_to z1 z2 = + if Z.equal z2 z2 then + `Eq + else + `Neq + end end @@ -258,6 +281,13 @@ struct | `Field (f,o) -> Field (f, to_cil o) end +module Z = +struct + include MakePrintable (Index.Z) + + let is_definite _ = true (* override to avoid iterating over offset *) +end + let () = Printexc.register_printer (function | Type_of_error (t, o) -> Some (GobPretty.sprintf "Offset.Type_of_error(%a, %s)" d_plaintype t o) diff --git a/src/cdomain/value/cdomains/offset_intf.ml b/src/cdomain/value/cdomains/offset_intf.ml index 82117b7d9f..a0d9ce144c 100644 --- a/src/cdomain/value/cdomains/offset_intf.ml +++ b/src/cdomain/value/cdomains/offset_intf.ml @@ -136,6 +136,10 @@ sig Used for Goblint-specific witness invariants. *) val all: GoblintCil.exp Lazy.t end + + module Z: Printable with type t = Z.t + (** {!Z} index. + Represents a definite index. *) end exception Type_of_error of GoblintCil.typ * string @@ -172,4 +176,7 @@ sig val to_cil : t -> GoblintCil.offset (** Convert to CIL offset. *) end + + (** Offset with {!Index.Z} indices. *) + module Z: Printable with type idx = Z.t end From acc182ead631e21cb687e981fbfef1dd50907abd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 19:07:19 +0300 Subject: [PATCH 086/160] Extract mem_rw and remove_rw to LockDomain.Lockset --- src/analyses/mutexAnalysis.ml | 7 +++---- src/cdomains/lockDomain.ml | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 204b064c1c..063c9e5333 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -167,18 +167,17 @@ struct | None -> ctx.local | Some mv -> let (s, m) = ctx.local in - if warn && (not (Lockset.mem (mv, true) s || Lockset.mem (mv, false) s)) then + if warn && not (Lockset.mem_rw mv s) then M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty mv; - let rm s = Lockset.remove (mv, true) (Lockset.remove (mv, false) s) in if MutexTypeAnalysis.must_be_recursive ctx mv then ( let (m', rmed) = Multiplicity.decrement mv m in if rmed then - (rm s, m') + (Lockset.remove_rw mv s, m') else (s, m') ) else - (rm s, m) + (Lockset.remove_rw mv s, m) let remove = remove' ~warn:true diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index a7b7f9bec7..1b156333ed 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -57,6 +57,12 @@ struct Mval.semantic_equal mv mv' = Some false ) set + let mem_rw mv set = + mem (mv, true) set || mem (mv, false) set + + let remove_rw mv set = + remove (mv, true) (remove (mv, false) set) + let export_locks ls = let f (x,_) set = Simple.add x set in fold f ls (Simple.empty ()) From b68e96980735c72f4e779a825419bc6f05edbf6c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 11:24:18 +0300 Subject: [PATCH 087/160] Use Mval.Z for must locksets --- src/analyses/mutexAnalysis.ml | 14 +++++++------- src/cdomains/lockDomain.ml | 35 ++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 063c9e5333..1759f278e9 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -155,7 +155,7 @@ struct let s' = Lockset.add (mv, rw) s in let m' = if MutexTypeAnalysis.must_be_recursive ctx mv then - Multiplicity.increment mv m + Multiplicity.increment (LockDomain.Mval.of_mval mv) m (* TODO: no definite check? *) else m in @@ -168,9 +168,9 @@ struct | Some mv -> let (s, m) = ctx.local in if warn && not (Lockset.mem_rw mv s) then - M.warn "unlocking mutex (%a) which may not be held" LockDomain.Mval.pretty mv; + M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; if MutexTypeAnalysis.must_be_recursive ctx mv then ( - let (m', rmed) = Multiplicity.decrement mv m in + let (m', rmed) = Multiplicity.decrement (LockDomain.Mval.of_mval mv) m in (* TODO: no definite check? *) if rmed then (Lockset.remove_rw mv s, m') else @@ -211,7 +211,7 @@ struct num_mutexes := 0; sum_protected := 0 - let query (ctx: (D.t, _, _, _) ctx) (type a) (q: a Queries.t): a Queries.result = + let query (ctx: (D.t, _, _, V.t) ctx) (type a) (q: a Queries.t): a Queries.result = let ls, m = ctx.local in (* get the set of mutexes protecting the variable v in the given mode *) let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in @@ -236,7 +236,7 @@ struct else *) non_overlapping held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (mutex, true) in + let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (LockDomain.Mval.of_mval mutex, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -245,12 +245,12 @@ struct Simple.subset mutex_lockset protecting | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - Simple.fold (fun addr ls -> Queries.AD.add (Addr addr) ls) held_locks (Queries.AD.empty ()) + Simple.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.Mval.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.Mval.of_mval m)))) in (* TODO: what if non-definite? *) VarSet.fold (fun v acc -> Queries.VS.add v acc ) protected (Queries.VS.empty ()) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 1b156333ed..0637f1ce59 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -1,7 +1,26 @@ (** Lockset domains. *) module Addr = ValueDomain.Addr -module Mval = ValueDomain.Mval +module Mval = +struct + include Mval.Z + + let of_mval ((v, o): ValueDomain.Mval.t): t = + let rec offs = function + | `NoOffset -> `NoOffset + | `Field (f, os') -> `Field (f, offs os') + | `Index (i, os') -> `Index (ValueDomain.IndexDomain.to_int i |> Option.get, offs os') + in + (v, offs o) + + let to_mval ((v, o): t): ValueDomain.Mval.t = + let rec offs = function + | `NoOffset -> `NoOffset + | `Field (f, os') -> `Field (f, offs os') + | `Index (i, os') -> `Index (ValueDomain.IndexDomain.of_int (Cilfacade.ptrdiff_ikind ()) i, offs os') + in + (v, offs o) +end module Offs = ValueDomain.Offs module Exp = CilType.Exp module IdxDom = ValueDomain.IndexDomain @@ -43,22 +62,24 @@ struct include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) let name () = "lockset" - let add ((mv, _) as lock) set = + let add (mv, rw) set = if Addr.Mval.is_definite mv then - add lock set + add (Mval.of_mval mv, rw) set else set - let remove ((mv, _) as lock) set = + let remove (mv, rw) set = if Addr.Mval.is_definite mv then - remove lock set + remove (Mval.of_mval mv, rw) set else filter (fun (mv', _) -> - Mval.semantic_equal mv mv' = Some false + (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) + ValueDomain.Mval.semantic_equal mv (Mval.to_mval mv') = Some false ) set let mem_rw mv set = - mem (mv, true) set || mem (mv, false) set + ValueDomain.Mval.is_definite mv && ( + mem (Mval.of_mval mv, true) set || mem (Mval.of_mval mv, false) set) let remove_rw mv set = remove (mv, true) (remove (mv, false) set) From d9d1326c970b751013eb887f07748383cfd2af8a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 11:59:35 +0300 Subject: [PATCH 088/160] Add test for unsound recursive mutex handling due to non-definite index --- .../05-lval_ls/24-idxunknown_recursive.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/regression/05-lval_ls/24-idxunknown_recursive.c diff --git a/tests/regression/05-lval_ls/24-idxunknown_recursive.c b/tests/regression/05-lval_ls/24-idxunknown_recursive.c new file mode 100644 index 0000000000..95e8bff798 --- /dev/null +++ b/tests/regression/05-lval_ls/24-idxunknown_recursive.c @@ -0,0 +1,38 @@ +// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] pthreadMutexType +_Bool __VERIFIER_nondet_bool(); + +#define _GNU_SOURCE +#include + +int g; + +#ifdef __APPLE__ +pthread_mutex_t m[2] = {PTHREAD_RECURSIVE_MUTEX_INITIALIZER, PTHREAD_RECURSIVE_MUTEX_INITIALIZER}; +#else +pthread_mutex_t m[2] = {PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP}; +#endif + +void *t_fun(void *arg) { + int i, j, k; + i = __VERIFIER_nondet_bool(); + j = __VERIFIER_nondet_bool(); + k = __VERIFIER_nondet_bool(); + + pthread_mutex_lock(&m[i]); // may lock m[1] + pthread_mutex_lock(&m[j]); // may lock m[1] recursively + pthread_mutex_lock(&m[0]); // must lock m[0] + pthread_mutex_unlock(&m[k]); // may unlock m[0] + // m[0] should not be in must-lockset here! + g++; // RACE! + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + g++; // RACE! + pthread_mutex_unlock(&m[0]); + return 0; +} From 659f3b0682982769dc84eab5057399c7cc2fa66c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 12:07:16 +0300 Subject: [PATCH 089/160] Fix unsound recursive mutex handling due to non-definite index --- src/analyses/mutexAnalysis.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 1759f278e9..cf78d66e1f 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -154,8 +154,8 @@ struct let (s, m) = ctx.local in let s' = Lockset.add (mv, rw) s in let m' = - if MutexTypeAnalysis.must_be_recursive ctx mv then - Multiplicity.increment (LockDomain.Mval.of_mval mv) m (* TODO: no definite check? *) + if ValueDomain.Mval.is_definite mv && MutexTypeAnalysis.must_be_recursive ctx mv then + Multiplicity.increment (LockDomain.Mval.of_mval mv) m else m in @@ -169,8 +169,8 @@ struct let (s, m) = ctx.local in if warn && not (Lockset.mem_rw mv s) then M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; - if MutexTypeAnalysis.must_be_recursive ctx mv then ( - let (m', rmed) = Multiplicity.decrement (LockDomain.Mval.of_mval mv) m in (* TODO: no definite check? *) + if ValueDomain.Mval.is_definite mv && MutexTypeAnalysis.must_be_recursive ctx mv then ( + let (m', rmed) = Multiplicity.decrement (LockDomain.Mval.of_mval mv) m in (* TODO: non-definite should also decrement (to 0)? *) if rmed then (Lockset.remove_rw mv s, m') else From d1444f2805331dcb625f0fb18cdb676bdd22eb63 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 12:23:28 +0300 Subject: [PATCH 090/160] Add second test for unsound recursive mutex handling due to non-definite index --- .../05-lval_ls/25-idxunknown_recursive_2.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/regression/05-lval_ls/25-idxunknown_recursive_2.c diff --git a/tests/regression/05-lval_ls/25-idxunknown_recursive_2.c b/tests/regression/05-lval_ls/25-idxunknown_recursive_2.c new file mode 100644 index 0000000000..47ea1ce0a7 --- /dev/null +++ b/tests/regression/05-lval_ls/25-idxunknown_recursive_2.c @@ -0,0 +1,36 @@ +// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] pthreadMutexType +_Bool __VERIFIER_nondet_bool(); + +#define _GNU_SOURCE +#include + +int g; + +#ifdef __APPLE__ +pthread_mutex_t m[2] = {PTHREAD_RECURSIVE_MUTEX_INITIALIZER, PTHREAD_RECURSIVE_MUTEX_INITIALIZER}; +#else +pthread_mutex_t m[2] = {PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP}; +#endif + +void *t_fun(void *arg) { + int i; + i = __VERIFIER_nondet_bool(); + + pthread_mutex_lock(&m[0]); // must lock m[0] + pthread_mutex_unlock(&m[i]); // may unlock m[0] + pthread_mutex_lock(&m[0]); // must lock m[0] + pthread_mutex_unlock(&m[0]); // must unlock m[0] + // m[0] should not be in must-lockset here! + g++; // RACE! + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + g++; // RACE! + pthread_mutex_unlock(&m[0]); + return 0; +} From 03311451d2633eaf57af6fe32551736e0c3ffe95 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 12:56:28 +0300 Subject: [PATCH 091/160] Fix second unsound recursive mutex handling due to non-definite index --- src/analyses/mutexAnalysis.ml | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index cf78d66e1f..a3d124900c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -41,12 +41,33 @@ struct else add v (current + 1) x + let increment mv m = + if Addr.Mval.is_definite mv then + increment (LockDomain.Mval.of_mval mv) m + else + m + let decrement v x = let current = find v x in if current = 0 then (x, true) else - (add v (current - 1) x, current - 1 = 0) + (add v (current - 1) x, current - 1 = 0) (* TODO: remove if 0? *) + + let decrement mv m = + if Addr.Mval.is_definite mv then + decrement (LockDomain.Mval.of_mval mv) m + else + (* TODO: non-definite should also decrement (to 0)? *) + fold (fun mv' _ (m, rmed) -> + (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) + if ValueDomain.Mval.semantic_equal mv (LockDomain.Mval.to_mval mv') = Some false then + (m, rmed) + else ( + let (m', rmed') = decrement mv' m in + (m', rmed || rmed') + ) + ) m (m, false) end module D = struct include Lattice.Prod(Lockset)(Multiplicity) @@ -154,8 +175,8 @@ struct let (s, m) = ctx.local in let s' = Lockset.add (mv, rw) s in let m' = - if ValueDomain.Mval.is_definite mv && MutexTypeAnalysis.must_be_recursive ctx mv then - Multiplicity.increment (LockDomain.Mval.of_mval mv) m + if MutexTypeAnalysis.must_be_recursive ctx mv then + Multiplicity.increment mv m else m in @@ -169,15 +190,17 @@ struct let (s, m) = ctx.local in if warn && not (Lockset.mem_rw mv s) then M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; - if ValueDomain.Mval.is_definite mv && MutexTypeAnalysis.must_be_recursive ctx mv then ( - let (m', rmed) = Multiplicity.decrement (LockDomain.Mval.of_mval mv) m in (* TODO: non-definite should also decrement (to 0)? *) + if MutexTypeAnalysis.must_be_recursive ctx mv then ( + let (m', rmed) = Multiplicity.decrement mv m in if rmed then + (* TODO: don't repeat the same semantic_equal checks *) + (* TODO: rmed per lockset element, not aggregated *) (Lockset.remove_rw mv s, m') else (s, m') ) else - (Lockset.remove_rw mv s, m) + (Lockset.remove_rw mv s, m) (* TODO: should decrement something if may be recursive? *) let remove = remove' ~warn:true From bdd8c3728fbf638ade5d9e8e499c29faa30d50a6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:01:48 +0300 Subject: [PATCH 092/160] Fix LockDomain indentation --- src/cdomains/lockDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 0637f1ce59..96ee361318 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -79,7 +79,7 @@ struct let mem_rw mv set = ValueDomain.Mval.is_definite mv && ( - mem (Mval.of_mval mv, true) set || mem (Mval.of_mval mv, false) set) + mem (Mval.of_mval mv, true) set || mem (Mval.of_mval mv, false) set) let remove_rw mv set = remove (mv, true) (remove (mv, false) set) From c3bb20f8dc00899aec16edbf8cb3e9d524b1876a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:48:08 +0300 Subject: [PATCH 093/160] Move Multiplicity to LockDomain --- src/analyses/mutexAnalysis.ml | 54 +---------------------------------- src/cdomains/lockDomain.ml | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index a3d124900c..f746eafb0d 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -4,6 +4,7 @@ module M = Messages module Addr = ValueDomain.Addr module Lock = LockDomain.Lock module Lockset = LockDomain.Lockset +module Multiplicity = LockDomain.Multiplicity module Simple = LockDomain.Simple module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions @@ -17,59 +18,6 @@ module Spec = struct module Arg = struct - module Multiplicity = struct - (* the maximum multiplicity which we keep track of precisely *) - let max_count () = 4 - - module Count = Lattice.Reverse ( - Lattice.Chain ( - struct - let n () = max_count () + 1 - let names x = if x = max_count () then Format.asprintf ">= %d" x else Format.asprintf "%d" x - end - ) - ) - - include MapDomain.MapTop_LiftBot (LockDomain.Mval) (Count) - - let name () = "multiplicity" - - let increment v x = - let current = find v x in - if current = max_count () then - x - else - add v (current + 1) x - - let increment mv m = - if Addr.Mval.is_definite mv then - increment (LockDomain.Mval.of_mval mv) m - else - m - - let decrement v x = - let current = find v x in - if current = 0 then - (x, true) - else - (add v (current - 1) x, current - 1 = 0) (* TODO: remove if 0? *) - - let decrement mv m = - if Addr.Mval.is_definite mv then - decrement (LockDomain.Mval.of_mval mv) m - else - (* TODO: non-definite should also decrement (to 0)? *) - fold (fun mv' _ (m, rmed) -> - (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) - if ValueDomain.Mval.semantic_equal mv (LockDomain.Mval.to_mval mv') = Some false then - (m, rmed) - else ( - let (m', rmed') = decrement mv' m in - (m', rmed || rmed') - ) - ) m (m, false) - end - module D = struct include Lattice.Prod(Lockset)(Multiplicity) let empty () = (Lockset.empty (), Multiplicity.empty ()) end diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 96ee361318..ef8e7c46fe 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -89,6 +89,59 @@ struct fold f ls (Simple.empty ()) end +module Multiplicity = struct + (* the maximum multiplicity which we keep track of precisely *) + let max_count () = 4 + + module Count = Lattice.Reverse ( + Lattice.Chain ( + struct + let n () = max_count () + 1 + let names x = if x = max_count () then Format.asprintf ">= %d" x else Format.asprintf "%d" x + end + ) + ) + + include MapDomain.MapTop_LiftBot (Mval) (Count) + + let name () = "multiplicity" + + let increment v x = + let current = find v x in + if current = max_count () then + x + else + add v (current + 1) x + + let increment mv m = + if Addr.Mval.is_definite mv then + increment (Mval.of_mval mv) m + else + m + + let decrement v x = + let current = find v x in + if current = 0 then + (x, true) + else + (add v (current - 1) x, current - 1 = 0) (* TODO: remove if 0? *) + + let decrement mv m = + if Addr.Mval.is_definite mv then + decrement (Mval.of_mval mv) m + else + (* TODO: non-definite should also decrement (to 0)? *) + fold (fun mv' _ (m, rmed) -> + (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) + if ValueDomain.Mval.semantic_equal mv (Mval.to_mval mv') = Some false then + (m, rmed) + else ( + let (m', rmed') = decrement mv' m in + (m', rmed || rmed') + ) + ) m (m, false) +end + module MayLocksetNoRW = struct include PreValueDomain.AD From 6df19ce1a896e2a463603bbeb7ed9fb215a46aad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:52:07 +0300 Subject: [PATCH 094/160] Rename LockDomain.Mval -> MustLock --- src/analyses/mutexAnalysis.ml | 12 ++++++------ src/cdomains/lockDomain.ml | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index f746eafb0d..9989308967 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -31,7 +31,7 @@ struct module V = struct - include Printable.Either (struct include CilType.Varinfo let name () = "protecting" end) (struct include LockDomain.Mval let name () = "protected" end) + include Printable.Either (struct include CilType.Varinfo let name () = "protecting" end) (struct include LockDomain.MustLock let name () = "protected" end) let name () = "mutex" let protecting x = `Left x let protected x = `Right x @@ -171,7 +171,7 @@ struct module GProtected = Arg.GProtected module G = Arg.G - module GM = Hashtbl.Make (LockDomain.Mval) + module GM = Hashtbl.Make (LockDomain.MustLock) let max_protected = ref 0 let num_mutexes = ref 0 @@ -207,7 +207,7 @@ struct else *) non_overlapping held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (LockDomain.Mval.of_mval mutex, true) in (* TODO: what if non-definite? *) + let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -216,12 +216,12 @@ struct Simple.subset mutex_lockset protecting | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in - Simple.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.Mval.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) + Simple.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) | Queries.MustBeAtomic -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.Mval.of_mval m)))) in (* TODO: what if non-definite? *) + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval m)))) in (* TODO: what if non-definite? *) VarSet.fold (fun v acc -> Queries.VS.add v acc ) protected (Queries.VS.empty ()) @@ -243,7 +243,7 @@ struct max_protected := max !max_protected s; sum_protected := !sum_protected + s; incr num_mutexes; - M.info_noloc ~category:Race "Mutex %a read-write protects %d variable(s): %a" LockDomain.Mval.pretty m s VarSet.pretty protected + M.info_noloc ~category:Race "Mutex %a read-write protects %d variable(s): %a" LockDomain.MustLock.pretty m s VarSet.pretty protected ) end | _ -> Queries.Result.top q diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index ef8e7c46fe..e68c39252c 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -1,7 +1,7 @@ (** Lockset domains. *) module Addr = ValueDomain.Addr -module Mval = +module MustLock = struct include Mval.Z @@ -28,7 +28,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) -module Simple = SetDomain.Reverse (SetDomain.ToppedSet (Mval) (struct let topname = "All mutexes" end)) +module Simple = SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) module Priorities = IntDomain.Lifted (* true means exclusive lock and false represents reader lock*) @@ -57,29 +57,29 @@ module Lock = MakeLockRW (Addr) module Lockset = struct - module Lock = MakeLockRW (Mval) + module Lock = MakeLockRW (MustLock) include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) let name () = "lockset" let add (mv, rw) set = if Addr.Mval.is_definite mv then - add (Mval.of_mval mv, rw) set + add (MustLock.of_mval mv, rw) set else set let remove (mv, rw) set = if Addr.Mval.is_definite mv then - remove (Mval.of_mval mv, rw) set + remove (MustLock.of_mval mv, rw) set else filter (fun (mv', _) -> (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) - ValueDomain.Mval.semantic_equal mv (Mval.to_mval mv') = Some false + ValueDomain.Mval.semantic_equal mv (MustLock.to_mval mv') = Some false ) set let mem_rw mv set = ValueDomain.Mval.is_definite mv && ( - mem (Mval.of_mval mv, true) set || mem (Mval.of_mval mv, false) set) + mem (MustLock.of_mval mv, true) set || mem (MustLock.of_mval mv, false) set) let remove_rw mv set = remove (mv, true) (remove (mv, false) set) @@ -102,7 +102,7 @@ module Multiplicity = struct ) ) - include MapDomain.MapTop_LiftBot (Mval) (Count) + include MapDomain.MapTop_LiftBot (MustLock) (Count) let name () = "multiplicity" @@ -115,7 +115,7 @@ module Multiplicity = struct let increment mv m = if Addr.Mval.is_definite mv then - increment (Mval.of_mval mv) m + increment (MustLock.of_mval mv) m else m @@ -128,12 +128,12 @@ module Multiplicity = struct let decrement mv m = if Addr.Mval.is_definite mv then - decrement (Mval.of_mval mv) m + decrement (MustLock.of_mval mv) m else (* TODO: non-definite should also decrement (to 0)? *) fold (fun mv' _ (m, rmed) -> (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) - if ValueDomain.Mval.semantic_equal mv (Mval.to_mval mv') = Some false then + if ValueDomain.Mval.semantic_equal mv (MustLock.to_mval mv') = Some false then (m, rmed) else ( let (m', rmed') = decrement mv' m in From 3acb191b94a7070d23fcd0ddb12a0b87bac88930 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:54:56 +0300 Subject: [PATCH 095/160] Rename LockDomain.Lockset -> MustLocksetRW --- src/analyses/mutexAnalysis.ml | 36 ++++++++++++++++++----------------- src/cdomains/lockDomain.ml | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9989308967..ac1d480e3f 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -3,7 +3,7 @@ module M = Messages module Addr = ValueDomain.Addr module Lock = LockDomain.Lock -module Lockset = LockDomain.Lockset +module MustLocksetRW = LockDomain.MustLocksetRW module Multiplicity = LockDomain.Multiplicity module Simple = LockDomain.Simple module Mutexes = LockDomain.Mutexes @@ -18,8 +18,10 @@ module Spec = struct module Arg = struct - module D = struct include Lattice.Prod(Lockset)(Multiplicity) - let empty () = (Lockset.empty (), Multiplicity.empty ()) + module D = + struct + include Lattice.Prod (MustLocksetRW) (Multiplicity) + let empty () = (MustLocksetRW.empty (), Multiplicity.empty ()) end @@ -121,7 +123,7 @@ struct match Addr.to_mval addr with | Some mv -> let (s, m) = ctx.local in - let s' = Lockset.add (mv, rw) s in + let s' = MustLocksetRW.add (mv, rw) s in let m' = if MutexTypeAnalysis.must_be_recursive ctx mv then Multiplicity.increment mv m @@ -136,19 +138,19 @@ struct | None -> ctx.local | Some mv -> let (s, m) = ctx.local in - if warn && not (Lockset.mem_rw mv s) then + if warn && not (MustLocksetRW.mem_rw mv s) then M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; if MutexTypeAnalysis.must_be_recursive ctx mv then ( let (m', rmed) = Multiplicity.decrement mv m in if rmed then (* TODO: don't repeat the same semantic_equal checks *) (* TODO: rmed per lockset element, not aggregated *) - (Lockset.remove_rw mv s, m') + (MustLocksetRW.remove_rw mv s, m') else (s, m') ) else - (Lockset.remove_rw mv s, m) (* TODO: should decrement something if may be recursive? *) + (MustLocksetRW.remove_rw mv s, m) (* TODO: should decrement something if may be recursive? *) let remove = remove' ~warn:true @@ -188,18 +190,18 @@ struct let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in let non_overlapping locks1 locks2 = Simple.is_empty @@ Simple.inter locks1 locks2 in match q with - | Queries.MayBePublic _ when Lockset.is_bot ls -> false + | Queries.MayBePublic _ when MustLocksetRW.is_bot ls -> false | Queries.MayBePublic {global=v; write; protection} -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then false else *) non_overlapping held_locks protecting - | Queries.MayBePublicWithout _ when Lockset.is_bot ls -> false + | Queries.MayBePublicWithout _ when MustLocksetRW.is_bot ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = Lockset.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = MustLocksetRW.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then @@ -207,7 +209,7 @@ struct else *) non_overlapping held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) + let mutex_lockset = MustLocksetRW.export_locks @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -215,10 +217,10 @@ struct else *) Simple.subset mutex_lockset protecting | Queries.MustLockset -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in Simple.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) | Queries.MustBeAtomic -> - let held_locks = Lockset.export_locks (Lockset.filter snd ls) in + let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval m)))) in (* TODO: what if non-definite? *) @@ -250,7 +252,7 @@ struct module A = struct - include Lockset + include MustLocksetRW let name () = "lock" let may_race ls1 ls2 = (* not mutually exclusive *) @@ -278,8 +280,8 @@ struct (*privatization*) match var_opt with | Some v -> - if not (Lockset.is_bot (fst octx.local)) then - let locks = Lockset.export_locks (Lockset.filter snd (fst octx.local)) in + if not (MustLocksetRW.is_bot (fst octx.local)) then + let locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd (fst octx.local)) in let write = match kind with | Write | Free -> true | Read -> false diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index e68c39252c..779b59ba6c 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -55,7 +55,7 @@ end module Lock = MakeLockRW (Addr) -module Lockset = +module MustLocksetRW = struct module Lock = MakeLockRW (MustLock) From 3d4a91dfe46c9a1ce14e1fc36362b03aa2ea923b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:56:52 +0300 Subject: [PATCH 096/160] Extract LockDomain.MustLockRW --- src/cdomains/lockDomain.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 779b59ba6c..2b214b5852 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -55,11 +55,11 @@ end module Lock = MakeLockRW (Addr) +module MustLockRW = MakeLockRW (MustLock) + module MustLocksetRW = struct - module Lock = MakeLockRW (MustLock) - - include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end)) + include SetDomain.Reverse (SetDomain.ToppedSet (MustLockRW) (struct let topname = "All mutexes" end)) let name () = "lockset" let add (mv, rw) set = From 3a8d939ef017082266f6848ec3858f594a89b7bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 13:58:32 +0300 Subject: [PATCH 097/160] Rename LockDomain.Multiplicity -> MustMultiplicity --- src/analyses/mutexAnalysis.ml | 10 +++++----- src/cdomains/lockDomain.ml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index ac1d480e3f..49c08024ac 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -4,7 +4,7 @@ module M = Messages module Addr = ValueDomain.Addr module Lock = LockDomain.Lock module MustLocksetRW = LockDomain.MustLocksetRW -module Multiplicity = LockDomain.Multiplicity +module MustMultiplicity = LockDomain.MustMultiplicity module Simple = LockDomain.Simple module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions @@ -20,8 +20,8 @@ struct struct module D = struct - include Lattice.Prod (MustLocksetRW) (Multiplicity) - let empty () = (MustLocksetRW.empty (), Multiplicity.empty ()) + include Lattice.Prod (MustLocksetRW) (MustMultiplicity) + let empty () = (MustLocksetRW.empty (), MustMultiplicity.empty ()) end @@ -126,7 +126,7 @@ struct let s' = MustLocksetRW.add (mv, rw) s in let m' = if MutexTypeAnalysis.must_be_recursive ctx mv then - Multiplicity.increment mv m + MustMultiplicity.increment mv m else m in @@ -141,7 +141,7 @@ struct if warn && not (MustLocksetRW.mem_rw mv s) then M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; if MutexTypeAnalysis.must_be_recursive ctx mv then ( - let (m', rmed) = Multiplicity.decrement mv m in + let (m', rmed) = MustMultiplicity.decrement mv m in if rmed then (* TODO: don't repeat the same semantic_equal checks *) (* TODO: rmed per lockset element, not aggregated *) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 2b214b5852..532db49661 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -89,7 +89,7 @@ struct fold f ls (Simple.empty ()) end -module Multiplicity = struct +module MustMultiplicity = struct (* the maximum multiplicity which we keep track of precisely *) let max_count () = 4 From 18eedd46819d570d6aa46bfbc882711cfc226784 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:02:25 +0300 Subject: [PATCH 098/160] Rename LockDomain.Simple -> MustLockset --- src/analyses/mutexAnalysis.ml | 30 +++++++++++++++--------------- src/cdomains/lockDomain.ml | 6 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 49c08024ac..5ac8f91513 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -3,9 +3,9 @@ module M = Messages module Addr = ValueDomain.Addr module Lock = LockDomain.Lock +module MustLockset = LockDomain.MustLockset module MustLocksetRW = LockDomain.MustLocksetRW module MustMultiplicity = LockDomain.MustMultiplicity -module Simple = LockDomain.Simple module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open GoblintCil @@ -71,17 +71,17 @@ struct (** Collects information about which variables are protected by which mutexes *) module GProtecting: sig include Lattice.S - val make: write:bool -> recovered:bool -> Simple.t -> t - val get: write:bool -> Queries.Protection.t -> t -> Simple.t + val make: write:bool -> recovered:bool -> MustLockset.t -> t + val get: write:bool -> Queries.Protection.t -> t -> MustLockset.t end = struct - include MakeP (Simple) + include MakeP (MustLockset) let make ~write ~recovered locks = (* If the access is not a write, set to T so intersection with current write-protecting is identity *) - let wlocks = if write then locks else Simple.bot () in + let wlocks = if write then locks else MustLockset.bot () in if recovered then (* If we are in single-threaded mode again, this does not need to be added to set of mutexes protecting in mt-mode only *) - ((locks, wlocks), (Simple.bot (), Simple.bot ())) + ((locks, wlocks), (MustLockset.bot (), MustLockset.bot ())) else ((locks, wlocks), (locks, wlocks)) end @@ -188,7 +188,7 @@ struct let ls, m = ctx.local in (* get the set of mutexes protecting the variable v in the given mode *) let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in - let non_overlapping locks1 locks2 = Simple.is_empty @@ Simple.inter locks1 locks2 in + let non_overlapping locks1 locks2 = MustLockset.is_empty @@ MustLockset.inter locks1 locks2 in match q with | Queries.MayBePublic _ when MustLocksetRW.is_bot ls -> false | Queries.MayBePublic {global=v; write; protection} -> @@ -215,13 +215,13 @@ struct (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then true else *) - Simple.subset mutex_lockset protecting + MustLockset.subset mutex_lockset protecting | Queries.MustLockset -> let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in - Simple.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) + MustLockset.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) | Queries.MustBeAtomic -> let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in - Simple.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) + MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval m)))) in (* TODO: what if non-definite? *) VarSet.fold (fun v acc -> @@ -235,8 +235,8 @@ struct | `Left g' -> (* protecting *) if GobConfig.get_bool "dbg.print_protection" then ( let protecting = GProtecting.get ~write:false Strong (G.protecting (ctx.global g)) in (* readwrite protecting *) - let s = Simple.cardinal protecting in - M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s Simple.pretty protecting + let s = MustLockset.cardinal protecting in + M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s MustLockset.pretty protecting ) | `Right m -> (* protected *) if GobConfig.get_bool "dbg.print_protection" then ( @@ -297,10 +297,10 @@ struct let held_weak = protecting Weak in let vs = VarSet.singleton v in let protected = G.create_protected @@ GProtected.make ~write vs in - Simple.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_strong; + MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_strong; (* If the mutex set here is top, it is actually not accessed *) - if is_recovered_to_st && not @@ Simple.is_bot held_weak then - Simple.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_weak; + if is_recovered_to_st && not @@ MustLockset.is_bot held_weak then + MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_weak; ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 532db49661..0a37b45454 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -28,7 +28,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) -module Simple = SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) +module MustLockset = SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) module Priorities = IntDomain.Lifted (* true means exclusive lock and false represents reader lock*) @@ -85,8 +85,8 @@ struct remove (mv, true) (remove (mv, false) set) let export_locks ls = - let f (x,_) set = Simple.add x set in - fold f ls (Simple.empty ()) + let f (x,_) set = MustLockset.add x set in + fold f ls (MustLockset.empty ()) end module MustMultiplicity = struct From e3bc1399ec2c8f17008d22b4c349ac13bc9bf8ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:04:55 +0300 Subject: [PATCH 099/160] Remove unused LockDomain.Priorities --- src/cdomains/lockDomain.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 0a37b45454..28f3f5b0c6 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -29,7 +29,6 @@ open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) module MustLockset = SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) -module Priorities = IntDomain.Lifted (* true means exclusive lock and false represents reader lock*) module RW = IntDomain.Booleans From 538c394eadda7109c0745aef9a6ebbd37e3a335b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:15:09 +0300 Subject: [PATCH 100/160] Add all and is_all to must locksets --- src/analyses/mutexAnalysis.ml | 14 +++++++------- src/cdomains/lockDomain.ml | 11 ++++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 5ac8f91513..5fabc1f597 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -78,10 +78,10 @@ struct let make ~write ~recovered locks = (* If the access is not a write, set to T so intersection with current write-protecting is identity *) - let wlocks = if write then locks else MustLockset.bot () in + let wlocks = if write then locks else MustLockset.all () in if recovered then (* If we are in single-threaded mode again, this does not need to be added to set of mutexes protecting in mt-mode only *) - ((locks, wlocks), (MustLockset.bot (), MustLockset.bot ())) + ((locks, wlocks), (MustLockset.all (), MustLockset.all ())) else ((locks, wlocks), (locks, wlocks)) end @@ -190,7 +190,7 @@ struct let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in let non_overlapping locks1 locks2 = MustLockset.is_empty @@ MustLockset.inter locks1 locks2 in match q with - | Queries.MayBePublic _ when MustLocksetRW.is_bot ls -> false + | Queries.MayBePublic _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublic {global=v; write; protection} -> let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in let protecting = protecting ~write protection v in @@ -199,7 +199,7 @@ struct false else *) non_overlapping held_locks protecting - | Queries.MayBePublicWithout _ when MustLocksetRW.is_bot ls -> false + | Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> let held_locks = MustLocksetRW.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in let protecting = protecting ~write protection v in @@ -215,7 +215,7 @@ struct (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then true else *) - MustLockset.subset mutex_lockset protecting + MustLockset.subset mutex_lockset protecting (* TODO: some mem? *) | Queries.MustLockset -> let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in MustLockset.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) @@ -280,7 +280,7 @@ struct (*privatization*) match var_opt with | Some v -> - if not (MustLocksetRW.is_bot (fst octx.local)) then + if not (MustLocksetRW.is_all (fst octx.local)) then let locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd (fst octx.local)) in let write = match kind with | Write | Free -> true @@ -299,7 +299,7 @@ struct let protected = G.create_protected @@ GProtected.make ~write vs in MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_strong; (* If the mutex set here is top, it is actually not accessed *) - if is_recovered_to_st && not @@ MustLockset.is_bot held_weak then + if is_recovered_to_st && not @@ MustLockset.is_all held_weak then MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_weak; ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 28f3f5b0c6..78f29dbd7f 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -28,7 +28,13 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) -module MustLockset = SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) +module MustLockset = +struct + include SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) + + let all (): t = `Top + let is_all (set: t) = set = `Top +end (* true means exclusive lock and false represents reader lock*) module RW = IntDomain.Booleans @@ -83,6 +89,9 @@ struct let remove_rw mv set = remove (mv, true) (remove (mv, false) set) + let all (): t = `Top + let is_all (set: t) = set = `Top + let export_locks ls = let f (x,_) set = MustLockset.add x set in fold f ls (MustLockset.empty ()) From 250be968592a96c39027ac7eba6fdc994391de8f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:25:01 +0300 Subject: [PATCH 101/160] Use MustLockset.disjoint in mutex analysis --- src/analyses/mutexAnalysis.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 5fabc1f597..0721263938 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -188,7 +188,6 @@ struct let ls, m = ctx.local in (* get the set of mutexes protecting the variable v in the given mode *) let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in - let non_overlapping locks1 locks2 = MustLockset.is_empty @@ MustLockset.inter locks1 locks2 in match q with | Queries.MayBePublic _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublic {global=v; write; protection} -> @@ -198,7 +197,7 @@ struct (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then false else *) - non_overlapping held_locks protecting + MustLockset.disjoint held_locks protecting | Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> let held_locks = MustLocksetRW.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in @@ -207,7 +206,7 @@ struct (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then false else *) - non_overlapping held_locks protecting + MustLockset.disjoint held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) let mutex_lockset = MustLocksetRW.export_locks @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in From 0624c7f76f5179bfb8f0cd2625fb3c0fcb3ef040 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:38:06 +0300 Subject: [PATCH 102/160] Remove Offset.Index.Printable.top --- src/cdomain/value/cdomains/mval.ml | 3 ++- src/cdomain/value/cdomains/mval_intf.ml | 6 +++--- src/cdomain/value/cdomains/offset.ml | 8 ++++---- src/cdomain/value/cdomains/offset_intf.ml | 12 ++++++------ 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/cdomain/value/cdomains/mval.ml b/src/cdomain/value/cdomains/mval.ml index 6dd64b68da..b9831fa513 100644 --- a/src/cdomain/value/cdomains/mval.ml +++ b/src/cdomain/value/cdomains/mval.ml @@ -36,12 +36,13 @@ struct let to_cil_exp lv = Lval (to_cil lv) let is_definite (_, o) = Offs.is_definite o - let top_indices (x, o) = (x, Offs.top_indices o) end module MakeLattice (Offs: Offset.Lattice): Lattice with type idx = Offs.idx = struct include MakePrintable (Offs) + + let top_indices (x, o) = (x, Offs.top_indices o) let semantic_equal (x, xoffs) (y, yoffs) = if CilType.Varinfo.equal x y then diff --git a/src/cdomain/value/cdomains/mval_intf.ml b/src/cdomain/value/cdomains/mval_intf.ml index 18aa81c16b..542eb1a300 100644 --- a/src/cdomain/value/cdomains/mval_intf.ml +++ b/src/cdomain/value/cdomains/mval_intf.ml @@ -17,9 +17,6 @@ sig @return [Some o] if it is (such that the variables are equal and [add_offset m1 o = m2]), [None] if it is not. *) - val top_indices: t -> t - (** Change all indices to top indices. *) - val to_cil: t -> GoblintCil.lval (** Convert to CIL lvalue. *) @@ -35,6 +32,9 @@ sig include Printable (** @closed *) include Lattice.S with type t := t (** @closed *) + val top_indices: t -> t + (** Change all indices to top indices. *) + val semantic_equal: t -> t -> bool option (** Check semantic equality of two mvalues. diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 1329f461eb..6d2e312db4 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -41,7 +41,6 @@ struct let equal_to _ _ = `Top (* TODO: more precise for definite indices *) let to_int _ = None (* TODO: more precise for definite indices *) - let top () = Lazy.force any end module Z = @@ -58,7 +57,6 @@ struct end ) - let top () = failwith "Offset.Index.Z.top" (* TODO: remove from interface? *) let to_int z = Some z let equal_to z1 z2 = if Z.equal z2 z2 then @@ -156,8 +154,6 @@ struct | `Field (f, o) -> `Field (f, map_indices g o) | `Index (i, o) -> `Index (g i, map_indices g o) - let top_indices = map_indices (fun _ -> Idx.top ()) - (* tries to follow o in t *) let rec type_of ~base:t o = match unrollType t, o with (* resolves TNamed *) | t, `NoOffset -> t @@ -202,6 +198,8 @@ struct let widen x y = merge `Widen x y let narrow x y = merge `Narrow x y + let top_indices = map_indices (fun _ -> Idx.top ()) + (* NB! Currently we care only about concrete indexes. Base (seeing only a int domain element) answers with any_index_exp on all non-concrete cases. *) let rec of_exp: exp offs -> t = function @@ -269,6 +267,8 @@ module Exp = struct include MakePrintable (Index.Exp) + let top_indices = map_indices (fun _ -> Lazy.force Index.Exp.any) + let rec of_cil: offset -> t = function | NoOffset -> `NoOffset | Index (i,o) -> `Index (i, of_cil o) diff --git a/src/cdomain/value/cdomains/offset_intf.ml b/src/cdomain/value/cdomains/offset_intf.ml index a0d9ce144c..0c5ccf6a89 100644 --- a/src/cdomain/value/cdomains/offset_intf.ml +++ b/src/cdomain/value/cdomains/offset_intf.ml @@ -15,9 +15,6 @@ struct sig include Printable.S (** @closed *) - val top: unit -> t - (** Unknown index. *) - val equal_to: Z.t -> t -> [`Eq | `Neq | `Top] (** Check semantic equality of an integer and the index. @@ -59,9 +56,6 @@ sig val map_indices: (idx -> idx) -> t -> t (** Apply function to all indexing. *) - val top_indices: t -> t - (** Change all indices to top indices. *) - val to_cil: t -> GoblintCil.offset (** Convert to CIL offset. *) @@ -89,6 +83,9 @@ sig include Printable (** @closed *) include Lattice.S with type t := t (** @closed *) + val top_indices: t -> t + (** Change all indices to top indices. *) + val of_exp: GoblintCil.exp offs -> t (** Convert from Goblint offset with {!GoblintCil.exp} indices. *) @@ -170,6 +167,9 @@ sig sig include Printable with type idx = GoblintCil.exp + val top_indices: t -> t + (** Change all indices to top indices. *) + val of_cil : GoblintCil.offset -> t (** Convert from CIL offset. *) From 36aa2f04907839623d82b611e80e8050dd26cfc3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:45:43 +0300 Subject: [PATCH 103/160] Extract Printable.Z --- src/cdomain/value/cdomains/offset.ml | 12 +----------- src/common/domains/printable.ml | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 6d2e312db4..52867c1a6f 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -45,18 +45,8 @@ struct module Z = struct - include Printable.StdLeaf (* TODO: move to GobZ *) - include GobZ + include Printable.Z let name () = "Z index" - - (* TODO: move to GobZ *) - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - let to_int z = Some z let equal_to z1 z2 = if Z.equal z2 z2 then diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index b0e5d725a7..79b2f73518 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -803,3 +803,17 @@ struct let to_yojson x = x (* override SimplePretty *) end + +module Z: S with type t = Z.t = +struct + include StdLeaf + include GobZ + let name () = "Z" + + include SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) +end From 470b431e1e14ba72f32466faf332cdfaa2d5264a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 14:57:30 +0300 Subject: [PATCH 104/160] Add Offset.Poly.map_indices --- src/cdomain/value/cdomains/offset.ml | 20 ++++++++++++-------- src/cdomain/value/cdomains/offset_intf.ml | 9 +++++++++ src/cdomains/lockDomain.ml | 14 ++------------ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 52867c1a6f..9c9c5c3333 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -57,6 +57,13 @@ struct end +module Poly = +struct + let rec map_indices g: 'a t -> 'b t = function + | `NoOffset -> `NoOffset + | `Field (f, o) -> `Field (f, map_indices g o) + | `Index (i, o) -> `Index (g i, map_indices g o) +end module MakePrintable (Idx: Index.Printable): Printable with type idx = Idx.t = struct @@ -90,6 +97,8 @@ struct end ) + include Poly + let rec is_definite: t -> bool = function | `NoOffset -> true | `Field (f,o) -> is_definite o @@ -114,7 +123,7 @@ struct | `Field(f,o) -> Field(f, to_cil_offset o) | `Index(i,o) -> NoOffset (* array domain can not deal with this -> leads to being handeled as access to unknown part *) - let rec to_exp: t -> exp offs = function + let rec to_exp: t -> exp offs = function (* TODO: Poly.map_indices *) | `NoOffset -> `NoOffset | `Index (i,o) -> let i_exp = match Idx.to_int i with @@ -139,11 +148,6 @@ struct | `Field (_, os) -> contains_index os | `Index _ -> true - let rec map_indices g: t -> t = function - | `NoOffset -> `NoOffset - | `Field (f, o) -> `Field (f, map_indices g o) - | `Index (i, o) -> `Index (g i, map_indices g o) - (* tries to follow o in t *) let rec type_of ~base:t o = match unrollType t, o with (* resolves TNamed *) | t, `NoOffset -> t @@ -192,7 +196,7 @@ struct (* NB! Currently we care only about concrete indexes. Base (seeing only a int domain element) answers with any_index_exp on all non-concrete cases. *) - let rec of_exp: exp offs -> t = function + let rec of_exp: exp offs -> t = function (* TODO: Poly.map_indices *) | `NoOffset -> `NoOffset | `Index (Const (CInt (i,ik,s)),o) -> `Index (Idx.of_int ik i, of_exp o) | `Index (_,o) -> `Index (Idx.top (), of_exp o) @@ -242,7 +246,7 @@ struct include MakePrintable (Index.Unit) (* TODO: rename to of_poly? *) - let rec of_offs: 'i offs -> t = function + let rec of_offs: 'i offs -> t = function (* TODO: Poly.map_indices *) | `NoOffset -> `NoOffset | `Field (f,o) -> `Field (f, of_offs o) | `Index (i,o) -> `Index ((), of_offs o) diff --git a/src/cdomain/value/cdomains/offset_intf.ml b/src/cdomain/value/cdomains/offset_intf.ml index 0c5ccf6a89..e858f72214 100644 --- a/src/cdomain/value/cdomains/offset_intf.ml +++ b/src/cdomain/value/cdomains/offset_intf.ml @@ -142,6 +142,15 @@ sig exception Type_of_error of GoblintCil.typ * string (** {!Printable.type_of} could not follow offset completely. *) + (** Polymorphic offset operations. *) + module Poly: + sig + val map_indices: ('a -> 'b) -> 'a t -> 'b t + (** Apply function to all indexing. *) + + (* TODO: include more operations *) + end + module type Printable = Printable module type Lattice = Lattice diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 78f29dbd7f..bb365a0def 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -6,20 +6,10 @@ struct include Mval.Z let of_mval ((v, o): ValueDomain.Mval.t): t = - let rec offs = function - | `NoOffset -> `NoOffset - | `Field (f, os') -> `Field (f, offs os') - | `Index (i, os') -> `Index (ValueDomain.IndexDomain.to_int i |> Option.get, offs os') - in - (v, offs o) + (v, Offset.Poly.map_indices (fun i -> ValueDomain.IndexDomain.to_int i |> Option.get) o) let to_mval ((v, o): t): ValueDomain.Mval.t = - let rec offs = function - | `NoOffset -> `NoOffset - | `Field (f, os') -> `Field (f, offs os') - | `Index (i, os') -> `Index (ValueDomain.IndexDomain.of_int (Cilfacade.ptrdiff_ikind ()) i, offs os') - in - (v, offs o) + (v, Offset.Poly.map_indices (ValueDomain.IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) o) end module Offs = ValueDomain.Offs module Exp = CilType.Exp From 45d47a402788e6f2d0566d23c2654a52f03c13ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:08:16 +0300 Subject: [PATCH 105/160] Clean up module aliases in LockDomain --- src/analyses/mutexAnalysis.ml | 3 ++- src/cdomains/lockDomain.ml | 35 +++++++++++++++++++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 0721263938..bce072e3ee 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -1,6 +1,7 @@ (** Must lockset and protecting lockset analysis ([mutex]). *) module M = Messages +module Mval = ValueDomain.Mval module Addr = ValueDomain.Addr module Lock = LockDomain.Lock module MustLockset = LockDomain.MustLockset @@ -139,7 +140,7 @@ struct | Some mv -> let (s, m) = ctx.local in if warn && not (MustLocksetRW.mem_rw mv s) then - M.warn "unlocking mutex (%a) which may not be held" ValueDomain.Mval.pretty mv; + M.warn "unlocking mutex (%a) which may not be held" Mval.pretty mv; if MutexTypeAnalysis.must_be_recursive ctx mv then ( let (m', rmed) = MustMultiplicity.decrement mv m in if rmed then diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index bb365a0def..6d14a4099e 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -1,21 +1,24 @@ (** Lockset domains. *) +open GoblintCil +open struct + module MvalZ = Mval.Z (* So we can define MustLock using Mval.Z after redefining Mval *) +end + +module IndexDomain = ValueDomain.IndexDomain +module Mval = ValueDomain.Mval module Addr = ValueDomain.Addr + module MustLock = struct - include Mval.Z + include MvalZ - let of_mval ((v, o): ValueDomain.Mval.t): t = - (v, Offset.Poly.map_indices (fun i -> ValueDomain.IndexDomain.to_int i |> Option.get) o) + let of_mval ((v, o): Mval.t): t = + (v, Offset.Poly.map_indices (fun i -> IndexDomain.to_int i |> Option.get) o) - let to_mval ((v, o): t): ValueDomain.Mval.t = - (v, Offset.Poly.map_indices (ValueDomain.IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) o) + let to_mval ((v, o): t): Mval.t = + (v, Offset.Poly.map_indices (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) o) end -module Offs = ValueDomain.Offs -module Exp = CilType.Exp -module IdxDom = ValueDomain.IndexDomain - -open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) module MustLockset = @@ -68,12 +71,12 @@ struct remove (MustLock.of_mval mv, rw) set else filter (fun (mv', _) -> - (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) - ValueDomain.Mval.semantic_equal mv (MustLock.to_mval mv') = Some false + (* TODO: avoid conversion: semantic_equal between Mval and Mval *) + Mval.semantic_equal mv (MustLock.to_mval mv') = Some false ) set let mem_rw mv set = - ValueDomain.Mval.is_definite mv && ( + Mval.is_definite mv && ( mem (MustLock.of_mval mv, true) set || mem (MustLock.of_mval mv, false) set) let remove_rw mv set = @@ -130,8 +133,8 @@ module MustMultiplicity = struct else (* TODO: non-definite should also decrement (to 0)? *) fold (fun mv' _ (m, rmed) -> - (* TODO: avoid conversion: semantic_equal between ValueDomain.Mval and Mval *) - if ValueDomain.Mval.semantic_equal mv (MustLock.to_mval mv') = Some false then + (* TODO: avoid conversion: semantic_equal between Mval and Mval *) + if Mval.semantic_equal mv (MustLock.to_mval mv') = Some false then (m, rmed) else ( let (m', rmed') = decrement mv' m in @@ -148,7 +151,7 @@ end module Symbolic = struct (* TODO: use SetDomain.Reverse *) - module S = SetDomain.ToppedSet (Exp) (struct let topname = "All mutexes" end) + module S = SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All mutexes" end) include Lattice.Reverse (S) let rec eq_set (ask: Queries.ask) e = From c7b54b76ef41205c4c468a01ffe7059ccb2f2710 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:10:31 +0300 Subject: [PATCH 106/160] Rename LockDomain.Lock -> AddrRW --- src/analyses/deadlock.ml | 2 +- src/analyses/locksetAnalysis.ml | 2 +- src/analyses/mutexAnalysis.ml | 4 ++-- src/cdomains/lockDomain.ml | 7 +++---- src/domains/events.ml | 4 ++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/analyses/deadlock.ml b/src/analyses/deadlock.ml index 23b9faa882..e83b122fd9 100644 --- a/src/analyses/deadlock.ml +++ b/src/analyses/deadlock.ml @@ -30,7 +30,7 @@ struct let part_access ctx: MCPAccess.A.t = Obj.obj (ctx.ask (PartAccess Point)) - let add ctx ((l, _): LockDomain.Lock.t) = + let add ctx ((l, _): LockDomain.AddrRW.t) = let after: LockEvent.t = (l, ctx.prev_node, part_access ctx) in (* use octx for access to use locksets before event *) D.iter (fun before -> side_lock_event_pair ctx before after diff --git a/src/analyses/locksetAnalysis.ml b/src/analyses/locksetAnalysis.ml index c7c7395dc1..4c8ec3544c 100644 --- a/src/analyses/locksetAnalysis.ml +++ b/src/analyses/locksetAnalysis.ml @@ -29,7 +29,7 @@ sig module G: Lattice.S module V: SpecSysVar - val add: (D.t, G.t, D.t, V.t) ctx -> LockDomain.Lock.t -> D.t + val add: (D.t, G.t, D.t, V.t) ctx -> LockDomain.AddrRW.t -> D.t val remove: (D.t, G.t, D.t, V.t) ctx -> ValueDomain.Addr.t -> D.t end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index bce072e3ee..23246326b2 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -3,7 +3,7 @@ module M = Messages module Mval = ValueDomain.Mval module Addr = ValueDomain.Addr -module Lock = LockDomain.Lock +module AddrRW = LockDomain.AddrRW module MustLockset = LockDomain.MustLockset module MustLocksetRW = LockDomain.MustLocksetRW module MustMultiplicity = LockDomain.MustMultiplicity @@ -120,7 +120,7 @@ struct let create_protected protected = `Lifted2 protected end - let add ctx ((addr, rw): Lock.t): D.t = + let add ctx ((addr, rw): AddrRW.t): D.t = match Addr.to_mval addr with | Some mv -> let (s, m) = ctx.local in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 6d14a4099e..e492cfe65d 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -33,7 +33,7 @@ end module RW = IntDomain.Booleans (* pair Addr and RW; also change pretty printing*) -module MakeLockRW (P: Printable.S) = +module MakeRW (P: Printable.S) = struct include Printable.Prod (P) (RW) @@ -51,9 +51,8 @@ struct ) end -module Lock = MakeLockRW (Addr) - -module MustLockRW = MakeLockRW (MustLock) +module AddrRW = MakeRW (Addr) +module MustLockRW = MakeRW (MustLock) module MustLocksetRW = struct diff --git a/src/domains/events.ml b/src/domains/events.ml index 90eb7611be..b194847bac 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -4,7 +4,7 @@ open GoblintCil open Pretty type t = - | Lock of LockDomain.Lock.t + | Lock of LockDomain.AddrRW.t | Unlock of LockDomain.Addr.t | Escape of EscapeDomain.EscapedVars.t | EnterMultiThreaded @@ -35,7 +35,7 @@ let emit_on_deadcode = function false let pretty () = function - | Lock m -> dprintf "Lock %a" LockDomain.Lock.pretty m + | Lock m -> dprintf "Lock %a" LockDomain.AddrRW.pretty m | Unlock m -> dprintf "Unlock %a" LockDomain.Addr.pretty m | Escape escaped -> dprintf "Escape %a" EscapeDomain.EscapedVars.pretty escaped | EnterMultiThreaded -> text "EnterMultiThreaded" From 8aabfa6a3338db8cf7480ba0f70c8c825ac31157 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:11:44 +0300 Subject: [PATCH 107/160] Remove LockDomain.Mutexes --- src/analyses/mutexAnalysis.ml | 1 - src/analyses/mutexEventsAnalysis.ml | 3 +-- src/cdomains/lockDomain.ml | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 23246326b2..baeb79c49a 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -7,7 +7,6 @@ module AddrRW = LockDomain.AddrRW module MustLockset = LockDomain.MustLockset module MustLocksetRW = LockDomain.MustLocksetRW module MustMultiplicity = LockDomain.MustMultiplicity -module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open GoblintCil open Analyses diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 2252dae8a7..2f31384ab9 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -4,7 +4,6 @@ module M = Messages module Addr = ValueDomain.Addr -module Mutexes = LockDomain.Mutexes module LF = LibraryFunctions open Batteries open GoblintCil @@ -20,7 +19,7 @@ struct let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointTo exp) let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = - let compute_refine_split (e:Mutexes.elt) = match e with + let compute_refine_split (e: Addr.t) = match e with | Addr a -> let e' = BinOp(Eq, arg, AddrOf ((PreValueDomain.Mval.to_cil a)), intType) in [Events.SplitBranch (e',true)] diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index e492cfe65d..a171700348 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -20,7 +20,6 @@ struct (v, Offset.Poly.map_indices (IndexDomain.of_int (Cilfacade.ptrdiff_ikind ())) o) end -module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) module MustLockset = struct include SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) From 449dc646b36fee626dd67058acbe1cfa0f6db510 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:19:28 +0300 Subject: [PATCH 108/160] Rename LockDomain.MustLocksetRW functions --- src/analyses/mutexAnalysis.ml | 20 ++++++++++---------- src/cdomains/lockDomain.ml | 13 +++++++------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index baeb79c49a..20050ecea3 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -123,7 +123,7 @@ struct match Addr.to_mval addr with | Some mv -> let (s, m) = ctx.local in - let s' = MustLocksetRW.add (mv, rw) s in + let s' = MustLocksetRW.add_mval_rw (mv, rw) s in let m' = if MutexTypeAnalysis.must_be_recursive ctx mv then MustMultiplicity.increment mv m @@ -138,19 +138,19 @@ struct | None -> ctx.local | Some mv -> let (s, m) = ctx.local in - if warn && not (MustLocksetRW.mem_rw mv s) then + if warn && not (MustLocksetRW.mem_mval mv s) then M.warn "unlocking mutex (%a) which may not be held" Mval.pretty mv; if MutexTypeAnalysis.must_be_recursive ctx mv then ( let (m', rmed) = MustMultiplicity.decrement mv m in if rmed then (* TODO: don't repeat the same semantic_equal checks *) (* TODO: rmed per lockset element, not aggregated *) - (MustLocksetRW.remove_rw mv s, m') + (MustLocksetRW.remove_mval mv s, m') else (s, m') ) else - (MustLocksetRW.remove_rw mv s, m) (* TODO: should decrement something if may be recursive? *) + (MustLocksetRW.remove_mval mv s, m) (* TODO: should decrement something if may be recursive? *) let remove = remove' ~warn:true @@ -191,7 +191,7 @@ struct match q with | Queries.MayBePublic _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublic {global=v; write; protection} -> - let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in + let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then @@ -200,7 +200,7 @@ struct MustLockset.disjoint held_locks protecting | Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = MustLocksetRW.export_locks @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = MustLocksetRW.to_must_lockset @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then @@ -208,7 +208,7 @@ struct else *) MustLockset.disjoint held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = MustLocksetRW.export_locks @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) + let mutex_lockset = MustLocksetRW.to_must_lockset @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -216,10 +216,10 @@ struct else *) MustLockset.subset mutex_lockset protecting (* TODO: some mem? *) | Queries.MustLockset -> - let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in + let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) | Queries.MustBeAtomic -> - let held_locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd ls) in + let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval m)))) in (* TODO: what if non-definite? *) @@ -280,7 +280,7 @@ struct match var_opt with | Some v -> if not (MustLocksetRW.is_all (fst octx.local)) then - let locks = MustLocksetRW.export_locks (MustLocksetRW.filter snd (fst octx.local)) in + let locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd (fst octx.local)) in let write = match kind with | Write | Free -> true | Read -> false diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index a171700348..aa14834e4a 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -50,6 +50,7 @@ struct ) end +module MvalRW = MakeRW (Mval) module AddrRW = MakeRW (Addr) module MustLockRW = MakeRW (MustLock) @@ -58,13 +59,13 @@ struct include SetDomain.Reverse (SetDomain.ToppedSet (MustLockRW) (struct let topname = "All mutexes" end)) let name () = "lockset" - let add (mv, rw) set = + let add_mval_rw ((mv, rw): MvalRW.t) (set: t) = if Addr.Mval.is_definite mv then add (MustLock.of_mval mv, rw) set else set - let remove (mv, rw) set = + let remove_mval_rw ((mv, rw): MvalRW.t) (set: t) = if Addr.Mval.is_definite mv then remove (MustLock.of_mval mv, rw) set else @@ -73,17 +74,17 @@ struct Mval.semantic_equal mv (MustLock.to_mval mv') = Some false ) set - let mem_rw mv set = + let mem_mval (mv: Mval.t) (set: t) = Mval.is_definite mv && ( mem (MustLock.of_mval mv, true) set || mem (MustLock.of_mval mv, false) set) - let remove_rw mv set = - remove (mv, true) (remove (mv, false) set) + let remove_mval (mv: Mval.t) set = + remove_mval_rw (mv, true) (remove_mval_rw (mv, false) set) let all (): t = `Top let is_all (set: t) = set = `Top - let export_locks ls = + let to_must_lockset (ls: t): MustLockset.t = let f (x,_) set = MustLockset.add x set in fold f ls (MustLockset.empty ()) end From b1eda024ddd31becf22e258b0c7046f1941f190e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:35:03 +0300 Subject: [PATCH 109/160] Add LockDomain.MustLock.semantic_equal_mval --- src/cdomains/lockDomain.ml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index aa14834e4a..c308de6b7a 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -13,6 +13,18 @@ module MustLock = struct include MvalZ + let semantic_equal_mval ((v, o): t) ((v', o'): Mval.t): bool option = + if CilType.Varinfo.equal v v' then ( + let (index1, _) = GoblintCil.bitsOffset v.vtype (Offset.Z.to_cil o) in (* TODO: better way to compute this? as Z.t not int *) + let index2: IndexDomain.t = ValueDomain.Offs.to_index ~typ:v.vtype o' in (* TODO: is this bits or bytes? *) + match IndexDomain.equal_to (Z.of_int index1) index2 with + | `Eq -> Some true + | `Neq -> Some false + | `Top -> None + ) + else + Some false + let of_mval ((v, o): Mval.t): t = (v, Offset.Poly.map_indices (fun i -> IndexDomain.to_int i |> Option.get) o) @@ -70,8 +82,7 @@ struct remove (MustLock.of_mval mv, rw) set else filter (fun (mv', _) -> - (* TODO: avoid conversion: semantic_equal between Mval and Mval *) - Mval.semantic_equal mv (MustLock.to_mval mv') = Some false + MustLock.semantic_equal_mval mv' mv = Some false ) set let mem_mval (mv: Mval.t) (set: t) = @@ -132,8 +143,7 @@ module MustMultiplicity = struct else (* TODO: non-definite should also decrement (to 0)? *) fold (fun mv' _ (m, rmed) -> - (* TODO: avoid conversion: semantic_equal between Mval and Mval *) - if Mval.semantic_equal mv (MustLock.to_mval mv') = Some false then + if MustLock.semantic_equal_mval mv' mv = Some false then (m, rmed) else ( let (m', rmed') = decrement mv' m in From c712fcf2bd77fa560076485035d91870a74c8e62 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 15:41:25 +0300 Subject: [PATCH 110/160] Update some variable names in mutex analysis --- src/analyses/mutexAnalysis.ml | 12 ++++++------ src/cdomains/lockDomain.ml | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 20050ecea3..ed1efbc2ef 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -207,8 +207,8 @@ struct false else *) MustLockset.disjoint held_locks protecting - | Queries.MustBeProtectedBy {mutex = Addr mutex; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = MustLocksetRW.to_must_lockset @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex, true) in (* TODO: what if non-definite? *) + | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} -> (* TODO: non-Addr? *) + let mutex_lockset = MustLocksetRW.to_must_lockset @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex_mv, true) in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then @@ -221,8 +221,8 @@ struct | Queries.MustBeAtomic -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) - | Queries.MustProtectedVars {mutex = Addr m; write} -> (* TODO: non-Addr? *) - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval m)))) in (* TODO: what if non-definite? *) + | Queries.MustProtectedVars {mutex = Addr mutex_mv; write} -> (* TODO: non-Addr? *) + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval mutex_mv)))) in (* TODO: what if non-definite? *) VarSet.fold (fun v acc -> Queries.VS.add v acc ) protected (Queries.VS.empty ()) @@ -296,10 +296,10 @@ struct let held_weak = protecting Weak in let vs = VarSet.singleton v in let protected = G.create_protected @@ GProtected.make ~write vs in - MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_strong; + MustLockset.iter (fun ml -> ctx.sideg (V.protected ml) protected) held_strong; (* If the mutex set here is top, it is actually not accessed *) if is_recovered_to_st && not @@ MustLockset.is_all held_weak then - MustLockset.iter (fun mv -> ctx.sideg (V.protected mv) protected) held_weak; + MustLockset.iter (fun ml -> ctx.sideg (V.protected ml) protected) held_weak; ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index c308de6b7a..e106b204c5 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -81,13 +81,17 @@ struct if Addr.Mval.is_definite mv then remove (MustLock.of_mval mv, rw) set else - filter (fun (mv', _) -> - MustLock.semantic_equal_mval mv' mv = Some false + filter (fun (ml, _) -> + MustLock.semantic_equal_mval ml mv = Some false ) set let mem_mval (mv: Mval.t) (set: t) = - Mval.is_definite mv && ( - mem (MustLock.of_mval mv, true) set || mem (MustLock.of_mval mv, false) set) + if Mval.is_definite mv then ( + let ml = MustLock.of_mval mv in + mem (ml, true) set || mem (ml, false) set + ) + else + false let remove_mval (mv: Mval.t) set = remove_mval_rw (mv, true) (remove_mval_rw (mv, false) set) @@ -96,7 +100,7 @@ struct let is_all (set: t) = set = `Top let to_must_lockset (ls: t): MustLockset.t = - let f (x,_) set = MustLockset.add x set in + let f (ml, _) set = MustLockset.add ml set in fold f ls (MustLockset.empty ()) end @@ -142,11 +146,11 @@ module MustMultiplicity = struct decrement (MustLock.of_mval mv) m else (* TODO: non-definite should also decrement (to 0)? *) - fold (fun mv' _ (m, rmed) -> - if MustLock.semantic_equal_mval mv' mv = Some false then + fold (fun ml _ (m, rmed) -> + if MustLock.semantic_equal_mval ml mv = Some false then (m, rmed) else ( - let (m', rmed') = decrement mv' m in + let (m', rmed') = decrement ml m in (m', rmed || rmed') ) ) m (m, false) From 91e31d72ae997e99ce5500940e2db965198c99a7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 16:07:31 +0300 Subject: [PATCH 111/160] Use mem for MustBeProtectedBy query --- src/analyses/mutexAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index ed1efbc2ef..a9253f26e8 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -208,13 +208,13 @@ struct else *) MustLockset.disjoint held_locks protecting | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} -> (* TODO: non-Addr? *) - let mutex_lockset = MustLocksetRW.to_must_lockset @@ MustLocksetRW.singleton (LockDomain.MustLock.of_mval mutex_mv, true) in (* TODO: what if non-definite? *) + let ml = LockDomain.MustLock.of_mval mutex_mv in (* TODO: what if non-definite? *) let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then true else *) - MustLockset.subset mutex_lockset protecting (* TODO: some mem? *) + MustLockset.mem ml protecting | Queries.MustLockset -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) From ac16e0249c1e6e02ad55dd76e8e8b5f7bcb94ea2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:57:06 +0300 Subject: [PATCH 112/160] Move LockDomain.Symbolic to SymbLocksDomain --- src/analyses/symbLocks.ml | 4 +-- src/cdomains/lockDomain.ml | 53 +-------------------------------- src/cdomains/symbLocksDomain.ml | 51 +++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index 6fd18de6ff..b1727ace81 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -23,8 +23,8 @@ struct exception Top - module D = LockDomain.Symbolic - module C = LockDomain.Symbolic + module D = SymbLocksDomain.Symbolic + module C = SymbLocksDomain.Symbolic let name () = "symb_locks" diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index e106b204c5..35d73e5f28 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -1,6 +1,5 @@ (** Lockset domains. *) -open GoblintCil open struct module MvalZ = Mval.Z (* So we can define MustLock using Mval.Z after redefining Mval *) end @@ -52,7 +51,7 @@ struct if write then P.pretty () a else - Pretty.dprintf "read lock %a" P.pretty a + GoblintCil.Pretty.dprintf "read lock %a" P.pretty a include Printable.SimplePretty ( struct @@ -160,53 +159,3 @@ module MayLocksetNoRW = struct include PreValueDomain.AD end - -module Symbolic = -struct - (* TODO: use SetDomain.Reverse *) - module S = SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All mutexes" end) - include Lattice.Reverse (S) - - let rec eq_set (ask: Queries.ask) e = - S.union - (match ask.f (Queries.EqualSet e) with - | es when not (Queries.ES.is_bot es) -> - Queries.ES.fold S.add es (S.empty ()) - | _ -> S.empty ()) - (match e with - | SizeOf _ - | SizeOfE _ - | SizeOfStr _ - | AlignOf _ - | Const _ - | AlignOfE _ - | UnOp _ - | BinOp _ - | Question _ - | Real _ - | Imag _ - | AddrOfLabel _ -> S.empty () - | AddrOf (Var _,_) - | StartOf (Var _,_) - | Lval (Var _,_) -> S.singleton e - | AddrOf (Mem e,ofs) -> S.map (fun e -> AddrOf (Mem e,ofs)) (eq_set ask e) - | StartOf (Mem e,ofs) -> S.map (fun e -> StartOf (Mem e,ofs)) (eq_set ask e) - | Lval (Mem e,ofs) -> S.map (fun e -> Lval (Mem e,ofs)) (eq_set ask e) - | CastE (_,e) -> eq_set ask e - ) - - let add (ask: Queries.ask) e st = - let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in - let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in - S.union addrs st - let remove ask e st = - (* TODO: Removing based on must-equality sets is not sound! *) - let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in - let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in - S.diff st addrs - let remove_var v st = S.filter (fun x -> not (SymbLocksDomain.Exp.contains_var v x)) st - - let filter = S.filter - let fold = S.fold - -end diff --git a/src/cdomains/symbLocksDomain.ml b/src/cdomains/symbLocksDomain.ml index ba2b96e8d0..10ea70da1d 100644 --- a/src/cdomains/symbLocksDomain.ml +++ b/src/cdomains/symbLocksDomain.ml @@ -317,3 +317,54 @@ struct let of_mval (v, o) = of_mval (v, conv_const_offset o) end + + +module Symbolic = +struct + (* TODO: use SetDomain.Reverse *) + module S = SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All mutexes" end) + include Lattice.Reverse (S) + + let rec eq_set (ask: Queries.ask) e = + S.union + (match ask.f (Queries.EqualSet e) with + | es when not (Queries.ES.is_bot es) -> + Queries.ES.fold S.add es (S.empty ()) + | _ -> S.empty ()) + (match e with + | SizeOf _ + | SizeOfE _ + | SizeOfStr _ + | AlignOf _ + | Const _ + | AlignOfE _ + | UnOp _ + | BinOp _ + | Question _ + | Real _ + | Imag _ + | AddrOfLabel _ -> S.empty () + | AddrOf (Var _,_) + | StartOf (Var _,_) + | Lval (Var _,_) -> S.singleton e + | AddrOf (Mem e,ofs) -> S.map (fun e -> AddrOf (Mem e,ofs)) (eq_set ask e) + | StartOf (Mem e,ofs) -> S.map (fun e -> StartOf (Mem e,ofs)) (eq_set ask e) + | Lval (Mem e,ofs) -> S.map (fun e -> Lval (Mem e,ofs)) (eq_set ask e) + | CastE (_,e) -> eq_set ask e + ) + + let add (ask: Queries.ask) e st = + let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in + let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in + S.union addrs st + let remove ask e st = + (* TODO: Removing based on must-equality sets is not sound! *) + let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in + let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in + S.diff st addrs + let remove_var v st = S.filter (fun x -> not (Exp.contains_var v x)) st + + let filter = S.filter + let fold = S.fold + +end From 3fbbfa28616b0eaf205a1384227b8244dd81afee Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 17:55:10 +0300 Subject: [PATCH 113/160] Use LockDomain.MustLockset for MustLockset query --- src/analyses/commonPriv.ml | 8 ++++---- src/analyses/mutexAnalysis.ml | 2 +- src/domains/queries.ml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 6376a6ca11..9f6fc14bd8 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -73,8 +73,8 @@ struct ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection}) let protected_vars (ask: Q.ask): varinfo list = - Q.AD.fold (fun m acc -> - Q.VS.join (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc + LockDomain.MustLockset.fold (fun ml acc -> + Q.VS.join (ask.f (Q.MustProtectedVars {mutex = Addr (LockDomain.MustLock.to_mval ml); write = true})) acc (* TODO: avoid Addr conversion *) ) (ask.f Q.MustLockset) (Q.VS.empty ()) |> Q.VS.elements end @@ -136,8 +136,8 @@ struct if !AnalysisState.global_initialization then Lockset.empty () else - let ad = ask.f Queries.MustLockset in - Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: use AD as Lockset *) + let mls = ask.f Queries.MustLockset in + LockDomain.MustLockset.fold (fun ml acc -> Lockset.add (Addr (LockDomain.MustLock.to_mval ml)) acc) mls (Lockset.empty ()) (* TODO: use MustLockset as Lockset *) (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index a9253f26e8..339856fcc9 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -217,7 +217,7 @@ struct MustLockset.mem ml protecting | Queries.MustLockset -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in - MustLockset.fold (fun addr ls -> Queries.AD.add (Addr (LockDomain.MustLock.to_mval addr)) ls) held_locks (Queries.AD.empty ()) (* TODO: Z indices for Queries.MustLockset result? *) + held_locks | Queries.MustBeAtomic -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index f5d7be21a4..7c6553d1bf 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -74,7 +74,7 @@ type _ t = | MayBePublic: maybepublic -> MayBool.t t (* old behavior with write=false *) | MayBePublicWithout: maybepublicwithout -> MayBool.t t | MustBeProtectedBy: mustbeprotectedby -> MustBool.t t - | MustLockset: AD.t t + | MustLockset: LockDomain.MustLockset.t t | MustBeAtomic: MustBool.t t | MustBeSingleThreaded: {since_start: bool} -> MustBool.t t | MustBeUniqueThread: MustBool.t t @@ -147,7 +147,7 @@ struct | MayPointTo _ -> (module AD) | ReachableFrom _ -> (module AD) | Regions _ -> (module LS) - | MustLockset -> (module AD) + | MustLockset -> (module LockDomain.MustLockset) | EvalFunvar _ -> (module AD) | ReachableUkTypes _ -> (module TS) | MayEscape _ -> (module MayBool) @@ -216,7 +216,7 @@ struct | MayPointTo _ -> AD.top () | ReachableFrom _ -> AD.top () | Regions _ -> LS.top () - | MustLockset -> AD.top () + | MustLockset -> LockDomain.MustLockset.top () | EvalFunvar _ -> AD.top () | ReachableUkTypes _ -> TS.top () | MayEscape _ -> MayBool.top () From edf476a0005b37f384aaf962513093950c0d1af9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 17:59:35 +0300 Subject: [PATCH 114/160] Use LockDomain.MustLock for MustProtectedVars query --- src/analyses/commonPriv.ml | 2 +- src/analyses/mutexAnalysis.ml | 4 ++-- src/domains/queries.ml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 9f6fc14bd8..ed236459da 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -74,7 +74,7 @@ struct let protected_vars (ask: Q.ask): varinfo list = LockDomain.MustLockset.fold (fun ml acc -> - Q.VS.join (ask.f (Q.MustProtectedVars {mutex = Addr (LockDomain.MustLock.to_mval ml); write = true})) acc (* TODO: avoid Addr conversion *) + Q.VS.join (ask.f (Q.MustProtectedVars {mutex = ml; write = true})) acc ) (ask.f Q.MustLockset) (Q.VS.empty ()) |> Q.VS.elements end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 339856fcc9..71b3a7e07e 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -221,8 +221,8 @@ struct | Queries.MustBeAtomic -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) - | Queries.MustProtectedVars {mutex = Addr mutex_mv; write} -> (* TODO: non-Addr? *) - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected (LockDomain.MustLock.of_mval mutex_mv)))) in (* TODO: what if non-definite? *) + | Queries.MustProtectedVars {mutex; write} -> + let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected mutex))) in VarSet.fold (fun v acc -> Queries.VS.add v acc ) protected (Queries.VS.empty ()) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 7c6553d1bf..a904f696eb 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -51,7 +51,7 @@ end type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: PreValueDomain.Addr.t; protection: Protection.t} [@@deriving ord, hash] type mustbeprotectedby = {mutex: PreValueDomain.Addr.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] -type mustprotectedvars = {mutex: PreValueDomain.Addr.t; write: bool} [@@deriving ord, hash] +type mustprotectedvars = {mutex: LockDomain.MustLock.t; write: bool} [@@deriving ord, hash] type access = | Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *) | Point (** Program point and state access (MHP), independent of memory location. *) From 3a92918e183a9959966b5340c7dca6b8b20a2238 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 18:17:53 +0300 Subject: [PATCH 115/160] Fix MustBeProtectedBy TODO --- src/analyses/mutexAnalysis.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 71b3a7e07e..4e369d0de2 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -207,8 +207,8 @@ struct false else *) MustLockset.disjoint held_locks protecting - | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} -> (* TODO: non-Addr? *) - let ml = LockDomain.MustLock.of_mval mutex_mv in (* TODO: what if non-definite? *) + | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} when Mval.is_definite mutex_mv -> (* only definite Addrs can be in must-locksets to begin with, anything else cannot protect anything *) + let ml = LockDomain.MustLock.of_mval mutex_mv in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then From f533707765e9eef01828121b66815d57169fb2bd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 12:41:13 +0300 Subject: [PATCH 116/160] Restore NULL mutex unlock warning --- src/analyses/mutexAnalysis.ml | 11 ++++++++--- tests/regression/06-symbeq/21-mult_accs_rc.t | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 4e369d0de2..a624b557c5 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -134,9 +134,14 @@ struct | None -> ctx.local let remove' ctx ~warn (addr: Addr.t): D.t = - match Addr.to_mval addr with - | None -> ctx.local - | Some mv -> + match addr with + | StrPtr _ + | UnknownPtr -> ctx.local + | NullPtr -> + if warn then + M.warn "unlocking NULL mutex"; + ctx.local + | Addr mv -> let (s, m) = ctx.local in if warn && not (MustLocksetRW.mem_mval mv s) then M.warn "unlocking mutex (%a) which may not be held" Mval.pretty mv; diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index 711205e38c..f32ca24131 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -10,6 +10,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Race] Memory location (struct s).data (race with conf. 100): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) @@ -25,6 +26,7 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:15:3-15:14) From 68d431ac107dd8cff5cf05d16b995d1efc196186 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 12:46:07 +0300 Subject: [PATCH 117/160] Add NULL mutex lock warning --- src/analyses/mutexAnalysis.ml | 10 +++++++--- tests/regression/06-symbeq/21-mult_accs_rc.t | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index a624b557c5..bcf66b7973 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -120,8 +120,8 @@ struct end let add ctx ((addr, rw): AddrRW.t): D.t = - match Addr.to_mval addr with - | Some mv -> + match addr with + | Addr mv -> let (s, m) = ctx.local in let s' = MustLocksetRW.add_mval_rw (mv, rw) s in let m' = @@ -131,7 +131,11 @@ struct m in (s', m') - | None -> ctx.local + | NullPtr -> + M.warn "locking NULL mutex"; + ctx.local + | StrPtr _ + | UnknownPtr -> ctx.local let remove' ctx ~warn (addr: Addr.t): D.t = match addr with diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index f32ca24131..ca2e219b05 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -10,6 +10,9 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Race] Memory location (struct s).data (race with conf. 100): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) @@ -26,6 +29,9 @@ Disable info messages because race summary contains (safe) memory location count [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) [Error][Imprecise][Unsound] Function definition missing for get_s (21-mult_accs_rc.c:13:3-13:14) From bb5011becb222b1015e06c612554331eb7f8a0b4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 27 May 2024 10:51:29 +0300 Subject: [PATCH 118/160] Update mutex analysis may-be-recursive unlock TODO --- src/analyses/mutexAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index bcf66b7973..b44795b6da 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -159,7 +159,7 @@ struct (s, m') ) else - (MustLocksetRW.remove_mval mv s, m) (* TODO: should decrement something if may be recursive? *) + (MustLocksetRW.remove_mval mv s, m) (* Should decrement something if may be recursive? No: https://github.com/goblint/analyzer/pull/1430#discussion_r1615266081. *) let remove = remove' ~warn:true From 7373da71966a958881bc18ececf4e0d9bc60c1e3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 10 Jun 2024 14:03:20 +0200 Subject: [PATCH 119/160] GobZ: derive `eq`, `ord`, `hash` --- src/util/std/gobZ.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/std/gobZ.ml b/src/util/std/gobZ.ml index 02110ac8bb..72f4b3bd74 100644 --- a/src/util/std/gobZ.ml +++ b/src/util/std/gobZ.ml @@ -1,4 +1,4 @@ -type t = Z.t +type t = Z.t [@@deriving eq, ord, hash] let to_yojson z = `Intlit (Z.to_string z) From b84f77be8987327350abdfc6af9e89b6d6f0fdcd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 11:35:58 +0300 Subject: [PATCH 120/160] Add unsound test for unlocking non-definite mutex in privatizations --- .../13-privatized/93-unlock-idx-ambiguous.c | 27 +++++++ .../13-privatized/93-unlock-idx-ambiguous.t | 75 +++++++++++++++++++ tests/regression/13-privatized/dune | 2 + 3 files changed, 104 insertions(+) create mode 100644 tests/regression/13-privatized/93-unlock-idx-ambiguous.c create mode 100644 tests/regression/13-privatized/93-unlock-idx-ambiguous.t create mode 100644 tests/regression/13-privatized/dune diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.c b/tests/regression/13-privatized/93-unlock-idx-ambiguous.c new file mode 100644 index 0000000000..081302514b --- /dev/null +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.c @@ -0,0 +1,27 @@ +// PARAM: --set ana.base.privatization protection --enable ana.sv-comp.functions +#include +#include +extern _Bool __VERIFIER_nondet_bool(); + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + pthread_mutex_lock(&m[1]); // so we're unlocking a mutex we definitely hold + g++; + int r = __VERIFIER_nondet_bool(); + pthread_mutex_unlock(&m[r]); // TODO NOWARN (definitely held either way) + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t new file mode 100644 index 0000000000..f954ac1d4c --- /dev/null +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t @@ -0,0 +1,75 @@ +TODO: should not succeed + + $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/13-privatized/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From 8d4abd8810b2f1512ead0583e76365ee727293b9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 14:32:41 +0300 Subject: [PATCH 121/160] Fix base privatization unsoundness with non-definite mutex unlock --- src/analyses/base.ml | 34 +++++++++++++++---- .../13-privatized/93-unlock-idx-ambiguous.t | 20 +++-------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2cd4aa1456..9bbd02dd80 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -3048,13 +3048,35 @@ struct match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; - Priv.lock ask (priv_getg ctx.global) st addr + begin match addr with + | UnknownPtr -> ctx.local + | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local + | Addr (v, o) when Addr.Mval.is_definite (v, o) -> + Priv.lock ask (priv_getg ctx.global) st addr + | _ -> ctx.local + end | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - if addr = UnknownPtr then - M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) + begin match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) + ctx.local (* TODO: remove all! *) + | Addr (v, o) when Addr.Mval.is_definite (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + ) + | Addr (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + let s = ctx.ask MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = Addr.Addr (LockDomain.MustLock.to_mval ml) in + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + ) s st + ) + | _ -> ctx.local + end | Events.Escape escaped -> Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped | Events.EnterMultiThreaded -> diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t index f954ac1d4c..8f24d728bf 100644 --- a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t @@ -1,8 +1,6 @@ -TODO: should not succeed - $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -13,11 +11,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -28,11 +24,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -43,11 +37,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -58,11 +50,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 From 11a6705508ab382d9c7ec9045fc0024ed35bde42 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:10:30 +0300 Subject: [PATCH 122/160] Add unsound test for unlocking non-definite mutex in relational privatizations --- .../46-apron2/87-unlock-idx-ambiguous.c | 28 +++++++++++++ .../46-apron2/87-unlock-idx-ambiguous.t | 39 +++++++++++++++++++ tests/regression/46-apron2/dune | 3 ++ 3 files changed, 70 insertions(+) create mode 100644 tests/regression/46-apron2/87-unlock-idx-ambiguous.c create mode 100644 tests/regression/46-apron2/87-unlock-idx-ambiguous.t diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c new file mode 100644 index 0000000000..97cada5369 --- /dev/null +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c @@ -0,0 +1,28 @@ +// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag +// TODO: why nonterm without threadflag path-sens? +#include +#include +extern _Bool __VERIFIER_nondet_bool(); + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + pthread_mutex_lock(&m[1]); // so we're unlocking a mutex we definitely hold + g++; + int r = __VERIFIER_nondet_bool(); + pthread_mutex_unlock(&m[r]); // TODO NOWARN (definitely held either way) + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t new file mode 100644 index 0000000000..5ff63babdc --- /dev/null +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t @@ -0,0 +1,39 @@ + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/46-apron2/dune b/tests/regression/46-apron2/dune index ca3efec235..8652d1a371 100644 --- a/tests/regression/46-apron2/dune +++ b/tests/regression/46-apron2/dune @@ -8,3 +8,6 @@ (glob_files ??-*.c)) (locks /update_suite) (action (chdir ../../.. (run %{update_suite} group apron2)))) + +(cram + (deps (glob_files *.c))) From b9a2268dd3cc9e292f41a56cf732387a20938c95 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:18:34 +0300 Subject: [PATCH 123/160] Port base privatization non-definite mutex unlock unsoundness fix to relation --- src/analyses/apron/relationAnalysis.apron.ml | 34 +++++++++++++++---- .../46-apron2/87-unlock-idx-ambiguous.t | 6 ++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 4242dfaf2a..1b6912acca 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -681,13 +681,35 @@ struct let st = ctx.local in match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr + begin match addr with + | UnknownPtr -> ctx.local + | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr + | _ -> ctx.local + end | Events.Unlock addr when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - if addr = UnknownPtr then - M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) + begin match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) + ctx.local (* TODO: remove all! *) + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + ) + | Addr (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + let s = ctx.ask MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in + Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + ) s st + ) + | _ -> ctx.local + end | Events.EnterMultiThreaded -> Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t index 5ff63babdc..abbdd74f00 100644 --- a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t @@ -1,6 +1,6 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -13,7 +13,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -26,7 +26,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 From 6c69bf8463d36e784c00544743f1f6ce616f185c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:42:26 +0300 Subject: [PATCH 124/160] Extract CommonPriv.lift_lock and lift_unlock --- src/analyses/apron/relationAnalysis.apron.ml | 41 ++++++-------------- src/analyses/base.ml | 36 ++++------------- src/analyses/commonPriv.ml | 27 +++++++++++++ 3 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 1b6912acca..91e94543f7 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -678,38 +678,19 @@ struct ctx.local let event ctx e octx = + let ask = Analyses.ask_of_ctx ctx in let st = ctx.local in match e with - | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> ctx.local - | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr - | _ -> ctx.local - end - | Events.Unlock addr when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) - ctx.local (* TODO: remove all! *) - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) - | Addr (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - let s = ctx.ask MustLockset in - LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then - st - else - let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) s st - ) - | _ -> ctx.local - end + | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) + CommonPriv.lift_lock ask (fun st m -> + Priv.lock ask ctx.global st m + ) st addr + | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) + WideningTokens.with_local_side_tokens (fun () -> + CommonPriv.lift_unlock ask (fun st m -> + Priv.unlock ask ctx.global ctx.sideg st m + ) st addr + ) | Events.EnterMultiThreaded -> Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9bbd02dd80..8d2b8f3ba5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -3048,35 +3048,15 @@ struct match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; - begin match addr with - | UnknownPtr -> ctx.local - | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local - | Addr (v, o) when Addr.Mval.is_definite (v, o) -> - Priv.lock ask (priv_getg ctx.global) st addr - | _ -> ctx.local - end + CommonPriv.lift_lock ask (fun st m -> + Priv.lock ask (priv_getg ctx.global) st m + ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) - ctx.local (* TODO: remove all! *) - | Addr (v, o) when Addr.Mval.is_definite (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) - | Addr (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - let s = ctx.ask MustLockset in - LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then - st - else - let addr = Addr.Addr (LockDomain.MustLock.to_mval ml) in - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) s st - ) - | _ -> ctx.local - end + WideningTokens.with_local_side_tokens (fun () -> + CommonPriv.lift_unlock ask (fun st m -> + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st m + ) st addr + ) | Events.Escape escaped -> Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped | Events.EnterMultiThreaded -> diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index ed236459da..e425b451df 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -296,3 +296,30 @@ struct let startstate () = W.bot (), LMust.top (), L.bot () end + + +let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + match addr with + | UnknownPtr -> st + | Addr (v, _) when ask.f (IsMultiple v) -> st + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + f st addr + | _ -> st + +let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) + st (* TODO: remove all! *) + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + f st addr + | Addr (v, o) -> + let s = ask.f MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in + f st addr + ) s st + | _ -> st From 06a585a2c7a1bbcd5aedbc4710b4d8d8d5c6bcb0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:54:35 +0300 Subject: [PATCH 125/160] Refactor CommonPriv.lift_lock and lift_unlock --- src/analyses/commonPriv.ml | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index e425b451df..46558b812e 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -299,27 +299,36 @@ end let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + (* Should be in sync with: + 1. LocksetAnalysis.MakeMust.event + 2. MutexAnalysis.Spec.Arg.add + 3. LockDomain.MustLocksetRW.add_mval_rw *) match addr with | UnknownPtr -> st | Addr (v, _) when ask.f (IsMultiple v) -> st - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - f st addr - | _ -> st + | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr _ + | NullPtr + | StrPtr _ -> st let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + (* Should be in sync with: + 1. LocksetAnalysis.MakeMust.event + 2. MutexAnalysis.Spec.Arg.remove + 3. MutexAnalysis.Spec.Arg.remove_all + 4. LockDomain.MustLocksetRW.remove_mval_rw *) match addr with | UnknownPtr -> M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) st (* TODO: remove all! *) - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - f st addr - | Addr (v, o) -> - let s = ask.f MustLockset in + | StrPtr _ + | NullPtr -> st + | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv -> LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + if LockDomain.MustLock.semantic_equal_mval ml mv = Some false then st else - let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in - f st addr - ) s st - | _ -> st + (* call privatization's unlock only with definite lock *) + f st (Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + ) (ask.f MustLockset) st From 12e5551f1980f837f8ad92067c842912b8233e17 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:00:46 +0300 Subject: [PATCH 126/160] Add unsound test for unlocking unknown mutex in privatizations --- .../13-privatized/94-unlock-unknown.c | 25 +++++++ .../13-privatized/94-unlock-unknown.t | 75 +++++++++++++++++++ .../regression/46-apron2/88-unlock-unknown.c | 25 +++++++ .../regression/46-apron2/88-unlock-unknown.t | 45 +++++++++++ 4 files changed, 170 insertions(+) create mode 100644 tests/regression/13-privatized/94-unlock-unknown.c create mode 100644 tests/regression/13-privatized/94-unlock-unknown.t create mode 100644 tests/regression/46-apron2/88-unlock-unknown.c create mode 100644 tests/regression/46-apron2/88-unlock-unknown.t diff --git a/tests/regression/13-privatized/94-unlock-unknown.c b/tests/regression/13-privatized/94-unlock-unknown.c new file mode 100644 index 0000000000..0897c38de8 --- /dev/null +++ b/tests/regression/13-privatized/94-unlock-unknown.c @@ -0,0 +1,25 @@ +// PARAM: --set ana.base.privatization protection --enable ana.sv-comp.functions +#include +#include + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + g++; + pthread_mutex_t *r; // rand + pthread_mutex_unlock(r); + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/13-privatized/94-unlock-unknown.t b/tests/regression/13-privatized/94-unlock-unknown.t new file mode 100644 index 0000000000..2d4e0d97cc --- /dev/null +++ b/tests/regression/13-privatized/94-unlock-unknown.t @@ -0,0 +1,75 @@ + $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/46-apron2/88-unlock-unknown.c b/tests/regression/46-apron2/88-unlock-unknown.c new file mode 100644 index 0000000000..3e09ee8659 --- /dev/null +++ b/tests/regression/46-apron2/88-unlock-unknown.c @@ -0,0 +1,25 @@ +// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions +#include +#include + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + g++; + pthread_mutex_t *r; // rand + pthread_mutex_unlock(r); + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/46-apron2/88-unlock-unknown.t b/tests/regression/46-apron2/88-unlock-unknown.t new file mode 100644 index 0000000000..f6649a116e --- /dev/null +++ b/tests/regression/46-apron2/88-unlock-unknown.t @@ -0,0 +1,45 @@ + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + From ad233e1abf6590a6171d73e922f44344aa024b0b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:02:51 +0300 Subject: [PATCH 127/160] Fix privatization unlocking unknown mutex unsoundness --- src/analyses/commonPriv.ml | 6 ++++-- .../regression/13-privatized/94-unlock-unknown.t | 15 +++++---------- tests/regression/46-apron2/88-unlock-unknown.t | 9 +++------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 46558b812e..e7964d200c 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -319,8 +319,10 @@ let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = 4. LockDomain.MustLocksetRW.remove_mval_rw *) match addr with | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) - st (* TODO: remove all! *) + LockDomain.MustLockset.fold (fun ml st -> + (* call privatization's unlock only with definite lock *) + f st (LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + ) (ask.f MustLockset) st | StrPtr _ | NullPtr -> st | Addr mv when LockDomain.Mval.is_definite mv -> f st addr diff --git a/tests/regression/13-privatized/94-unlock-unknown.t b/tests/regression/13-privatized/94-unlock-unknown.t index 2d4e0d97cc..cc0aa921fe 100644 --- a/tests/regression/13-privatized/94-unlock-unknown.t +++ b/tests/regression/13-privatized/94-unlock-unknown.t @@ -1,8 +1,7 @@ $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -14,10 +13,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -29,10 +27,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -44,10 +41,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -59,10 +55,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 diff --git a/tests/regression/46-apron2/88-unlock-unknown.t b/tests/regression/46-apron2/88-unlock-unknown.t index f6649a116e..35e4ec4503 100644 --- a/tests/regression/46-apron2/88-unlock-unknown.t +++ b/tests/regression/46-apron2/88-unlock-unknown.t @@ -1,8 +1,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -14,10 +13,9 @@ total memory locations: 1 $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -29,10 +27,9 @@ total memory locations: 1 $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 From c06af6842ec5e6e6770d8c00a5e2f861999af6da Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Jun 2024 13:31:43 +0300 Subject: [PATCH 128/160] Add SKIP to Apron tests (PR #1500) --- tests/regression/46-apron2/87-unlock-idx-ambiguous.c | 2 +- tests/regression/46-apron2/88-unlock-unknown.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c index 97cada5369..ff6461217e 100644 --- a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag // TODO: why nonterm without threadflag path-sens? #include #include diff --git a/tests/regression/46-apron2/88-unlock-unknown.c b/tests/regression/46-apron2/88-unlock-unknown.c index 3e09ee8659..dbdc4b9f77 100644 --- a/tests/regression/46-apron2/88-unlock-unknown.c +++ b/tests/regression/46-apron2/88-unlock-unknown.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions #include #include From 44d7abb7589d1b94322f46479860a545e4df0bd1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Jun 2024 17:37:39 +0300 Subject: [PATCH 129/160] Add enabled_if to Apron cram tests (PR #1500) --- tests/regression/46-apron2/dune | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/regression/46-apron2/dune b/tests/regression/46-apron2/dune index 8652d1a371..8953edfc79 100644 --- a/tests/regression/46-apron2/dune +++ b/tests/regression/46-apron2/dune @@ -10,4 +10,6 @@ (action (chdir ../../.. (run %{update_suite} group apron2)))) (cram + (alias runaprontest) + (enabled_if %{lib-available:apron}) (deps (glob_files *.c))) From a21a2d4676bf8db1e309b8d2cd3488f7b3e29840 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 10 Jun 2024 18:10:32 +0200 Subject: [PATCH 130/160] Rename tests --- tests/regression/46-apron2/{88-wtf2.c => 92-wtf2.c} | 0 tests/regression/46-apron2/{87-wtf.c => 93-wtf.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/46-apron2/{88-wtf2.c => 92-wtf2.c} (100%) rename tests/regression/46-apron2/{87-wtf.c => 93-wtf.c} (100%) diff --git a/tests/regression/46-apron2/88-wtf2.c b/tests/regression/46-apron2/92-wtf2.c similarity index 100% rename from tests/regression/46-apron2/88-wtf2.c rename to tests/regression/46-apron2/92-wtf2.c diff --git a/tests/regression/46-apron2/87-wtf.c b/tests/regression/46-apron2/93-wtf.c similarity index 100% rename from tests/regression/46-apron2/87-wtf.c rename to tests/regression/46-apron2/93-wtf.c From 48cd36562049bc52ce2eed5b68b53752f3390463 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 May 2024 17:43:20 +0300 Subject: [PATCH 131/160] Add Apron mutex-meet abortUnless non-termination test case (PR #1464) --- .../05-apron-mutex-meet-abortUnless.c | 39 +++++++++++++++++++ tests/regression/62-abortUnless/dune | 14 +++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/regression/62-abortUnless/05-apron-mutex-meet-abortUnless.c create mode 100644 tests/regression/62-abortUnless/dune diff --git a/tests/regression/62-abortUnless/05-apron-mutex-meet-abortUnless.c b/tests/regression/62-abortUnless/05-apron-mutex-meet-abortUnless.c new file mode 100644 index 0000000000..e00c665056 --- /dev/null +++ b/tests/regression/62-abortUnless/05-apron-mutex-meet-abortUnless.c @@ -0,0 +1,39 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] abortUnless --set ana.path_sens[+] threadflag +// Minimized SV-COMP version of our regression test +// NOTIMEOUT +#include +#include + +void reach_error() { + assert(0); // NOWARN +} +void __VERIFIER_assert(int cond) { // NOWARN + if(!(cond)) { ERROR: {reach_error();abort();} } +} + +int glob1 = 5; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +void *t_fun(void *arg) { + int t; + pthread_mutex_lock(&mutex1); + t = glob1; + __VERIFIER_assert(t == 5); // NOWARN + glob1 = -10; + __VERIFIER_assert(glob1 == -10); // NOWARN + glob1 = t; + pthread_mutex_unlock(&mutex1); + return ((void *)0); +} +int main(void) { + pthread_t id; + __VERIFIER_assert(glob1 == 5); // NOWARN + pthread_create(&id, ((void *)0), t_fun, ((void *)0)); + pthread_mutex_lock(&mutex1); + glob1++; + __VERIFIER_assert(glob1 == 6); // NOWARN + glob1--; + pthread_mutex_unlock(&mutex1); + pthread_join (id, ((void *)0)); + return 0; +} diff --git a/tests/regression/62-abortUnless/dune b/tests/regression/62-abortUnless/dune new file mode 100644 index 0000000000..d0f8f1af41 --- /dev/null +++ b/tests/regression/62-abortUnless/dune @@ -0,0 +1,14 @@ +(rule + (aliases runtest runaprontest) + (enabled_if %{lib-available:apron}) + (deps + (package goblint) + ../../../goblint ; update_suite calls local goblint + (:update_suite ../../../scripts/update_suite.rb) + (glob_files ??-*.c)) + (locks /update_suite) + (action + (chdir ../../.. + (progn + (run %{update_suite} apron-mutex-meet-abortUnless -q))))) + From 7c22d485fcf7310277fd7d70b63afcd7b5a5175d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 May 2024 17:56:08 +0300 Subject: [PATCH 132/160] Make relational mutex-meet join sync more precise when threadflag is path-sensitive (PR #1464) --- src/analyses/apron/relationPriv.apron.ml | 2 +- src/analyses/commonPriv.ml | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 47ae75d107..32657e9dbd 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -638,7 +638,7 @@ struct st end | `Join -> - if (ask.f (Q.MustBeSingleThreaded {since_start = true})) then + if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded {since_start = true})) then st else let rel = st.rel in diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index e7964d200c..0a386cf583 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -50,6 +50,19 @@ struct if not threadflag_path_sens then failwith "The activated privatization requires the 'threadflag' analysis to be path sensitive if it is enabled (it is currently enabled, but not path sensitive)"; () end + + let branched_thread_creation () = + let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in + if threadflag_active then + let threadflag_path_sens = List.mem "threadflag" (GobConfig.get_string_list "ana.path_sens") in + if not threadflag_path_sens then + true + else + false + else + true + + let no_branched_thread_creation () = not (branched_thread_creation ()) end module Protection = From b78173ce3cc51ea56664ea22315d0baafb6f8eaa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 May 2024 18:01:53 +0300 Subject: [PATCH 133/160] Use branched thread creation check for other privatizations --- src/analyses/apron/relationPriv.apron.ml | 6 +++--- src/analyses/basePriv.ml | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 32657e9dbd..13135bba3f 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -98,7 +98,7 @@ struct let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason = match reason with | `Join -> - if ask.f (Q.MustBeSingleThreaded {since_start = true}) then + if ConfCheck.no_branched_thread_creation () || ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else (* must be like enter_multithreaded *) @@ -347,7 +347,7 @@ struct st end | `Join -> - if (ask.f (Q.MustBeSingleThreaded { since_start= true })) then + if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded { since_start= true })) then st else (* must be like enter_multithreaded *) @@ -1195,7 +1195,7 @@ struct match reason with | `Return -> st (* TODO: implement? *) | `Join -> - if (ask.f (Q.MustBeSingleThreaded {since_start = true})) then + if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded {since_start = true})) then st else let rel = st.rel in diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7e5bd9991f..cb07f63bf1 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -307,7 +307,7 @@ struct let sync ask getg sideg (st: BaseComponents (D).t) reason = match reason with - | `Join -> (* required for branched thread creation *) + | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) (* TODO: this makes mutex-oplus less precise in 28-race_reach/10-ptrmunge_racefree and 28-race_reach/trylock2_racefree, why? *) @@ -318,6 +318,7 @@ struct sideg (V.global x) (CPA.singleton x v) ) st.cpa; st + | `Join | `Return | `Normal | `Init @@ -404,7 +405,7 @@ struct let sync ask getg sideg (st: BaseComponents (D).t) reason = match reason with - | `Join -> (* required for branched thread creation *) + | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) @@ -421,6 +422,7 @@ struct ) st.cpa st.cpa in {st with cpa = cpa'} + | `Join | `Return | `Normal | `Init @@ -772,7 +774,7 @@ struct let sync ask getg sideg (st: BaseComponents (D).t) reason = let sideg = Wrapper.sideg ask sideg in match reason with - | `Join -> (* required for branched thread creation *) + | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x && is_unprotected ask x then ( sideg (V.unprotected x) v; @@ -782,6 +784,7 @@ struct else st ) st.cpa st + | `Join | `Return | `Normal | `Init From 98ff8f6dd377a6d8667a910e218b650ef0d4b6cc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 21 May 2024 11:09:19 +0300 Subject: [PATCH 134/160] Remove ConfCheck.no_branched_thread_creation --- src/analyses/apron/relationPriv.apron.ml | 20 ++++++++++++-------- src/analyses/commonPriv.ml | 2 -- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 13135bba3f..851e26564a 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -97,8 +97,8 @@ struct let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason = match reason with - | `Join -> - if ConfCheck.no_branched_thread_creation () || ask.f (Q.MustBeSingleThreaded {since_start = true}) then + | `Join when ConfCheck.branched_thread_creation () -> + if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else (* must be like enter_multithreaded *) @@ -110,6 +110,7 @@ struct ) in {st with rel = rel_local} + | `Join | `Normal | `Init | `Thread @@ -346,8 +347,8 @@ struct | _ -> st end - | `Join -> - if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded { since_start= true })) then + | `Join when ConfCheck.branched_thread_creation () -> + if ask.f (Q.MustBeSingleThreaded { since_start= true }) then st else (* must be like enter_multithreaded *) @@ -375,6 +376,7 @@ struct let rel_local' = RD.meet rel_local (getg ()) in {st with rel = rel_local'} *) st + | `Join | `Normal | `Init | `Thread -> @@ -637,8 +639,8 @@ struct | _ -> st end - | `Join -> - if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded {since_start = true})) then + | `Join when ConfCheck.branched_thread_creation () -> + if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else let rel = st.rel in @@ -660,6 +662,7 @@ struct ) in {st with rel = rel_local} + | `Join | `Normal | `Init | `Thread -> @@ -1194,8 +1197,8 @@ struct let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = match reason with | `Return -> st (* TODO: implement? *) - | `Join -> - if ConfCheck.no_branched_thread_creation () || (ask.f (Q.MustBeSingleThreaded {since_start = true})) then + | `Join when ConfCheck.branched_thread_creation () -> + if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else let rel = st.rel in @@ -1209,6 +1212,7 @@ struct ) in {st with rel = rel_local} + | `Join | `Normal | `Init | `Thread -> diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 0a386cf583..3627e2bd57 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,8 +61,6 @@ struct false else true - - let no_branched_thread_creation () = not (branched_thread_creation ()) end module Protection = From eb73537770d4206f7abf2d93bcc3768ec723e9bc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 21 May 2024 11:11:57 +0300 Subject: [PATCH 135/160] Simplify and document ConfCheck.branched_thread_creation --- src/analyses/commonPriv.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 3627e2bd57..fb5d0db142 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -51,14 +51,12 @@ struct () end + (** Whether branched thread creation needs to be handled by [sync `Join] of privatization. *) let branched_thread_creation () = let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in if threadflag_active then let threadflag_path_sens = List.mem "threadflag" (GobConfig.get_string_list "ana.path_sens") in - if not threadflag_path_sens then - true - else - false + not threadflag_path_sens else true end From da31d5c23c076def7bd0c6e1eaa623c8804166f1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 12 Jun 2024 11:46:33 +0200 Subject: [PATCH 136/160] Introduce sync reason "SyncJoin" Helps avoid "sync" calls for function start nodes when threadflag is context-sensitive. Previsouly, it was only avoided when the flag was path-sensitive --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/apron/relationPriv.apron.ml | 80 ++++++++++++------- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 43 ++++++++-- src/analyses/basePriv.mli | 2 +- src/analyses/commonPriv.ml | 9 +++ src/framework/analyses.ml | 2 +- src/framework/constraints.ml | 5 +- .../46-apron2/94-threadflag-ctx-sens.c | 27 +++++++ 9 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 tests/regression/46-apron2/94-threadflag-ctx-sens.c diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 91e94543f7..c6b108c7dc 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -768,7 +768,7 @@ struct PCU.RH.replace results ctx.node new_value; end; WideningTokens.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ) let init marshal = diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 851e26564a..8f8fb733c5 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -36,7 +36,7 @@ module type S = val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `Return | `Init | `Thread] -> relation_components_t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> relation_components_t val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t @@ -96,8 +96,7 @@ struct { st with rel = rel_local } let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -110,7 +109,14 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread @@ -337,17 +343,8 @@ struct let thread_join ?(force=false) ask getg exp st = st let thread_return ask getg sideg tid st = st - let sync ask getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> (* required for thread return *) - (* TODO: implement? *) - begin match ThreadId.get_current ask with - | `Lifted x (* when CPA.mem x st.cpa *) -> - st - | _ -> - st - end - | `Join when ConfCheck.branched_thread_creation () -> + let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = + let branched_sync () = if ask.f (Q.MustBeSingleThreaded { since_start= true }) then st else @@ -376,7 +373,22 @@ struct let rel_local' = RD.meet rel_local (getg ()) in {st with rel = rel_local'} *) st + in + match reason with + | `Return -> (* required for thread return *) + (* TODO: implement? *) + begin match ThreadId.get_current ask with + | `Lifted x (* when CPA.mem x st.cpa *) -> + st + | _ -> + st + end + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> @@ -629,17 +641,8 @@ struct let thread_join ?(force=false) ask getg exp st = st let thread_return ask getg sideg tid st = st - let sync ask getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> (* required for thread return *) - (* TODO: implement? *) - begin match ThreadId.get_current ask with - | `Lifted x (* when CPA.mem x st.cpa *) -> - st - | _ -> - st - end - | `Join when ConfCheck.branched_thread_creation () -> + let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -662,7 +665,22 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Return -> (* required for thread return *) + (* TODO: implement? *) + begin match ThreadId.get_current ask with + | `Lifted x (* when CPA.mem x st.cpa *) -> + st + | _ -> + st + end + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> @@ -1195,9 +1213,7 @@ struct st let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> st (* TODO: implement? *) - | `Join when ConfCheck.branched_thread_creation () -> + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -1212,7 +1228,15 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Return -> st (* TODO: implement? *) + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8d2b8f3ba5..9d4e3be0d4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -449,7 +449,7 @@ struct else ctx.local - let sync ctx reason = sync' (reason :> [`Normal | `Join | `Return | `Init | `Thread]) ctx + let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ctx let publish_all ctx reason = ignore (sync' reason ctx) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index cb07f63bf1..2157b13c13 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -31,7 +31,7 @@ sig val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseComponents (D).t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseComponents (D).t val escape: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseComponents (D).t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> BaseComponents (D).t @@ -306,8 +306,8 @@ struct st let sync ask getg sideg (st: BaseComponents (D).t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) (* TODO: this makes mutex-oplus less precise in 28-race_reach/10-ptrmunge_racefree and 28-race_reach/trylock2_racefree, why? *) @@ -318,7 +318,14 @@ struct sideg (V.global x) (CPA.singleton x v) ) st.cpa; st + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -404,8 +411,8 @@ struct {st with cpa = cpa'} let sync ask getg sideg (st: BaseComponents (D).t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) @@ -422,7 +429,14 @@ struct ) st.cpa st.cpa in {st with cpa = cpa'} + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -772,9 +786,9 @@ struct ) st.cpa st let sync ask getg sideg (st: BaseComponents (D).t) reason = - let sideg = Wrapper.sideg ask sideg in - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) + let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x && is_unprotected ask x then ( sideg (V.unprotected x) v; @@ -784,7 +798,14 @@ struct else st ) st.cpa st + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -1034,6 +1055,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1089,6 +1111,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1160,6 +1183,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1318,6 +1342,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1496,6 +1521,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1678,6 +1704,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index e176a450fa..87b17e5ad0 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -20,7 +20,7 @@ sig val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t - val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t + val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t val escape: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseDomain.BaseComponents (D).t val enter_multithreaded: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index fb5d0db142..8290f0e0a2 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -59,6 +59,15 @@ struct not threadflag_path_sens else true + + (** Whether branched thread creation at start nodes of procedures needs to be handled by [sync `JoinCall] of privatization. *) + let branched_thread_creation_at_call () = + let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in + if threadflag_active then + let threadflag_ctx_sens = not (List.mem "threadflag" @@ GobConfig.get_string_list "ana.ctx_insens") in + not threadflag_ctx_sens + else + true end module Protection = diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index c8464f6e84..844a387b2b 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -208,7 +208,7 @@ sig val context : fundec -> D.t -> C.t - val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `Return] -> D.t + val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall | `Return] -> D.t val query : (D.t, G.t, C.t, V.t) ctx -> 'a Queries.t -> 'a Queries.result (** A transfer function which handles the assignment of a rval to a lval, i.e., diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index aba2ad695f..478864d62e 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -528,9 +528,10 @@ struct let sync ctx = match ctx.prev_node, Cfg.prev ctx.prev_node with - | _, _ :: _ :: _ (* Join in CFG. *) - | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + | _, _ :: _ :: _ -> (* Join in CFG. *) S.sync ctx `Join + | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + S.sync ctx `JoinCall | _, _ -> S.sync ctx `Normal let side_context sideg f c = diff --git a/tests/regression/46-apron2/94-threadflag-ctx-sens.c b/tests/regression/46-apron2/94-threadflag-ctx-sens.c new file mode 100644 index 0000000000..10fc5f8d1e --- /dev/null +++ b/tests/regression/46-apron2/94-threadflag-ctx-sens.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet +// NOTIMEOUT +#include +#include + +int g; +pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + +void fn() { + // Just do nothing + return; +} + +void *t_fun(void *arg) { + pthread_mutex_lock(&m); + g = g+1; + fn(); + pthread_mutex_unlock(&m); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + return 0; +} From 2c92cee9a55ab4939362a4b8872cd77f95375334 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 25 Jun 2024 16:53:37 +0200 Subject: [PATCH 137/160] Employ same invalidation as for base --- src/analyses/apron/relationAnalysis.apron.ml | 59 ++++++++++---------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index c6b108c7dc..ae1c47cf51 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -509,6 +509,35 @@ struct if RD.is_bot_env res then raise Deadcode; {st with rel = res} + let special_unknown_invalidate ctx f args = + (* No warning here, base already prodcues the appropriate warnings *) + let desc = LibraryFunctions.find f in + let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in + let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in + 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? *) + | _ -> acc + ) deep_addrs + ) + else + deep_addrs + in + let lvallist e = + match ctx.ask (Queries.MayPointTo e) with + | ad when Queries.AD.is_top ad -> [] + | ad -> + Queries.AD.to_mval ad + |> List.map ValueDomain.Addr.Mval.to_cil + in + let st' = forget_reachable ctx ctx.local deep_addrs in + let shallow_lvals = List.concat_map lvallist shallow_addrs in + List.fold_left (invalidate_one (Analyses.ask_of_ctx ctx) ctx) st' shallow_lvals + let special ctx r f args = let ask = Analyses.ask_of_ctx ctx in let st = ctx.local in @@ -544,31 +573,7 @@ struct assert_fn {ctx with local = st} (BinOp (Ge, Lval lv, zero, intType)) true | None -> st) | _, _ -> - let lvallist e = - match ask.f (Queries.MayPointTo e) with - | ad when Queries.AD.is_top ad -> [] - | ad -> - Queries.AD.to_mval ad - |> List.map ValueDomain.Addr.Mval.to_cil - in - let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in - let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args 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? *) - | _ -> acc - ) deep_addrs - ) - else - deep_addrs - in - let st' = forget_reachable ctx st deep_addrs in - let shallow_lvals = List.concat_map lvallist shallow_addrs in - let st' = List.fold_left (invalidate_one ask ctx) st' shallow_lvals in + let st' = special_unknown_invalidate ctx f args in (* invalidate lval if present *) match r with | Some lv -> invalidate_one ask ctx st' lv @@ -670,9 +675,7 @@ struct let new_rel = make_callee_rel ~thread:true ctx fd args in [{st' with rel = new_rel}] | exception Not_found -> - (* Unknown functions *) - (* TODO: do something like base? *) - failwith "relation.threadenter: unknown function" + [special_unknown_invalidate ctx f args] let threadspawn ctx ~multiple lval f args fctx = ctx.local From 76c27bfcfbd965f083311745a66f2becd85b6c03 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 27 Jun 2024 10:28:26 +0200 Subject: [PATCH 138/160] Hotfix --- src/cdomains/apron/apronDomain.apron.ml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d0ef268ca6..9f0572a32e 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -443,7 +443,18 @@ struct let show (x:t) = Format.asprintf "%a (env: %a)" A.print x (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x) - let pretty () (x:t) = text (show x) + + (* HACK: Hotfix for https://github.com/goblint/cil/issues/169, remove once that is resolved and dependencies updated *) + let custom_text (s:string) = + let lines = String.split_on_char '\n' s in + let rec doit = function + | [] -> nil + | [x1] -> text x1 + | x1::xs -> (text x1) ++ line ++ doit xs + in + doit lines + + let pretty () (x:t) = custom_text (show x) let equal x y = Environment.equal (A.env x) (A.env y) && A.is_eq Man.mgr x y From 547491b8fbc624626969df0fa446df22202af66d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 2 Jul 2024 11:18:10 +0200 Subject: [PATCH 139/160] Undo svcomp stuff leaking into otherwise reasonable things --- src/transform/evalAssert.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index eab06222ef..034dd4d257 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -27,7 +27,7 @@ module EvalAssert = struct let surroundByAtomic = true (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass = makeVarinfo true "__VERIFIER_assert" (TVoid []) + let ass = makeVarinfo true "assert" (TVoid []) let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) From 837837c0d0c2540a125adfe10b10ce3a83af32d9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sat, 6 Jul 2024 17:02:44 +0200 Subject: [PATCH 140/160] Use same invalidation strategy as base (References #1535) --- src/analyses/apron/relationAnalysis.apron.ml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ae1c47cf51..b471a231ce 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -465,10 +465,9 @@ struct | None -> None | Some st -> let ad = ask.f (Queries.ReachableFrom e) in - if Queries.AD.is_top ad then - None - else - Some (Queries.AD.join ad st) + (* See https://github.com/goblint/analyzer/issues/1535 *) + let ad = Queries.AD.remove UnknownPtr ad in + Some (Queries.AD.join ad st) in List.fold_right reachable es (Some (Queries.AD.empty ())) @@ -510,7 +509,7 @@ struct {st with rel = res} let special_unknown_invalidate ctx f args = - (* No warning here, base already prodcues the appropriate warnings *) + (* No warning here, base already produces the appropriate warnings *) let desc = LibraryFunctions.find f in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in From 73e336e208b38b54b63820cbda255f203cdec239 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 7 Jul 2024 19:20:05 +0200 Subject: [PATCH 141/160] Consider all globals in scope (workaround for #1538) --- src/cdomain/value/domains/invariantCil.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/domains/invariantCil.ml b/src/cdomain/value/domains/invariantCil.ml index 813ec25818..c037d70fc2 100644 --- a/src/cdomain/value/domains/invariantCil.ml +++ b/src/cdomain/value/domains/invariantCil.ml @@ -29,7 +29,7 @@ let exp_deep_unroll_types = let var_is_in_scope scope vi = match Cilfacade.find_scope_fundec vi with - | None -> vi.vstorage <> Static (* CIL pulls static locals into globals, but they aren't syntactically in global scope *) + | None -> true (* vi.vstorage <> Static CIL pulls static locals into globals, but they aren't syntactically in global scope *) | Some fd -> if CilType.Fundec.equal fd scope then GobConfig.get_bool "witness.invariant.all-locals" || (not @@ hasAttribute "goblint_cil_nested" vi.vattr) From f8da59eec62465f938702c0f9f12ca003f14d771 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 8 Jul 2024 10:57:26 +0200 Subject: [PATCH 142/160] traces-rel.json: Limit YAML entry type to location_invariant --- conf/traces-rel.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/traces-rel.json b/conf/traces-rel.json index 405fb593cf..eaf6a3f232 100644 --- a/conf/traces-rel.json +++ b/conf/traces-rel.json @@ -80,6 +80,9 @@ "other": false, "loop-head": false, "after-lock": true + }, + "yaml" : { + "entry-types": ["location_invariant"] } }, "pre": { From 1a4d6d16e66c32ed8e6b43551bd73295342f444e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 9 Jul 2024 11:05:32 +0200 Subject: [PATCH 143/160] Skip check for speedup --- src/witness/yamlWitness.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 42254f30de..aab006c610 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -605,7 +605,7 @@ struct let result = LvarS.fold (fun ((n, _) as lvar) (acc: VR.t) -> let fundec = Node.find_fundec n in - let result: VR.result = match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + let result: VR.result = match InvariantParser.parse_cil ~check:false inv_parser ~fundec ~loc inv_cabs with | Ok inv_exp -> let x = ask_local lvar (Queries.EvalInt inv_exp) in if Queries.ID.is_bot x || Queries.ID.is_bot_ikind x then (* dead code *) From a1817de94ca3b2e6f34668a704e95a23a383f3b9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 9 Jul 2024 15:07:08 +0200 Subject: [PATCH 144/160] Non verbose --- src/util/precCompare.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 2386367967..482b41e65b 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -118,7 +118,7 @@ struct RH.filter_map (fun k v -> if Hashtbl.mem disregard1 (Key.to_location k) || Hashtbl.mem disregard2 (Key.to_location k) then None else Some v) lvh2 | _ -> lvh2 in - let (c, d) = CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 in + let (c, d) = CompareDump.compare ~verbose:false ~name1 lvh1 ~name2 lvh2 in comparisons := (name1, name2, c, d) :: !comparisons; (c, d) From 970a99913fd746181ef1f2b82425ce7eafead2ca Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 9 Jul 2024 15:10:48 +0200 Subject: [PATCH 145/160] Revert "Non verbose" This reverts commit a1817de94ca3b2e6f34668a704e95a23a383f3b9. --- src/util/precCompare.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/precCompare.ml b/src/util/precCompare.ml index 482b41e65b..2386367967 100644 --- a/src/util/precCompare.ml +++ b/src/util/precCompare.ml @@ -118,7 +118,7 @@ struct RH.filter_map (fun k v -> if Hashtbl.mem disregard1 (Key.to_location k) || Hashtbl.mem disregard2 (Key.to_location k) then None else Some v) lvh2 | _ -> lvh2 in - let (c, d) = CompareDump.compare ~verbose:false ~name1 lvh1 ~name2 lvh2 in + let (c, d) = CompareDump.compare ~verbose:true ~name1 lvh1 ~name2 lvh2 in comparisons := (name1, name2, c, d) :: !comparisons; (c, d) From 3a30b0cb9bf532920fbf211408a439fc39717d87 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 12 Jul 2024 16:58:31 +0200 Subject: [PATCH 146/160] Cehck overflows when generating witnesses --- src/cdomains/apron/sharedFunctions.apron.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index cc1fb5026a..42693c55be 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -263,7 +263,7 @@ struct let expr = ref (fst @@ coeff_to_const false (Linexpr1.get_cst linexpr1)) in let append_summand (c:Coeff.union_5) v = match V.to_cil_varinfo v with - | Some vinfo -> + | Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) -> (* TODO: What to do with variables that have a type that cannot be stored into ILongLong to avoid overflows? *) let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in let coeff, flip = coeff_to_const true c in @@ -273,6 +273,7 @@ struct else expr := BinOp(PlusA,!expr,prod,longlong) | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %s" (Var.to_string v); raise Unsupported_Linexpr1 + | _ -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var in overflow preserving manner: %s" (Var.to_string v); raise Unsupported_Linexpr1 in Linexpr1.iter append_summand linexpr1; !expr From c35fd8620353b9601a3bae7a048953dc54324d55 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 12 Jul 2024 17:02:16 +0200 Subject: [PATCH 147/160] Mark TODO completed --- src/cdomains/apron/sharedFunctions.apron.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 42693c55be..49d936ddf7 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -264,7 +264,6 @@ struct let append_summand (c:Coeff.union_5) v = match V.to_cil_varinfo v with | Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) -> - (* TODO: What to do with variables that have a type that cannot be stored into ILongLong to avoid overflows? *) let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in let coeff, flip = coeff_to_const true c in let prod = BinOp(Mult, coeff, var, longlong) in From 3eb80171568a4f9333ca63a86e7545c6ba4e661b Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Jul 2024 18:25:58 +0200 Subject: [PATCH 148/160] Add witness cram test --- .../46-apron2/95-witness-mm-escape.c | 19 + .../46-apron2/95-witness-mm-escape.t | 25 + .../46-apron2/95-witness-mm-escape.yml | 449 ++++++++++++++++++ tests/regression/46-apron2/dune | 2 +- 4 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 tests/regression/46-apron2/95-witness-mm-escape.c create mode 100644 tests/regression/46-apron2/95-witness-mm-escape.t create mode 100644 tests/regression/46-apron2/95-witness-mm-escape.yml diff --git a/tests/regression/46-apron2/95-witness-mm-escape.c b/tests/regression/46-apron2/95-witness-mm-escape.c new file mode 100644 index 0000000000..e6f2d5d429 --- /dev/null +++ b/tests/regression/46-apron2/95-witness-mm-escape.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 95-witness-mm-escape.yml +#include +#include + +int *b; +pthread_mutex_t e; + +void main() { + + int g = 8; + int a; + if(a) { + g = 10; + } + + b = &g; + + pthread_mutex_lock(&e); +} diff --git a/tests/regression/46-apron2/95-witness-mm-escape.t b/tests/regression/46-apron2/95-witness-mm-escape.t new file mode 100644 index 0000000000..c5cee8cfdb --- /dev/null +++ b/tests/regression/46-apron2/95-witness-mm-escape.t @@ -0,0 +1,25 @@ + $ goblint --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 95-witness-mm-escape.yml 95-witness-mm-escape.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Success][Witness] invariant confirmed: 0 <= g (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: 0 <= *b (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: g <= 127 (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: *b <= 127 (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: (2147483638LL + (long long )a) + (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: (2147483637LL - (long long )a) + (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: (2147483658LL + (long long )a) - (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: (2147483657LL - (long long )a) - (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: b == & g (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: g != 0 (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: *b != 0 (95-witness-mm-escape.c:19:1) + [Info][Witness] witness validation summary: + confirmed: 22 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 22 diff --git a/tests/regression/46-apron2/95-witness-mm-escape.yml b/tests/regression/46-apron2/95-witness-mm-escape.yml new file mode 100644 index 0000000000..bf99e03f97 --- /dev/null +++ b/tests/regression/46-apron2/95-witness-mm-escape.yml @@ -0,0 +1,449 @@ +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: bd605de4-9e24-4df2-a8b6-7a20973f08c3 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: 0 <= g + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: d52450ae-a439-4c7e-9cd7-88a4d7b2729d + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: 0 <= *b + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: 934e24ac-ff4e-4ce8-b10e-28c96d1d9a85 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: g <= 127 + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: b67ec3d2-1506-4a5a-a019-b959fa02c710 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: '*b <= 127' + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: 799dbde2-5854-4786-b784-22941410dd4c + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: (2147483638LL + (long long )a) + (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: fbf9961a-853f-4bc0-be9b-a12f6d49ab20 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: (2147483637LL - (long long )a) + (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: c7d1d801-2b2a-4f34-a0ed-29e496ae2bbf + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: (2147483658LL + (long long )a) - (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: f626d0f0-9ab2-4e7e-8568-194a3ba0f671 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: (2147483657LL - (long long )a) - (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: 70ebfa4c-8b42-439e-bb6f-f8b87b8bebe9 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: b == & g + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: 1b25d102-ebc5-4dde-854d-ac5525aa4fed + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: g != 0 + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: bc5e8286-3029-4002-b87b-1288f32e44c4 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + location_invariant: + string: '*b != 0' + type: assertion + format: C +- entry_type: invariant_set + metadata: + format_version: "0.1" + uuid: 8cad3de5-98bb-4400-833c-bae232e13530 + creation_time: 2024-07-16T16:10:49Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + data_model: LP64 + language: C + content: + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: 0 <= g + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: 0 <= *b + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: g <= 127 + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: '*b <= 127' + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: (2147483638LL + (long long )a) + (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: (2147483637LL - (long long )a) + (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: (2147483658LL + (long long )a) - (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: (2147483657LL - (long long )a) - (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: b == & g + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: g != 0 + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + line: 19 + column: 1 + function: main + value: '*b != 0' + format: c_expression diff --git a/tests/regression/46-apron2/dune b/tests/regression/46-apron2/dune index 8953edfc79..b957911e36 100644 --- a/tests/regression/46-apron2/dune +++ b/tests/regression/46-apron2/dune @@ -12,4 +12,4 @@ (cram (alias runaprontest) (enabled_if %{lib-available:apron}) - (deps (glob_files *.c))) + (deps (glob_files *.c) (glob_files *.yml))) From 13ee48abb36f1cdd144312fb46f8774336268e9d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Jul 2024 18:26:22 +0200 Subject: [PATCH 149/160] Fix `read_global` --- src/analyses/apron/relationAnalysis.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index b471a231ce..1c2262e69a 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -56,7 +56,8 @@ struct Priv.read_global ask getg st g x else ( let rel = st.rel in - let g_var = RV.global g in + (* If it has escaped and we have never been multi-threaded, we can still refer to the local *) + let g_var = if g.vglob then RV.global g else RV.local g in let x_var = RV.local x in let rel' = RD.add_vars rel [g_var] in let rel' = RD.assign_var rel' x_var g_var in @@ -87,7 +88,7 @@ struct let e' = visitCilExpr visitor e in let rel = RD.add_vars st.rel (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* add temporary g#in-s *) let rel' = VH.fold (fun v v_in rel -> - if M.tracing then M.trace "relation" "read_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; + if M.tracing then M.trace "gurki" "read_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; read_global ask getg {st with rel} v v_in (* g#in = g; *) ) v_ins rel in From 5c0338b03185e8e2e711b201889d006b2b10c072 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Jul 2024 18:27:27 +0200 Subject: [PATCH 150/160] Spurious tracing --- src/analyses/apron/relationAnalysis.apron.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 1c2262e69a..704985fb6b 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -88,7 +88,6 @@ struct let e' = visitCilExpr visitor e in let rel = RD.add_vars st.rel (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* add temporary g#in-s *) let rel' = VH.fold (fun v v_in rel -> - if M.tracing then M.trace "gurki" "read_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; read_global ask getg {st with rel} v v_in (* g#in = g; *) ) v_ins rel in From f1bd3dd4e57a74fb5fae64f60954d9cc86ffb26c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Jul 2024 18:32:45 +0200 Subject: [PATCH 151/160] Fix tracing --- src/analyses/apron/relationAnalysis.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 704985fb6b..444b3a299c 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -88,6 +88,7 @@ struct let e' = visitCilExpr visitor e in let rel = RD.add_vars st.rel (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* add temporary g#in-s *) let rel' = VH.fold (fun v v_in rel -> + if M.tracing then M.trace "relation" "read_global %a %a" CilType.Varinfo.pretty v CilType.Varinfo.pretty v_in; read_global ask getg {st with rel} v v_in (* g#in = g; *) ) v_ins rel in From 2fcb551dfa06ccfbabe7254dc20dbb1a3566e636 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 16 Jul 2024 18:39:16 +0200 Subject: [PATCH 152/160] Update cram test --- .../46-apron2/95-witness-mm-escape.t | 8 +- .../46-apron2/95-witness-mm-escape.yml | 312 ++++++++++++++---- 2 files changed, 248 insertions(+), 72 deletions(-) diff --git a/tests/regression/46-apron2/95-witness-mm-escape.t b/tests/regression/46-apron2/95-witness-mm-escape.t index c5cee8cfdb..047cb15718 100644 --- a/tests/regression/46-apron2/95-witness-mm-escape.t +++ b/tests/regression/46-apron2/95-witness-mm-escape.t @@ -7,19 +7,23 @@ [Success][Witness] invariant confirmed: 0 <= *b (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: g <= 127 (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: *b <= 127 (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: -8LL + (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: 2147483648LL + (long long )a >= 0LL (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: (2147483638LL + (long long )a) + (long long )g >= 0LL (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: (2147483637LL - (long long )a) + (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: 10LL - (long long )g >= 0LL (95-witness-mm-escape.c:19:1) + [Success][Witness] invariant confirmed: 2147483647LL - (long long )a >= 0LL (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: (2147483658LL + (long long )a) - (long long )g >= 0LL (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: (2147483657LL - (long long )a) - (long long )g >= 0LL (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: b == & g (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: g != 0 (95-witness-mm-escape.c:19:1) [Success][Witness] invariant confirmed: *b != 0 (95-witness-mm-escape.c:19:1) [Info][Witness] witness validation summary: - confirmed: 22 + confirmed: 30 unconfirmed: 0 refuted: 0 error: 0 unchecked: 0 unsupported: 0 disabled: 0 - total validation entries: 22 + total validation entries: 30 diff --git a/tests/regression/46-apron2/95-witness-mm-escape.yml b/tests/regression/46-apron2/95-witness-mm-escape.yml index bf99e03f97..66715bd382 100644 --- a/tests/regression/46-apron2/95-witness-mm-escape.yml +++ b/tests/regression/46-apron2/95-witness-mm-escape.yml @@ -1,25 +1,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: bd605de4-9e24-4df2-a8b6-7a20973f08c3 - creation_time: 2024-07-16T16:10:49Z + uuid: f88caf27-fbfe-4ce8-a15d-463646cca899 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -30,25 +31,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: d52450ae-a439-4c7e-9cd7-88a4d7b2729d - creation_time: 2024-07-16T16:10:49Z + uuid: 7949534c-6edd-4412-9778-fc58745e7cc5 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -59,25 +61,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: 934e24ac-ff4e-4ce8-b10e-28c96d1d9a85 - creation_time: 2024-07-16T16:10:49Z + uuid: ffe5f8b3-569c-4d42-8e9b-21c48312e6ce + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -88,25 +91,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: b67ec3d2-1506-4a5a-a019-b959fa02c710 - creation_time: 2024-07-16T16:10:49Z + uuid: cad5c137-4373-4431-b8c8-c5d1e537a716 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -117,25 +121,86 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: 799dbde2-5854-4786-b784-22941410dd4c - creation_time: 2024-07-16T16:10:49Z + uuid: aa21493b-1338-4004-90b7-046b9e826169 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + location_invariant: + string: -8LL + (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: b640fdf6-abc8-45b9-ad4f-2695e0d66a3d + creation_time: 2024-07-16T16:36:39Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + location_invariant: + string: 2147483648LL + (long long )a >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: ecc017f2-d045-45c7-a795-d3d16088368d + creation_time: 2024-07-16T16:36:39Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -146,25 +211,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: fbf9961a-853f-4bc0-be9b-a12f6d49ab20 - creation_time: 2024-07-16T16:10:49Z + uuid: 52ace953-715d-4d44-9c44-5b40c1124593 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -175,25 +241,86 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: c7d1d801-2b2a-4f34-a0ed-29e496ae2bbf - creation_time: 2024-07-16T16:10:49Z + uuid: 661e7eee-b5c0-4de3-89ed-369f6978ba28 + creation_time: 2024-07-16T16:36:39Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + location_invariant: + string: 10LL - (long long )g >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: 7878f84e-a951-4247-a196-97f9195cf2fb + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + location_invariant: + string: 2147483647LL - (long long )a >= 0LL + type: assertion + format: C +- entry_type: location_invariant + metadata: + format_version: "0.1" + uuid: e80582ed-4527-4773-97fd-08631c673c21 + creation_time: 2024-07-16T16:36:39Z + producer: + name: Goblint + version: heads/check_overflow_convert-0-gc35fd8620-dirty + command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' + ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' + ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + task: + input_files: + - 95-witness-mm-escape.c + input_file_hashes: + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + data_model: LP64 + language: C + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -204,25 +331,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: f626d0f0-9ab2-4e7e-8568-194a3ba0f671 - creation_time: 2024-07-16T16:10:49Z + uuid: 49f62de7-3bfc-45ee-8709-0ff295f23b7a + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -233,25 +361,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: 70ebfa4c-8b42-439e-bb6f-f8b87b8bebe9 - creation_time: 2024-07-16T16:10:49Z + uuid: cb86dd3a-2ff5-420b-8d49-42240a324a26 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -262,25 +391,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: 1b25d102-ebc5-4dde-854d-ac5525aa4fed - creation_time: 2024-07-16T16:10:49Z + uuid: 2573f907-0386-4098-9b0c-7f1ec86d3f90 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -291,25 +421,26 @@ - entry_type: location_invariant metadata: format_version: "0.1" - uuid: bc5e8286-3029-4002-b87b-1288f32e44c4 - creation_time: 2024-07-16T16:10:49Z + uuid: 15693293-61f1-431b-867e-86add36e4d80 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -320,20 +451,21 @@ - entry_type: invariant_set metadata: format_version: "0.1" - uuid: 8cad3de5-98bb-4400-833c-bae232e13530 - creation_time: 2024-07-16T16:10:49Z + uuid: 5f4a70a3-8b30-4b5a-a260-56bb341a6283 + creation_time: 2024-07-16T16:36:39Z producer: name: Goblint version: heads/check_overflow_convert-0-gc35fd8620-dirty command_line: '''../../../goblint'' ''95-witness-mm-escape.c'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]'' ''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid-cluster12'' ''--enable'' ''witness.yaml.enabled'' ''--disable'' - ''witness.invariant.other'' ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' + ''witness.invariant.other'' ''--enable'' ''ana.relation.invariant.one-var'' + ''--set'' ''witness.yaml.path'' ''95-witness-mm-escape.yml''' task: input_files: - 95-witness-mm-escape.c input_file_hashes: - 95-witness-mm-escape.c: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + 95-witness-mm-escape.c: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 data_model: LP64 language: C content: @@ -341,7 +473,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -351,7 +483,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -361,7 +493,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -371,7 +503,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -381,7 +513,27 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + value: -8LL + (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + value: 2147483648LL + (long long )a >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -391,7 +543,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -401,7 +553,27 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + value: 10LL - (long long )g >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 + line: 19 + column: 1 + function: main + value: 2147483647LL - (long long )a >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 95-witness-mm-escape.c + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -411,7 +583,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -421,7 +593,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -431,7 +603,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main @@ -441,7 +613,7 @@ type: location_invariant location: file_name: 95-witness-mm-escape.c - file_hash: 59ef053c97b76b763526674d2e5f5a4bb304200ec4b738e93dcf7b42738b8ebe + file_hash: bc9de79e9c6aebc20f4284c088f10093ed99a05b0758005a17a5f39a9cc1b7e8 line: 19 column: 1 function: main From 5fe42bd7f0cdf9bc477ccb2b8429d86dc81b3d7e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 24 Jul 2024 11:19:46 +0200 Subject: [PATCH 153/160] Apron: Do not produce invariants about stale locals that have escaped --- src/analyses/apron/relationAnalysis.apron.ml | 4 ++++ .../46-apron2/96-witness-mm-escape2.c | 22 +++++++++++++++++++ .../46-apron2/96-witness-mm-escape2.t | 18 +++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 tests/regression/46-apron2/96-witness-mm-escape2.c create mode 100644 tests/regression/46-apron2/96-witness-mm-escape2.t diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 444b3a299c..a892551798 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -603,6 +603,10 @@ struct | Some (Local v) -> if VH.mem v_ins_inv v then keep_global + else if ThreadEscape.has_escaped ask v then + (* Escaped local variables should be read in via their v#in# variables, this apron var may refer to stale values only *) + (* and is not a sound description of the C variable. *) + false else keep_local | _ -> false diff --git a/tests/regression/46-apron2/96-witness-mm-escape2.c b/tests/regression/46-apron2/96-witness-mm-escape2.c new file mode 100644 index 0000000000..2fa3530679 --- /dev/null +++ b/tests/regression/46-apron2/96-witness-mm-escape2.c @@ -0,0 +1,22 @@ +// PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 95-witness-mm-escape.yml +#include +int *b; +pthread_mutex_t e; + +void* other(void* arg) { + pthread_mutex_lock(&e); + *b = -100; + pthread_mutex_unlock(&e); + + return NULL; +} + +void main() { + pthread_t t; + pthread_create(&t, NULL, other, NULL); + int g = 8; + + b = &g; + + pthread_mutex_lock(&e); +} diff --git a/tests/regression/46-apron2/96-witness-mm-escape2.t b/tests/regression/46-apron2/96-witness-mm-escape2.t new file mode 100644 index 0000000000..9311af8306 --- /dev/null +++ b/tests/regression/46-apron2/96-witness-mm-escape2.t @@ -0,0 +1,18 @@ + $ goblint --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --enable witness.yaml.enabled --disable witness.invariant.other --disable witness.invariant.loop-head 96-witness-mm-escape2.c --set witness.yaml.path 96-witness-mm-escape2.yml + [Info][Witness] witness generation summary: + total generation entries: 5 + + $ goblint --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 96-witness-mm-escape2.yml 96-witness-mm-escape2.c + [Success][Witness] invariant confirmed: (unsigned long )arg == 0UL (96-witness-mm-escape2.c:8:5) + [Success][Witness] invariant confirmed: -128 <= g (96-witness-mm-escape2.c:22:1) + [Success][Witness] invariant confirmed: g <= 127 (96-witness-mm-escape2.c:22:1) + [Success][Witness] invariant confirmed: g != 0 (96-witness-mm-escape2.c:22:1) + [Info][Witness] witness validation summary: + confirmed: 8 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 8 From 88d9a3bca2b49c8b19ded4be5a376e57fc6e8a30 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 24 Jul 2024 13:42:51 +0200 Subject: [PATCH 154/160] 46/96: Use deterministic warn --- tests/regression/46-apron2/96-witness-mm-escape2.t | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/regression/46-apron2/96-witness-mm-escape2.t b/tests/regression/46-apron2/96-witness-mm-escape2.t index 9311af8306..e8e46edc06 100644 --- a/tests/regression/46-apron2/96-witness-mm-escape2.t +++ b/tests/regression/46-apron2/96-witness-mm-escape2.t @@ -1,12 +1,8 @@ - $ goblint --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --enable witness.yaml.enabled --disable witness.invariant.other --disable witness.invariant.loop-head 96-witness-mm-escape2.c --set witness.yaml.path 96-witness-mm-escape2.yml + $ goblint --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --enable witness.yaml.enabled --disable witness.invariant.other --disable witness.invariant.loop-head 96-witness-mm-escape2.c --set witness.yaml.path 96-witness-mm-escape2.yml [Info][Witness] witness generation summary: total generation entries: 5 - $ goblint --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 96-witness-mm-escape2.yml 96-witness-mm-escape2.c - [Success][Witness] invariant confirmed: (unsigned long )arg == 0UL (96-witness-mm-escape2.c:8:5) - [Success][Witness] invariant confirmed: -128 <= g (96-witness-mm-escape2.c:22:1) - [Success][Witness] invariant confirmed: g <= 127 (96-witness-mm-escape2.c:22:1) - [Success][Witness] invariant confirmed: g != 0 (96-witness-mm-escape2.c:22:1) + $ goblint --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 96-witness-mm-escape2.yml 96-witness-mm-escape2.c [Info][Witness] witness validation summary: confirmed: 8 unconfirmed: 0 @@ -16,3 +12,7 @@ unsupported: 0 disabled: 0 total validation entries: 8 + [Success][Witness] invariant confirmed: (unsigned long )arg == 0UL (96-witness-mm-escape2.c:8:5) + [Success][Witness] invariant confirmed: -128 <= g (96-witness-mm-escape2.c:22:1) + [Success][Witness] invariant confirmed: g != 0 (96-witness-mm-escape2.c:22:1) + [Success][Witness] invariant confirmed: g <= 127 (96-witness-mm-escape2.c:22:1) From d9eea8a2167a59cde0fc8b6f7a3ba2a3e970772f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 4 Aug 2024 14:02:53 +0200 Subject: [PATCH 155/160] Typo --- 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 9d4e3be0d4..7afe8861b0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2149,7 +2149,7 @@ struct end | _, _ when get_bool "sem.unknown_function.spawn" -> (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. - But here we consider all non-ThreadCrate functions also unknown, so old-style LibraryFunctions access + But here we consider all non-ThreadCreate functions also unknown, so old-style LibraryFunctions access definitions using `Write would still spawn because they are not truly unknown functions (missing from LibraryFunctions). Need this to not have memmove spawn in SV-COMP. *) let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in From e493bcaa614d66e1dccdcc0df2469ba5b5cb1f0f Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 4 Aug 2024 14:10:09 +0200 Subject: [PATCH 156/160] Relation: Call `enter_mulithreaded` also for unknown threads --- src/analyses/apron/relationAnalysis.apron.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index a892551798..f7aad3cdb5 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -667,14 +667,14 @@ struct let threadenter ctx ~multiple lval f args = let st = ctx.local in + (* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread. + Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published... + sync `Thread doesn't help us here, it's not specific to entering multithreaded mode. + EnterMultithreaded events only execute after threadenter and threadspawn. *) + if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx)) then + ignore (Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st); match Cilfacade.find_varinfo_fundec f with | fd -> - (* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread. - Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published... - sync `Thread doesn't help us here, it's not specific to entering multithreaded mode. - EnterMultithreaded events only execute after threadenter and threadspawn. *) - if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx)) then - ignore (Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st); let st' = Priv.threadenter (Analyses.ask_of_ctx ctx) ctx.global st in let new_rel = make_callee_rel ~thread:true ctx fd args in [{st' with rel = new_rel}] From 88c88b6a7cfec5d7b3718b88b7c4a2907133fdcc Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 4 Aug 2024 14:21:02 +0200 Subject: [PATCH 157/160] Add test for #1511 --- tests/regression/46-apron2/97-issue-1511.c | 17 ++++++++++++++ tests/regression/46-apron2/97-issue-1511.t | 26 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/regression/46-apron2/97-issue-1511.c create mode 100644 tests/regression/46-apron2/97-issue-1511.t diff --git a/tests/regression/46-apron2/97-issue-1511.c b/tests/regression/46-apron2/97-issue-1511.c new file mode 100644 index 0000000000..d1f5aaed7c --- /dev/null +++ b/tests/regression/46-apron2/97-issue-1511.c @@ -0,0 +1,17 @@ +#include +pthread_mutex_t c; +int d, e, f; + +void b(void* arg); + + +void main(int argc, char *argv) { + pthread_t t; + + e = pthread_create(&t, 0, b, &f); + + pthread_mutex_lock(&c); + d=0; + pthread_mutex_unlock(&c); + pthread_mutex_lock(&c); +} diff --git a/tests/regression/46-apron2/97-issue-1511.t b/tests/regression/46-apron2/97-issue-1511.t new file mode 100644 index 0000000000..3bf5b41334 --- /dev/null +++ b/tests/regression/46-apron2/97-issue-1511.t @@ -0,0 +1,26 @@ + $ goblint --disable warn.unsound --disable warn.imprecise --disable sem.unknown_function.invalidate.globals --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid --enable witness.yaml.enabled --disable witness.invariant.other --disable witness.invariant.loop-head 97-issue-1511.c --set witness.yaml.path 97-issue-1511.yml + [Info][Witness] witness generation summary: + total generation entries: 11 + [Error][Imprecise][Unsound] Function definition missing + + $ goblint --disable warn.unsound --disable warn.imprecise --disable sem.unknown_function.invalidate.globals --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 97-issue-1511.yml 97-issue-1511.c + [Info][Witness] witness validation summary: + confirmed: 20 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 20 + [Success][Witness] invariant confirmed: (2147483647LL + (long long )d) - (long long )f >= 0LL (97-issue-1511.c:14:3) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) - (long long )f >= 0LL (97-issue-1511.c:14:3) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) + (long long )f >= 0LL (97-issue-1511.c:14:3) + [Success][Witness] invariant confirmed: (2147483648LL - (long long )d) + (long long )f >= 0LL (97-issue-1511.c:14:3) + [Success][Witness] invariant confirmed: d == 0 (97-issue-1511.c:14:3) + [Success][Witness] invariant confirmed: (2147483647LL + (long long )d) - (long long )f >= 0LL (97-issue-1511.c:17:1) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) - (long long )f >= 0LL (97-issue-1511.c:17:1) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) + (long long )f >= 0LL (97-issue-1511.c:17:1) + [Success][Witness] invariant confirmed: (2147483648LL - (long long )d) + (long long )f >= 0LL (97-issue-1511.c:17:1) + [Success][Witness] invariant confirmed: d == 0 (97-issue-1511.c:17:1) + [Error][Imprecise][Unsound] Function definition missing From 9ef0ab63628e8e0fa8b2edc32f9cf08462ef7d25 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 6 Aug 2024 16:18:25 +0200 Subject: [PATCH 158/160] Clustered LMust --- src/analyses/apron/relationPriv.apron.ml | 93 +++++++++++++++--------- src/analyses/basePriv.ml | 2 +- src/analyses/commonPriv.ml | 12 ++- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 8f8fb733c5..3184d66bbb 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -724,11 +724,17 @@ module type ClusterArg = functor (RD: RelationDomain.RD) -> sig module LRD: Lattice.S + module Cluster: sig + include Printable.S + end + val keep_only_protected_globals: Q.ask -> LockDomain.Addr.t -> LRD.t -> LRD.t val keep_global: varinfo -> LRD.t -> LRD.t val lock: RD.t -> LRD.t -> LRD.t -> RD.t - val unlock: W.t -> RD.t -> LRD.t + val unlock: W.t -> RD.t -> LRD.t * (Cluster.t list) + + val filter_clusters: LRD.t -> (Cluster.t -> bool) -> LRD.t val name: unit -> string end @@ -738,6 +744,7 @@ module NoCluster:ClusterArg = functor (RD: RelationDomain.RD) -> struct open CommonPerMutex(RD) module LRD = RD + module Cluster = Printable.Unit let keep_only_protected_globals = keep_only_protected_globals @@ -749,7 +756,13 @@ struct RD.meet oct (RD.join local_m get_m) let unlock w oct_side = - oct_side + oct_side, [()] + + let filter_clusters oct f = + if f () then + oct + else + RD.bot () let name () = "no-clusters" end @@ -823,6 +836,8 @@ struct module VS = SetDomain.Make (CilType.Varinfo) module LRD = MapDomain.MapBot (VS) (RD) + module Cluster = VS + let keep_only_protected_globals ask m octs = (* normal (strong) mapping: contains only still fully protected *) (* must filter by protection to avoid later meeting with non-protecting *) @@ -872,7 +887,10 @@ struct let oct_side_cluster gs = RD.keep_vars oct_side (gs |> VS.elements |> List.map V.global) in - LRD.add_list_fun clusters oct_side_cluster (LRD.empty ()) + (LRD.add_list_fun clusters oct_side_cluster (LRD.empty ()), clusters) + + let filter_clusters oct f = + LRD.filter (fun gs _ -> f gs) oct let name = ClusteringArg.name end @@ -888,6 +906,8 @@ struct module LRD1 = DCCluster.LRD module LRD = Lattice.Prod (LRD1) (LRD1) (* second component is only used between keep_* and lock for additional weak mapping *) + module Cluster = DCCluster.Cluster + let name = ClusteringArg.name let filter_map' f m = @@ -949,7 +969,11 @@ struct r let unlock w oct_side = - (DCCluster.unlock w oct_side, LRD1.bot ()) + let lad, clusters = DCCluster.unlock w oct_side in + ((lad, LRD1.bot ()), clusters) + + let filter_clusters (lad,lad') f = + (LRD1.filter (fun gs _ -> f gs) lad, LRD1.filter (fun gs _ -> f gs) lad') end (** Per-mutex meet with TIDs. *) @@ -963,7 +987,7 @@ struct module Cluster = NC module LRD = NC.LRD - include PerMutexTidCommon (Digest) (LRD) + include PerMutexTidCommon (Digest) (LRD) (NC.Cluster) module AV = RD.V module P = UnitP @@ -985,13 +1009,11 @@ struct let get_m = get_relevant_writes ask m (G.mutex @@ getg (V.mutex m)) in if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.Addr.pretty m LRD.pretty get_m; let r = - if not inits then - get_m - else - let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in - let get_mutex_inits' = Cluster.keep_only_protected_globals ask m get_mutex_inits in - if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; - LRD.join get_m get_mutex_inits' + let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in + let get_mutex_inits' = Cluster.keep_only_protected_globals ask m get_mutex_inits in + let get_mutex_inits' = Cluster.filter_clusters get_mutex_inits' inits in + if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; + LRD.join get_m get_mutex_inits' in if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r @@ -1010,13 +1032,11 @@ struct in if M.tracing then M.traceli "relationpriv" "get_mutex_global_g_with_mutex_inits %a\n get=%a" CilType.Varinfo.pretty g LRD.pretty get_mutex_global_g; let r = - if not inits then - get_mutex_global_g - else - let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in - let get_mutex_inits' = Cluster.keep_global g get_mutex_inits in - if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; - LRD.join get_mutex_global_g get_mutex_inits' + let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in + let get_mutex_inits' = Cluster.keep_global g get_mutex_inits in + let get_mutex_inits' = Cluster.filter_clusters get_mutex_inits' inits in + if M.tracing then M.trace "relationpriv" "inits=%a\n inits'=%a" LRD.pretty get_mutex_inits LRD.pretty get_mutex_inits'; + LRD.join get_mutex_global_g get_mutex_inits' in if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r @@ -1024,11 +1044,9 @@ struct let get_mutex_global_g_with_mutex_inits_atomic inits ask getg = (* Unprotected invariant is one big relation. *) let get_mutex_global_g = get_relevant_writes_nofilter ask @@ G.mutex @@ getg (V.mutex atomic_mutex) in - if not inits then - get_mutex_global_g - else - let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in - LRD.join get_mutex_global_g get_mutex_inits + let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in + let get_mutex_inits' = Cluster.filter_clusters get_mutex_inits inits in + LRD.join get_mutex_global_g get_mutex_inits' let read_global (ask: Q.ask) getg (st: relation_components_t) g x: RD.t = let atomic = Param.handle_atomic && ask.f MustBeAtomic in @@ -1042,9 +1060,9 @@ struct if atomic && RD.mem_var rel (AV.global g) then rel (* Read previous unpublished unprotected write in current atomic section. *) else if atomic then - Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits_atomic (not (LMust.mem lm lmust)) ask getg) (* Read unprotected invariant as full relation. *) + Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits_atomic (fun c -> (not (LMust.mem (lm,c) lmust))) ask getg) (* Read unprotected invariant as full relation. *) else - Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits (not (LMust.mem lm lmust)) ask getg g) + Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits (fun c -> (not (LMust.mem (lm,c) lmust))) ask getg g) in (* read *) let g_var = AV.global g in @@ -1076,9 +1094,9 @@ struct if atomic && RD.mem_var rel (AV.global g) then rel (* Read previous unpublished unprotected write in current atomic section. *) else if atomic then - Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits_atomic (not (LMust.mem lm lmust)) ask getg) (* Read unprotected invariant as full relation. *) + Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits_atomic (fun c -> (not (LMust.mem (lm,c) lmust))) ask getg) (* Read unprotected invariant as full relation. *) else - Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits (not (LMust.mem lm lmust)) ask getg g) + Cluster.lock rel local_m (get_mutex_global_g_with_mutex_inits (fun c -> (not (LMust.mem (lm,c) lmust))) ask getg g) in (* write *) let g_var = AV.global g in @@ -1088,7 +1106,7 @@ struct (* unlock *) if not atomic then ( let rel_side = RD.keep_vars rel_local [g_var] in - let rel_side = Cluster.unlock (W.singleton g) rel_side in + let rel_side, clusters = Cluster.unlock (W.singleton g) rel_side in let digest = Digest.current ask in let sidev = GMutex.singleton digest rel_side in if Param.handle_atomic then @@ -1102,7 +1120,8 @@ struct else rel_local in - {rel = rel_local'; priv = (W.add g w,LMust.add lm lmust,l')} + let lmust' = List.fold (fun a c -> LMust.add (lm,c) a) lmust clusters in + {rel = rel_local'; priv = (W.add g w,lmust',l')} ) else (* Delay publishing unprotected write in the atomic section. *) @@ -1114,7 +1133,7 @@ struct let rel = st.rel in let _,lmust,l = st.priv in let lm = LLock.mutex m in - let get_m = get_m_with_mutex_inits (not (LMust.mem lm lmust)) ask getg m in + let get_m = get_m_with_mutex_inits (fun c -> (not (LMust.mem (lm,c) lmust))) ask getg m in let local_m = BatOption.default (LRD.bot ()) (L.find_opt lm l) in (* Additionally filter get_m in case it contains variables it no longer protects. E.g. in 36/22. *) let local_m = Cluster.keep_only_protected_globals ask m local_m in @@ -1144,13 +1163,14 @@ struct {rel = rel_local; priv = (w',lmust,l)} else let rel_side = keep_only_protected_globals ask m rel in - let rel_side = Cluster.unlock w rel_side in + let rel_side, clusters = Cluster.unlock w rel_side in let digest = Digest.current ask in let sidev = GMutex.singleton digest rel_side in sideg (V.mutex m) (G.create_mutex sidev); let lm = LLock.mutex m in let l' = L.add lm rel_side l in - {rel = rel_local; priv = (w',LMust.add lm lmust,l')} + let lmust' = List.fold (fun a c -> LMust.add (lm,c) a) lmust clusters in + {rel = rel_local; priv = (w',lmust',l')} ) else ( (* Publish delayed unprotected write as if it were protected by the atomic section. *) @@ -1161,14 +1181,15 @@ struct {rel = rel_local; priv = (w',lmust,l)} else let rel_side = keep_only_globals ask m rel in - let rel_side = Cluster.unlock w rel_side in + let rel_side, clusters = Cluster.unlock w rel_side in let digest = Digest.current ask in let sidev = GMutex.singleton digest rel_side in (* Unprotected invariant is one big relation. *) sideg (V.mutex atomic_mutex) (G.create_mutex sidev); let (lmust', l') = W.fold (fun g (lmust, l) -> let lm = LLock.global g in - (LMust.add lm lmust, L.add lm rel_side l) + let lmust'' = List.fold (fun a c -> LMust.add (lm,c) a) lmust clusters in + (lmust'', L.add lm rel_side l) ) w (lmust, l) in {rel = rel_local; priv = (w',lmust',l')} @@ -1258,7 +1279,7 @@ struct ) (RD.vars rel) in let rel_side = RD.keep_vars rel g_vars in - let rel_side = Cluster.unlock (W.top ()) rel_side in (* top W to avoid any filtering *) + let rel_side, clusters = Cluster.unlock (W.top ()) rel_side in (* top W to avoid any filtering *) let digest = Digest.current ask in let sidev = GMutex.singleton digest rel_side in sideg V.mutex_inits (G.create_mutex sidev); diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 2157b13c13..0b1aea2dd7 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -448,7 +448,7 @@ module PerMutexMeetTIDPriv (Digest: Digest): S = struct open Queries.Protection include PerMutexMeetPrivBase - include PerMutexTidCommon (Digest) (CPA) + include PerMutexTidCommonNC (Digest) (CPA) let iter_sys_vars getg vq vf = match vq with diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 8290f0e0a2..d7174ea850 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -237,7 +237,7 @@ struct | _ -> false end -module PerMutexTidCommon (Digest: Digest) (LD:Lattice.S) = +module PerMutexTidCommon (Digest: Digest) (LD:Lattice.S) (Cluster:Printable.S) = struct include ConfCheck.RequireThreadFlagPathSensInit @@ -268,7 +268,7 @@ struct (** Mutexes / globals to which values have been published, i.e. for which the initializers need not be read **) module LMust = struct - include SetDomain.Reverse (SetDomain.ToppedSet (LLock) (struct let topname = "All locks" end)) + include SetDomain.Reverse (SetDomain.ToppedSet (Printable.Prod(LLock)(Cluster)) (struct let topname = "All locks" end)) let name () = "LMust" end @@ -315,6 +315,14 @@ struct let startstate () = W.bot (), LMust.top (), L.bot () end +module PerMutexTidCommonNC (Digest: Digest) (LD:Lattice.S) = struct + include PerMutexTidCommon (Digest) (LD) (Printable.Unit) + module LMust = struct + include LMust + let mem lm lmust = mem (lm, ()) lmust + let add lm lmust = add (lm, ()) lmust + end +end let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = (* Should be in sync with: From 1d0e9ad8bed45534ba8bc34456000aeabf2ce4f9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Tue, 6 Aug 2024 16:21:46 +0200 Subject: [PATCH 159/160] Add regression test --- tests/regression/46-apron2/98-issue-1511b.c | 32 +++++++++++++++++ tests/regression/46-apron2/98-issue-1511b.t | 40 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/regression/46-apron2/98-issue-1511b.c create mode 100644 tests/regression/46-apron2/98-issue-1511b.t diff --git a/tests/regression/46-apron2/98-issue-1511b.c b/tests/regression/46-apron2/98-issue-1511b.c new file mode 100644 index 0000000000..6ba233b61b --- /dev/null +++ b/tests/regression/46-apron2/98-issue-1511b.c @@ -0,0 +1,32 @@ +#include +int d, j, k; + +pthread_mutex_t f; + +void nothing() {}; +void nothing2() {}; + +int top() { + int top2; + return top2; +} + +void main() { + d = top(); + if (d) { + k = 1; + pthread_t tid; + pthread_create(&tid, 0, ¬hing, NULL); + pthread_mutex_lock(&f); + j = 0; // To ensure something is published + pthread_mutex_unlock(&f); + pthread_mutex_lock(&f); + + pthread_t tid2; + pthread_create(&tid2, 0, ¬hing2, NULL); + float f = 8.0f; + } else { + pthread_t tid2; + pthread_create(&tid2, 0, ¬hing2, NULL); + } +} diff --git a/tests/regression/46-apron2/98-issue-1511b.t b/tests/regression/46-apron2/98-issue-1511b.t new file mode 100644 index 0000000000..8add2f3d1c --- /dev/null +++ b/tests/regression/46-apron2/98-issue-1511b.t @@ -0,0 +1,40 @@ + $ goblint --disable warn.unsound --disable warn.imprecise --disable sem.unknown_function.invalidate.globals --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid --enable witness.yaml.enabled --disable witness.invariant.other --disable witness.invariant.loop-head 98-issue-1511b.c --set witness.yaml.path 98-issue-1511b.yml + [Info][Witness] witness generation summary: + total generation entries: 27 + + $ goblint --disable warn.unsound --disable warn.imprecise --disable sem.unknown_function.invalidate.globals --enable warn.deterministic --disable ana.dead-code.lines --disable warn.race --disable warn.behavior --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.validate 98-issue-1511b.yml 98-issue-1511b.c + [Info][Witness] witness validation summary: + confirmed: 52 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 52 + [Success][Witness] invariant confirmed: (1LL + (long long )j) - (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (1LL - (long long )j) - (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483646LL + (long long )j) + (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483646LL - (long long )d) + (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483646LL - (long long )j) + (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483647LL + (long long )d) + (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) + (long long )j >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) - (long long )j >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) + (long long )j >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) - (long long )j >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483648LL - (long long )d) - (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (2147483649LL + (long long )d) - (long long )k >= 0LL (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: j == 0 (98-issue-1511b.c:21:5) + [Success][Witness] invariant confirmed: (1LL + (long long )j) - (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (1LL - (long long )j) - (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483646LL + (long long )j) + (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483646LL - (long long )d) + (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483646LL - (long long )j) + (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483647LL + (long long )d) + (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) + (long long )j >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483647LL - (long long )d) - (long long )j >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) + (long long )j >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483648LL + (long long )d) - (long long )j >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483648LL - (long long )d) - (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: (2147483649LL + (long long )d) - (long long )k >= 0LL (98-issue-1511b.c:26:5) + [Success][Witness] invariant confirmed: j == 0 (98-issue-1511b.c:26:5) From 3878a2283d60a07d5dcb767e0c788e1997b5d5fa Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 9 Aug 2024 19:20:52 +0200 Subject: [PATCH 160/160] Make `update_offset` idempotent for blobs --- src/cdomain/value/cdomains/valueDomain.ml | 43 ++++++++++--------- tests/regression/13-privatized/95-mm-calloc.c | 28 ++++++++++++ 2 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 tests/regression/13-privatized/95-mm-calloc.c diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 869cacdd4e..0531fb772b 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -982,26 +982,29 @@ struct end | Blob (x,s,orig), _ -> begin - let l', o' = shift_one_over l o in - let x = zero_init_calloced_memory orig x t in - (* Strong update of scalar variable is possible if the variable is unique and size of written value matches size of blob being written to. *) - let do_strong_update = - begin match v with - | (Var var, _) -> - let blob_size_opt = ID.to_int s in - not @@ ask.is_multiple var - && Option.is_some blob_size_opt (* Size of blob is known *) - && (( - not @@ Cil.isVoidType t (* Size of value is known *) - && Z.equal (Option.get blob_size_opt) (Z.of_int @@ Cil.alignOf_int t) - ) || blob_destructive) - | _ -> false - end - in - if do_strong_update then - Blob ((do_update_offset ask x offs value exp l' o' v t), s, orig) - else - mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, orig)) + match offs, value with + | `NoOffset, Blob (x2, s2, orig2) -> mu (Blob (join x x2, ID.join s s2,ZeroInit.join orig orig2)) + | _ -> + let l', o' = shift_one_over l o in + let x = zero_init_calloced_memory orig x t in + (* Strong update of scalar variable is possible if the variable is unique and size of written value matches size of blob being written to. *) + let do_strong_update = + begin match v with + | (Var var, _) -> + let blob_size_opt = ID.to_int s in + not @@ ask.is_multiple var + && Option.is_some blob_size_opt (* Size of blob is known *) + && (( + not @@ Cil.isVoidType t (* Size of value is known *) + && Z.equal (Option.get blob_size_opt) (Z.of_int @@ Cil.alignOf_int t) + ) || blob_destructive) + | _ -> false + end + in + if do_strong_update then + Blob ((do_update_offset ask x offs value exp l' o' v t), s, orig) + else + mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, orig)) end | Thread _, _ -> (* hack for pthread_t variables *) diff --git a/tests/regression/13-privatized/95-mm-calloc.c b/tests/regression/13-privatized/95-mm-calloc.c new file mode 100644 index 0000000000..60a88379fc --- /dev/null +++ b/tests/regression/13-privatized/95-mm-calloc.c @@ -0,0 +1,28 @@ +// PARAM: --set ana.base.privatization mutex-meet +#include +#include +#include +struct a { + void (*b)(); +}; + +int g = 0; + +struct a* t; +void m() { + // Reachable! + g = 1; +} + +void* j(void* arg) {}; + +void main() { + pthread_t tid; + pthread_create(&tid, 0, j, NULL); + t = calloc(1, sizeof(struct a)); + t->b = &m; + struct a r = *t; + r.b(); + + __goblint_check(g ==0); //UNKNOWN! +}